ci(audit): point audit validator at our HTS terminology server (#183)#184
ci(audit): point audit validator at our HTS terminology server (#183)#184mauripunzueta wants to merge 6 commits into
Conversation
The audit-events workflow validated extracted AuditEvent codes against the public HL7 test server tx.fhir.org, which is rate-limited, sometimes unreachable, and outside our control. Point it at our own terminology server at hts.heliossoftware.com instead. - audit-events.yml: --tx-base-url -> https://hts.heliossoftware.com - validate_report.py: DEFAULT_TX_BASE_URL -> https://hts.heliossoftware.com, rename source id tx-r5-live-validation -> hts-live-validation, and generalize the report heading (the base URL is already printed below it). HTS serves terminology operations at the root (no /r4 or /r5 path segment) and needs no auth; $validate-code resolves the audit code systems (audit-event-type, restful-interaction, audit-event-action, audit-event-outcome). The hts.yml comparison oracle is intentionally left on tx.fhir.org as the source of truth.
Code review1 issue found. Bug: stale source name in
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The source entry was renamed tx-r5-live-validation -> hts-live-validation, but the lookup in build_terminology_context() still checked the old name, making the per-run tx_base_url override dead code. With a custom --tx-base-url the report would show the stale default instead of the URL actually validated against.
|
@mauripunzueta When I ran this GitHub action on this branch, I'm seeing errors: https://github.com/HeliosSoftware/hfs/actions/runs/28484701168 |
smunini
left a comment
There was a problem hiding this comment.
I'm seeing errors: https://github.com/HeliosSoftware/hfs/actions/runs/28484701168
hts.heliossoftware.com is fronted by Cloudflare, whose bot protection rejects the default Python urllib User-Agent with HTTP 403 (Error 1010: browser_signature_banned) before the request reaches the terminology server. Every extracted code then surfaced as an invalid system+code pair, so the audit coverage validation step failed across all backends. Present a browser-like User-Agent on the $validate-code requests so the Cloudflare filter is cleared and the real validation response is used.
hts.heliossoftware.com holds two versions of http://terminology.hl7.org/CodeSystem/audit-event-type for the same canonical URL: the FHIR-core 4.0.1 fragment (only `rest`) and the complete THO 1.0.0 (adds hl7-v2, hl7-v3, document, object). Bare $validate-code default-resolves to the 4.0.1 fragment, so `object` - which AuditEvent.type legitimately uses - was rejected as an unknown code. The previous tx.fhir.org/r5 backend default-resolved to 1.0.0, so this only surfaced after migrating to HTS. Pin the complete CodeSystem version (1.0.0) for that system so validation resolves against the full concept list. Verified live: object and rest both validate, and the unaffected systems are unchanged.
|
@smunini Thanks for catching this — resolved now. The run is green: https://github.com/HeliosSoftware/hfs/actions/runs/28806280943 Two transport-level issues surfaced by pointing the validator at
Filed a follow-up for the deeper server-side cause (HTS default-picking the incomplete fragment): #200. |
…onstant Address code-review feedback: TX_SYSTEM_VERSION_OVERRIDES repeated the audit-event-type canonical URL as a magic string instead of reusing the existing AUDIT_EVENT_TYPE_SYSTEM constant. Reuse the constant so the system URL has a single source of truth and the version pin cannot silently miss on a future edit to one copy.
The hts.yml integration-test job compared every HTS operation live against tx.fhir.org/r4 and /r5 as the source of truth, with skip-if-unavailable probes. Per #183, drop that public-server dependency entirely rather than repointing it at our own HTS (which would keep a network call and reduce the test to a self-comparison). Assert each operation against pinned golden values instead — the stable, HL7-defined displays, expansion totals, and outcomes for the UTG code systems these tests exercise (identical for R4 and R5, confirmed against the last green main run): - lookup displays: gender M/F/UN -> Male/Female/Undifferentiated; v3-ActStatus displays == codes - validate-code (CS + VS): valid -> true, invalid / wrong-display -> false - expand totals: v3-AdministrativeGender = 3, v3-ActStatus = 10 - subsumes: same -> equivalent, M/F -> not-subsumed - translate: v2-0001 source -> true, gender source -> false, unmapped -> false, reverse -> true Replace compare_bool/compare_str (HTS-vs-tx) with expect_bool/expect_str (HTS-vs-golden); remove tx_base from the matrix, TX_BASE from the env, the tx_post helper, and the translate availability probe. The job now makes zero external terminology calls and is fully deterministic. Also gitignore __pycache__/ and *.pyc.
| # (server-agnostic — any conformant THO host serves it) so validation resolves | ||
| # against the full concept list. | ||
| TX_SYSTEM_VERSION_OVERRIDES = { | ||
| AUDIT_EVENT_TYPE_SYSTEM: "1.0.0", |
There was a problem hiding this comment.
We shouldn't do this - remove this and we need to fix the underlying issue.
| base = tx_base_url.rstrip("/") | ||
| query = urllib.parse.urlencode({"url": system, "code": code}) | ||
| params = {"url": system, "code": code} | ||
| version = TX_SYSTEM_VERSION_OVERRIDES.get(system) |
smunini
left a comment
There was a problem hiding this comment.
The version=1.0.0 pin in TX_SYSTEM_VERSION_OVERRIDES (and its use in
tx_validate_code) is a client-side workaround for the HTS resolver bug in
#200 — where a bare $validate-code default-resolves audit-event-type to the
incomplete 4.0.1 fragment instead of the complete THO 1.0.0.
I'd rather not merge this. It turns the audit-events CI validator green by
masking the exact server-side defect #200 exists to fix, and #200's own
acceptance criterion is removing this pin. If it lands, the check keeps
passing even after the real bug regresses or is fixed incorrectly, and we lose
the signal.
Let's pause this PR until #200 is resolved, then validate against a bare
(system, code) call with no version override — that's the real end-to-end
assertion we want.
The rest of the migration looks good; the Cloudflare User-Agent fix in the
same file is a legitimate transport change and I'm fine keeping that regardless.
Summary
Closes #183 — migrate CI off the public HL7 test terminology server
tx.fhir.org.Two GitHub Actions workflows reached out to
tx.fhir.orgduring their runs. That server is rate-limited, occasionally unreachable, community-shared, and its server-side content drifts — so our baselines drifted with it and workflows carried "probe availability / skip if down" workarounds. This removes both live dependencies.Changes
1.
audit-events.yml— validation backend → our HTS.github/workflows/audit-events.yml:--tx-base-url→https://hts.heliossoftware.comcrates/hfs/tests/audit/validate_report.py:DEFAULT_TX_BASE_URL→https://hts.heliossoftware.com; rename source idtx-r5-live-validation→hts-live-validation; generalize the report heading (base URL is printed dynamically beneath it)User-Agenton$validate-coderequests (Cloudflare bot filter — see below)2.
hts.yml— comparison oracle → pinned golden valuesPreviously the integration-test job hit
tx.fhir.org/r4and/r5live as the "source of truth" and compared every HTS operation against it (with skip-if-unavailable probes). Rather than repoint the oracle at our own server — which would keep a network dependency and reduce the test to a self-comparison (CI build vs. our own deploy) — this removes the oracle entirely and asserts each operation against pinned golden values: the stable, HL7-defined displays / expansion totals / outcomes for these UTG code systems.tx_basefrom the matrix,TX_BASEfrom the env, and thetx_posthelper.compare_bool/compare_str(HTS-vs-tx) withexpect_bool/expect_str(HTS-vs-golden).mainrun (identical for R4 and R5): genderM/F/UN→Male/Female/Undifferentiated; v3-ActStatus displays == codes;$expandtotals gender = 3, actStatus = 10; subsumes same→equivalent,M/F→not-subsumed; translate v2-0001-source→true, gender-source→false, unmapped→false, reverse→true.$translateloses its tx probe/skip branch — it already ran HTS-only in practice (tx was unreachable from the self-hosted runner) and is fully deterministic since the ConceptMap is seeded by the workflow.Issue checklist (#183)
audit-events.yml's--tx-base-urlat our server → §1hts.yml's oracle and revisit the availability-probe / skip logic → §2 (removed entirely via golden values; probes deleted)hts.yml; separately verifiedhts.heliossoftware.comdoes serve the audit + UTG systems these workflows exercisehts.ymlnow makes no server call at alltx.fhir.org— see belowAudit of remaining
tx.fhir.orgreferenceshts-ig-conformance.yml— already runs against a local HTS; thetx.fhir.orgstrings are comments / intentionally upstream-gated (mode: tx.fhir.org) fixtures. No change.crates/fhirpath/src/terminology_client.rs— unit-test assertions about URL trimming; no network. No change.crates/fhirpath/src/evaluator.rs:645— the FHIRPath runtime library default terminology server. Intentionally left as-is: this is shipped product behavior (not a CI workflow), changing it alters the default for every FHIRPath consumer and would needtest_r5_test_suite's oracle-derived expectations re-derived. Tracked separately from this CI-scoped issue.crates/hts/src/**comments describing "tx.fhir.org behaviour" — documentation only.Notes
/r4or/r5path segment (those 404), and no auth.hfs.heliossoftware.comis the FHIR server (hfsbinary, no terminology ops); the terminology server ishts.heliossoftware.com(htsbinary).__pycache__/+*.pycto.gitignore.Follow-up: Cloudflare bot filter (addresses @smunini review)
The first dispatch failed the "Validate audit coverage and generate report" step across all backends. Root cause:
hts.heliossoftware.comis fronted by Cloudflare, whose bot protection rejects the default PythonurllibUser-Agent with HTTP 403 — Error 1010 (browser_signature_banned) before the request reaches the origin, so every extracted code surfaced as "invalid" (18 unique pairs / 285 occurrences).Fix: present a browser-like
User-Agenton$validate-code. Verified live — plain client → 403; with the header →200and a validParametersresponse for every affected code system.