16. Testing (Connectathon approach)

16.1 Actors

Actor Role

Federation Tier (Gateway)

System under test for the gateway profile; presents the openEHR Query API, resolves identity, fans out, combines, routes follow-ups.

Node (openEHR CDR + Query API)

System under test for the node profile; runs standard AQL scoped to an ehr_id; never receives subject.

PIX Manager

Resolves sourceIdentifier → targetSystem (local ehr_id) via PIXm $ihe-pix (ITI-83); seeded by ITI-104.

Localization service (optional)

Returns candidate nodes (XCPD ITI-55; or a regional service). MAY additionally return a consent decision if the deployment under test is consent-aware (§14.3).

Consent service (optional)

Supplies the Step-1 pre-filter decision where a deployment has one (N27a). Absent in a minimal conformant deployment; consent is then enforced at the nodes alone.

Client (Consumer)

Drives queries and follow-ups; verifies it needs no federation-specific syntax.

16.2 Two conformance profiles

A system declares conformance to one or both profiles (mirroring the Exchange-Routing IG’s two CapabilityStatements, "Intermediary" and "Destination Server"):

  • Federation-Gateway - implements the transparent façade and the ITS-REST surface, Step-1 resolution, query-side identifier hygiene, fan-out, combine/annotate, dedup policy, completeness/timeout semantics, follow-up read/write routing, and auth conveyance (N1–N43).

  • Federation-Node - implements a standard openEHR Query API and ITS-REST scoped by ehr_id, passes through node-level errors, honours per-node authorization/consent, never requires subject, and satisfies the federation’s admission conditions (§12b.2).

The Federation-Node profile is thin because the specification’s scope stops at a node’s interface; the asymmetry with the gateway profile is a scope boundary. A node must be invocable on ehr_id alone, must not require subject, must enforce consent before it releases data, and must meet the identifier-integrity conditions of §12b.2. The specification does not cover node internals: it does not mandate where a directly identifying identifier is stored (N34), does not constrain what a node holds (N5), and leaves the wider admission profile unsettled (§12b.3), so that a federation can admit CDRs it did not design.

The thin profile has a consequence for scoring. A conformance point marked Node or Operator in §17 is scored against that actor. CP-18, CP-19 and CP-27 are node obligations; CP-20 and CP-33a are operator obligations verified at admission or in the registry; and a gateway is not marked down for any of them. A fuller node profile is a candidate for a future release (§18).

16.3 Test tracks

# Track Asserts Key requirements

1

Transparency

An unmodified openEHR client queries the gateway with a plain patient AQL and gets a single-CDR-shaped result (no endpoint columns unless selected); columns[] is the same for the same query however the nodes responded (CP-35).

N1, N2, N17, N18

2

subject → ehrId resolution

The gateway resolves subject to per-node ehr_id and dispatches AQL keyed on ehr_id; assert nodes never receive subject by inspecting node-side AQL. Run the same query in both carriers, EHR_STATUS.subject.external_ref and an ENTRY-level subject PARTY_IDENTIFIED/DV_IDENTIFIER predicate. Both MUST resolve and return the same rows (CP-38), since neither is optional.

N3, N5, N7, N33

3

Directed / endpoint pin

FROM ENDPOINT […​] (and ORGANISATION […​]) select the node set; not-resolved list members are reported, not errored; per-node scope stays the resolved ehr_id.

N10, N11, §8

4

Partial results

Unresponsive / node-error / not-resolved / consent-denied / not-localized nodes appear in meta.federation.endpoints[] with the right status from §11.1 (and an OperationOutcome warning/incomplete for FHIR consumers); "found nowhere" → 200 + empty rows; status codes per §11.2 - assert a node timeout under the default strategy yields 504 (a node answering 500 yields 424 and is reported node-error, not offline; one of each yields 504), that the failing response still carries the meta.federation envelope naming the failed node, and that 200 with flagged partial rows appears only when openEHR-federation-completeness: partial was requested against a gateway offering it. Assert the two carve-outs survive the default: all-not-resolved → 200 + empty rows, and a consent-denied node → 200 with the remaining rows, both with complete: false and neither a 424 (§11.3). Assert also that registry members localization did not name are reported not-localized and do not clear meta.federation.complete.

N6, N16, §11

5

Dedup + DISTINCT

Default pass-through leaves duplicates; DISTINCT and ORDER BY work at the Tier; opt-in object_id dedup keeps the originating copy for an imported composition.

N13, N15

6

Follow-up read/write routing

