Skip to content

Commit 6875434

Browse files
Add testing statement and publish test report (#139)
Add docs/testing-statement.md for the internal assurance team, describing the testing approach (per ADR-002), what automated tests cover, the CI quality gates, where evidence lives, and what is not covered by automated testing and how each gap is otherwise assured. Publish a human-readable test report to GitHub Pages on every main build so the statement can cite a stable, per-build link: - add pytest-html; the test stage emits a self-contained reports/index.html and uploads it as an artifact - add a publish-test-report job to the main-branch workflow that deploys the artifact to Pages
1 parent 9d3e46f commit 6875434

6 files changed

Lines changed: 149 additions & 1 deletion

File tree

.github/workflows/cicd-2-main-branch.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,39 @@ jobs:
3333
uses: ./.github/workflows/stage-2-test.yaml
3434
secrets: inherit
3535

36+
# Publish the test report (produced by the test stage) to GitHub Pages so
37+
# assurance reviewers have a stable, per-build link to the latest results.
38+
# Runs even if tests fail, so a failing report is still published.
39+
publish-test-report:
40+
name: Publish test report
41+
needs: test-stage
42+
if: always() && needs.test-stage.result != 'skipped'
43+
runs-on: ubuntu-latest
44+
permissions:
45+
pages: write
46+
id-token: write
47+
environment:
48+
name: github-pages
49+
url: ${{ steps.deployment.outputs.page_url }}
50+
steps:
51+
- name: Download test report
52+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
53+
with:
54+
name: test-report
55+
path: reports
56+
57+
- name: Configure Pages
58+
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
59+
60+
- name: Upload Pages artifact
61+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
62+
with:
63+
path: reports
64+
65+
- name: Deploy to GitHub Pages
66+
id: deployment
67+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
68+
3669
# Resolve the latest release tag once so downstream jobs can consume it.
3770
resolve:
3871
name: Resolve deployment targets

.github/workflows/stage-2-test.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
run: uv sync --all-extras
3737

3838
- name: Run tests
39-
run: uv run pytest
39+
run: uv run pytest --html=reports/index.html --self-contained-html
4040

4141
- name: Upload coverage reports
4242
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
@@ -47,6 +47,14 @@ jobs:
4747
name: codecov-umbrella
4848
fail_ci_if_error: false
4949

50+
- name: Upload test report
51+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
52+
if: always() # capture the report even when tests fail
53+
with:
54+
name: test-report
55+
path: reports/
56+
if-no-files-found: error
57+
5058
test-lint:
5159
name: Lint and type checking
5260
runs-on: ubuntu-latest

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ build/
4141
.coverage.*
4242
htmlcov/
4343
coverage.xml
44+
reports/
4445
.tox/
4546
.hypothesis/
4647

docs/testing-statement.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Rubie Gateway — Testing Statement
2+
3+
**Purpose:** Describe how the Rubie Gateway is tested, what is and is not covered by automated testing, and where test evidence can be found.
4+
**Status:** Living document — figures reflect the state of `main` and should be refreshed when materially changed.
5+
6+
---
7+
8+
## 1. What the gateway does (context for scope)
9+
10+
The Rubie Gateway is a Python application that runs on a VM inside an NHS Trust network and bridges the on-site imaging modality and the cloud-based Rubie service. Its responsibilities are:
11+
12+
- **MWL (Modality Worklist) SCP** — receives worklist create/update commands from Rubie (via Azure Relay), and serves them to the modality over DICOM C-FIND.
13+
- **PACS SCP** — receives acquired images from the modality over DICOM C-STORE, and answers DICOM C-ECHO connectivity checks.
14+
- **Upload service** — forwards stored images to the Rubie cloud API.
15+
- **Relay listener** — maintains the Azure Relay connection over which Rubie sends commands.
16+
17+
Testing is scoped to the behaviour of this application. The wider distributed system (Rubie cloud, NBSS, the Trust PACS, the physical modality) is out of scope for automated testing here.
18+
19+
## 2. Testing approach
20+
21+
The strategy is recorded in [ADR-002: Testing strategy](./adr/ADR-002_Testing_strategy.md). In summary, the suite is layered:
22+
23+
| Layer | What it exercises | Dependencies |
24+
| --- | --- | --- |
25+
| **Unit** | Individual functions and classes in isolation | Mocked |
26+
| **Integration** | Components working together, including the gateway server running in a background thread | Real SQLite database, real filesystem |
27+
| **End-to-end (in-process)** | A full relay-command → MWL → C-STORE → upload flow | Real SQLite + filesystem; external network boundaries emulated |
28+
29+
Because it is impractical to stand up the entire Rubie + PACS + modality ecosystem for automated testing, the suite uses **emulation** at the system boundaries: crafted DICOM datasets and a modality emulator stand in for real imaging hardware, and the Azure Relay transport is mocked so command-processing logic can be tested deterministically.
30+
31+
## 3. What is covered
32+
33+
Automated tests cover the core DICOM and messaging behaviour, including:
34+
35+
- **MWL**: C-FIND worklist queries (including patient-name search), worklist item creation, and status transitions.
36+
- **PACS**: C-STORE image receipt and metadata persistence, handling of already-stored SOP instances, and C-ECHO verification (both PACS and MWL application entities).
37+
- **Relay listener**: processing of inbound action commands and dispatch to the correct handler.
38+
- **Upload**: forwarding of stored instances to the Rubie API.
39+
- **Modality emulator**: worklist querying and image generation used to drive end-to-end tests.
40+
41+
**Current figures (refresh on material change):**
42+
43+
- **Over 200 automated tests** (194 unit, 19 integration), all passing on `main`.
44+
- **Approximately 90% line coverage** of application code (`src/`), measured by `pytest-cov` and reported to Codecov on every run.
45+
46+
## 4. Automated quality gates (CI)
47+
48+
Every pull request and every merge to `main` runs the following in GitHub Actions; all must pass before code is deployable:
49+
50+
| Stage | Check | Tool |
51+
| --- | --- | --- |
52+
| Commit | Secret scanning across branch history | gitleaks |
53+
| Test | Full unit + integration suite | pytest |
54+
| Test | Line-coverage measurement and reporting | pytest-cov / Codecov |
55+
| Test | Linting and formatting | ruff |
56+
| Test | Static type checking | pyright |
57+
| Scheduled | Static application security testing (SAST) | CodeQL (weekly) |
58+
59+
## 5. Test evidence
60+
61+
- **Per-run evidence:** the GitHub Actions run for each PR and each `main` build records the full test output, pass/fail status, and coverage upload. These are retained against the commit and pull request.
62+
- **Coverage trend:** reported to Codecov, giving per-file coverage and change-over-time.
63+
- **Local reproduction:** the full suite is `make test`; subsets are `make test-unit` and `make test-integration`. Coverage HTML is written to `htmlcov/`.
64+
- **Published report:** a human-readable test report is published to GitHub Pages on every `main` build and is available at <https://nhsdigital.github.io/manage-breast-screening-gateway/>. This gives assurance reviewers a stable link to the latest results without navigating CI logs, and is refreshed automatically per build.
65+
66+
## 6. What is NOT covered by automated testing
67+
68+
| Not automatically tested | Why | Compensating assurance |
69+
| --- | --- | --- |
70+
| **Real modality hardware** | No physical modality in CI | Emulated with crafted DICOM and the modality emulator; validated against real Hologic kit during site dry runs. |
71+
| **Live Azure Relay / Managed Identity auth** | No Azure resources in CI; the transport is mocked | Credential and connection logic is exercised by deployments to dev/pre-prod and by post-deployment smoke tests. |
72+
| **PowerShell deployment scripts** (`deploy.ps1`, `rollback.ps1`) | Windows blue/green cutover, NSSM service install and rollback are not unit-testable in this suite | Exercised by real deployments to non-production environments with smoke tests; Bash helper scripts are covered by ShellCheck. |
73+
| **The end-to-end distributed system** (Rubie cloud, NBSS, Trust PACS) | Impractical to assemble for automated testing (ADR-002) | Emulation at boundaries; manual/clinical validation via site dry runs. |
74+
75+
## 7. Maintaining this statement
76+
77+
This statement should be reviewed when the testing strategy changes materially — for example a new test layer, a significant coverage change, a new CI gate, or a change to what is emulated versus tested against real dependencies. The figures in §3 are point-in-time and can be regenerated with `make test`.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies = [
2323
dev = [
2424
"pytest>=9.0.3,<10",
2525
"pytest-cov>=7.0.0,<8",
26+
"pytest-html>=4.1,<5",
2627
"ruff>=0.14.1,<0.16",
2728
"pyright>=1.1.390",
2829
"ipdb>=0.13.13,<0.14",

uv.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)