diff --git a/.github/workflows/flow-pull-request-formatting.yaml b/.github/workflows/flow-pull-request-formatting.yaml new file mode 100644 index 0000000..af6ef52 --- /dev/null +++ b/.github/workflows/flow-pull-request-formatting.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: Apache-2.0 +name: "PR Formatting" +on: + pull_request_target: + types: + - assigned + - unassigned + - labeled + - unlabeled + - opened + - reopened + - edited + - converted_to_draft + - ready_for_review + - review_requested + - review_request_removed + - locked + - unlocked + - synchronize + +defaults: + run: + shell: bash + +permissions: + statuses: write + +jobs: + title-check: + name: Title Check + runs-on: hedera-agent-linux-medium + steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Check PR Title + uses: step-security/conventional-pr-title-action@d47e8818876fa91d2010b65c4d699bb5f0d34d56 # v3.2.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + assignee-check: + name: Assignee Check + runs-on: hedera-agent-linux-medium + steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Check Assignee + if: ${{ github.event.pull_request.assignees == null || github.event.pull_request.assignees[0] == null }} + run: | + echo "Assignee is not set. Failing the workflow." + exit 1 diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml new file mode 100644 index 0000000..0a3a519 --- /dev/null +++ b/.github/workflows/pr-tests.yml @@ -0,0 +1,28 @@ +name: PR Tests + +on: + pull_request: + branches: [ main, "feat/75-tests-ci" ] + +permissions: + contents: read + +jobs: + unit-tests: + uses: ./.github/workflows/run-unit-tests.yml + + integration-tests: + needs: [ unit-tests ] + uses: ./.github/workflows/run-integration-tests.yml + secrets: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + + e2e-tests: + needs: [ integration-tests ] + uses: ./.github/workflows/run-e2e-tests.yml + secrets: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml new file mode 100644 index 0000000..782e512 --- /dev/null +++ b/.github/workflows/run-e2e-tests.yml @@ -0,0 +1,77 @@ +name: Run E2E Tests + +on: + workflow_call: + inputs: + workdir: + description: Working directory + required: false + default: 'python' + type: string + secrets: + OPENAI_API_KEY: + required: true + ACCOUNT_ID: + required: true + PRIVATE_KEY: + required: true + +permissions: + contents: read + +jobs: + e2e-tests: + runs-on: hedera-agent-linux-medium + env: + WORKDIR: python + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + E2E_LLM_PROVIDER: ${{ vars.E2E_LLM_PROVIDER }} + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.11' + + - name: Upgrade pip + run: pip install --upgrade pip pytest + + - name: Install Poetry + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 + with: + version: 2.2.1 + virtualenvs-create: true + virtualenvs-in-project: true + virtualenvs-path: .venv + installer-parallel: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + working-directory: ${{ inputs.workdir }} + run: poetry install --no-interaction --no-root + + - name: Run E2E tests (throttled) + working-directory: ${{ inputs.workdir }} + env: + TEST_DELAY_MS: '8000' + run: | + source .venv/bin/activate + pytest test/e2e/ diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml new file mode 100644 index 0000000..819c6b4 --- /dev/null +++ b/.github/workflows/run-integration-tests.yml @@ -0,0 +1,78 @@ +name: Run Integration Tests + +on: + workflow_call: + inputs: + workdir: + description: Working directory + required: false + default: 'python' + type: string + secrets: + OPENAI_API_KEY: + required: true + ACCOUNT_ID: + required: true + PRIVATE_KEY: + required: true + outputs: {} + +permissions: + contents: read + +jobs: + integration-tests: + runs-on: hedera-agent-linux-medium + env: + WORKDIR: python + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ACCOUNT_ID: ${{ secrets.ACCOUNT_ID }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + E2E_LLM_PROVIDER: ${{ vars.E2E_LLM_PROVIDER }} + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.11' + + - name: Upgrade pip + run: pip install --upgrade pip pytest + + - name: Install Poetry + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 + with: + version: 2.2.1 + virtualenvs-create: true + virtualenvs-in-project: true + virtualenvs-path: .venv + installer-parallel: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + working-directory: ${{ inputs.workdir }} + run: poetry install --no-interaction --no-root + + - name: Run integration tests (throttled) + working-directory: ${{ inputs.workdir }} + env: + TEST_DELAY_MS: '8000' + run: | + source .venv/bin/activate + pytest test/integration/ diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml new file mode 100644 index 0000000..f4fb671 --- /dev/null +++ b/.github/workflows/run-unit-tests.yml @@ -0,0 +1,64 @@ +name: Run Unit Tests + +on: + workflow_call: + inputs: + workdir: + description: Working directory + required: false + default: 'python' + type: string + +permissions: + contents: read + +jobs: + unit-tests: + runs-on: hedera-agent-linux-medium + env: + WORKDIR: python + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.11' + + - name: Upgrade pip + run: pip install --upgrade pip pytest + + - name: Install Poetry + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 + with: + version: 2.2.1 + virtualenvs-create: true + virtualenvs-in-project: true + virtualenvs-path: .venv + installer-parallel: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + working-directory: ${{ inputs.workdir }} + run: poetry install --no-interaction --no-root + + - name: Run tests + working-directory: ${{ inputs.workdir }} + run: | + source .venv/bin/activate + pytest test/unit/ diff --git a/python/poetry.lock b/python/poetry.lock index 95b848a..2b76d27 100644 --- a/python/poetry.lock +++ b/python/poetry.lock @@ -836,7 +836,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\"", dev = "sys_platform == \"win32\" or platform_system == \"Windows\""} +markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "cryptography" @@ -1675,7 +1675,7 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -2503,7 +2503,7 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -2902,7 +2902,7 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -2917,7 +2917,7 @@ version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, @@ -2980,7 +2980,7 @@ version = "16.1" description = "pytest plugin to re-run tests to eliminate flaky failures" optional = false python-versions = ">=3.10" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pytest_rerunfailures-16.1-py3-none-any.whl", hash = "sha256:5d11b12c0ca9a1665b5054052fcc1084f8deadd9328962745ef6b04e26382e86"}, {file = "pytest_rerunfailures-16.1.tar.gz", hash = "sha256:c38b266db8a808953ebd71ac25c381cb1981a78ff9340a14bcb9f1b9bff1899e"}, @@ -3521,7 +3521,7 @@ version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, @@ -4186,4 +4186,4 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "04c08cf35ea1c2ebe8f5aca159cc52596b2f66a47be182a591cbd885a1301f88" +content-hash = "745995a17ab036fcad2f7568ddd0869008399962e6b1b8aa8f4781fc493293ce"