A follow-up read/write for a row routes to the owning CDR via creating_system_id; uids are unchanged; a write to an existing object hits exactly the controlling CDR; an unroutable write is rejected.

N21, N22, N23

7

Auth conveyance + consent-deny

The gateway authenticates onward and conveys client identity (OAuth 2.0 client-credentials with an RFC 7523 signed JWT client assertion, verified against the gateway’s JWKS, whose location the gateway publishes in OPTIONS {base}/ as federation.auth.jwks_uri - §13.1; or the deployment’s regional equivalent); sensitive-data decisions are delegated to the node. Consent is exercised in three configurations: (a) no consent service - a node denies on consent and the row set and meta.federation.endpoints[] reflect it; (b) consent service present - a node dropped at Step 1 is reported consent-denied and is never dispatched to; (c) the disagreement case - localization returns a node, the node denies anyway, and the query still succeeds with that node reported. A directed query (FROM ENDPOINT, no localization) MUST still be consent-checked at the node.

N24, N25, N26, N27, N27a

8

PMIR merge/split (ITI-93/94) (provisional)

An identity merge/split at the identity source propagates so a subsequent federated query resolves the surviving identity. (Hooks reserved; full propagation MAY be deferred, §18.)

N3, §18

9

REST surface & self-description

An unmodified openEHR client, configured only with the registry base URL, performs a read and a write through the gateway - no /rest/openehr or other prefix assumed; both EHR addressing forms work; OPTIONS {base}/ describes the surface and lists the endpoints behind the gateway; write responses carry the acting-endpoint headers with Location/ETag untouched; DEMOGRAPHIC behaves as declared; definition routes to one node (and, if offered, template broadcast reports per node and never reports a partial failure as success). Where a gateway declares definition.stored_query_registry, additionally: a definition PUT at the gateway is invocable by name and fans out across members, the response carries the ITS-REST name member, a repeat PUT to the same {name}/{version} is refused, an endpoint-targeted definition is refused for fan-out, and a declared definition fan-out reports a one-node rejection as a partial success (CP-40).

N28–N32, N43, N44

10

Identifier leakage (adversarial)

The same directly identifying identifier is supplied four ways in the query surface (external_ref predicate, PARTY_IDENTIFIED/DV_IDENTIFIER predicate, SELECT projection, and query string or header), and node-side capture is inspected for any occurrence of the value in the dispatched AQL, path, query string or headers. Zero occurrences is a pass, and so is a 400 rejection. A forwarded value is a fail, including in a logged or echoed query. A converse check also applies: a COMPOSITION committed with a DV_IDENTIFIER in its content MUST arrive at the node byte-identical, so a gateway that strips or rewrites a write body fails this track. The track also asserts the node is invocable on ehr_id alone.

N33, N34, N5

11

Integrity: ehr_id collision & admission conditions

Gateway: a deliberately seeded duplicate ehr_id across two nodes - a harness-injected fixture; it does not represent a conformant node state, and creating it is the test harness operator’s responsibility, since §12b.2 forbids a node from reaching this state on its own - yields 409 and an incident, never a served row or an applied write; a write to an ambiguous path ehr_id is rejected and never probed. Node, at admission: candidate nodes are checked against the identifier-integrity conditions of §12b.2 (UUID-v4 generation, no reuse, no adoption of foreign ehr_id s, federation-unique system_id, and a working patient-identifier → ehr_id exchange).

N41, N42, N42a

Track 8 is provisional. Its subject matter, full propagation of PMIR identity merges and splits, MAY be deferred to a future release (§18), and the track is numbered here so the hooks have somewhere to be asserted once it is not. It is accordingly the only track that no conformance point in §17 scores. A claim of "passes tracks 1–11" is not weakened by its omission, and a scorer should not read its absence as a gap. Equally, a deferred item should not be taken to carry the same weight as the tracks around it.

Tracks 10 and 11 are adversarial, designed to fail a gateway that is merely plausible. Track 10 in particular should be run with node-side wire capture and not the gateway’s own logs, because a gateway that sanitises its logs but not its dispatches passes the wrong test. The track cuts both ways: it fails a gateway that leaks an identifier into a dispatched query, and a gateway that takes it upon itself to rewrite a commit body.

16.4 Process

Run a peer-to-peer test matrix (each Gateway against each Node, each Node against each PIX Manager), with Gazelle-style pass/fail logging per test track and conformance point. Each executed test records the conformance points it exercised (§17), so coverage is auditable.