diff --git a/.github/workflows/dataviewer-backend-pytests.yml b/.github/workflows/dataviewer-backend-pytests.yml new file mode 100644 index 00000000..aa55aaf9 --- /dev/null +++ b/.github/workflows/dataviewer-backend-pytests.yml @@ -0,0 +1,65 @@ +name: Dataviewer Backend Pytest + +on: + workflow_call: + inputs: + code-coverage: + description: 'Enable Codecov coverage upload' + required: false + default: false + type: boolean + +permissions: + contents: read + +jobs: + pytest-dataviewer: + name: Pytest Dataviewer Backend + runs-on: ubuntu-latest + defaults: + run: + working-directory: data-management/viewer/backend + permissions: + contents: read + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + + - name: Setup uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends ffmpeg + + - name: Install dependencies + run: uv sync --extra dev --extra analysis --extra hdf5 --extra export --extra auth + + - name: Run pytest with coverage + run: uv run pytest -v --cov=src --cov-report=xml --cov-report=term-missing + + - name: Upload coverage.xml artifact + if: ${{ inputs.code-coverage && always() }} + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: pytest-dataviewer-coverage-xml + path: data-management/viewer/backend/coverage.xml + retention-days: 30 + + - name: Upload coverage to Codecov + if: ${{ inputs.code-coverage && always() }} + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + with: + files: coverage.xml + use_oidc: true + fail_ci_if_error: false + verbose: true + flags: pytest-dataviewer + name: pytest-dataviewer-coverage diff --git a/.github/workflows/dataviewer-frontend-tests.yml b/.github/workflows/dataviewer-frontend-tests.yml index 18e82ef8..3e1b47cc 100644 --- a/.github/workflows/dataviewer-frontend-tests.yml +++ b/.github/workflows/dataviewer-frontend-tests.yml @@ -2,19 +2,13 @@ name: Dataviewer Frontend Tests on: workflow_call: - inputs: - soft-fail: - description: 'Whether to continue on test or lint failures' - required: false - type: boolean - default: false permissions: contents: read jobs: frontend-checks: - name: Lint, Type-check & Test + name: Lint, Type-check, Test and Build runs-on: ubuntu-latest defaults: run: @@ -23,7 +17,7 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -38,44 +32,16 @@ jobs: run: npm ci - name: ESLint - id: lint - continue-on-error: true - run: npm run lint 2>&1 | tee lint-results.txt + run: npm run lint - name: Type check - id: typecheck - continue-on-error: true - run: npm run type-check 2>&1 | tee typecheck-results.txt + run: npm run type-check - name: Format check - id: format - continue-on-error: true - run: npm run format 2>&1 | tee format-results.txt + run: npm run format - name: Unit tests - id: tests - continue-on-error: true - run: npm run test 2>&1 | tee test-results.txt + run: npm run test - - name: Upload results - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4.4.3 - with: - name: frontend-test-results - path: | - data-management/viewer/frontend/lint-results.txt - data-management/viewer/frontend/typecheck-results.txt - data-management/viewer/frontend/format-results.txt - data-management/viewer/frontend/test-results.txt - retention-days: 30 - - - name: Fail job if errors found - if: >- - (steps.lint.outcome == 'failure' - || steps.typecheck.outcome == 'failure' - || steps.format.outcome == 'failure' - || steps.tests.outcome == 'failure') - && !inputs.soft-fail - run: | - echo "::error::Frontend checks failed. Run 'npm run lint', 'npm run type-check', 'npm run format', and 'npm run test' locally to reproduce." - exit 1 + - name: Build + run: npm run build diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 94bbbce6..62fa3c12 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -106,6 +106,23 @@ jobs: contents: read id-token: write + # Dataviewer frontend lint, type-check, and build + dataviewer-frontend-tests: + name: Dataviewer Frontend Tests + uses: ./.github/workflows/dataviewer-frontend-tests.yml + permissions: + contents: read + + # Dataviewer backend pytest execution + dataviewer-backend-pytests: + name: Dataviewer Backend Pytest + uses: ./.github/workflows/dataviewer-backend-pytests.yml + with: + code-coverage: true + permissions: + contents: read + id-token: write + # Python linting using ruff python-lint: name: Python Lint @@ -154,6 +171,8 @@ jobs: - dependency-pinning - pester-tests - pytest-tests + - dataviewer-frontend-tests + - dataviewer-backend-pytests - python-lint - terraform-lint - terraform-validation diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 40befbd1..2c7b9ca9 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -131,6 +131,17 @@ jobs: permissions: contents: read id-token: write + + # Dataviewer backend pytest execution + dataviewer-backend-pytests: + name: Dataviewer Backend Pytest + uses: ./.github/workflows/dataviewer-backend-pytests.yml + with: + code-coverage: true + permissions: + contents: read + id-token: write + # Python linting using ruff python-lint: name: Python Lint diff --git a/codecov.yml b/codecov.yml index bba7dc6a..830305c7 100644 --- a/codecov.yml +++ b/codecov.yml @@ -27,6 +27,11 @@ coverage: - pytest target: auto threshold: 1% + pytest-dataviewer: + flags: + - pytest-dataviewer + target: auto + threshold: 1% patch: default: target: auto @@ -41,6 +46,10 @@ flags: paths: - src/ carryforward: true + pytest-dataviewer: + paths: + - data-management/viewer/backend/src/ + carryforward: true parsers: jacoco: diff --git a/data-management/viewer/backend/pyproject.toml b/data-management/viewer/backend/pyproject.toml index 659d610b..34e83405 100644 --- a/data-management/viewer/backend/pyproject.toml +++ b/data-management/viewer/backend/pyproject.toml @@ -17,6 +17,7 @@ dev = [ "ruff>=0.8.0", "pytest>=8.3.0", "pytest-asyncio>=0.24.0", + "pytest-cov>=6.0.0", "httpx>=0.28.0", ] azure = [ diff --git a/data-management/viewer/backend/tests/test_dataset_service.py b/data-management/viewer/backend/tests/test_dataset_service.py index feb8530b..e87c2744 100644 --- a/data-management/viewer/backend/tests/test_dataset_service.py +++ b/data-management/viewer/backend/tests/test_dataset_service.py @@ -653,4 +653,10 @@ async def upload_video(self, dataset_id: str, camera: str, episode_idx: int, cac service._upload_video_to_blob("dataset\r\nname", 7.0, "cam0", tmp_path / "video.mp4") - assert logged == [("Blob upload failed for %s ep %d: %s", "datasetname", 7, "upload failed")] + assert len(logged) == 1 + message, ds_id, ep_idx, exc = logged[0] + assert message == "Blob upload failed for %s ep %d: %s" + assert ds_id == "datasetname" + assert ep_idx == 7 + assert isinstance(exc, RuntimeError) + assert str(exc) == "upload failed"