use std::collections::HashMap;
use std::sync::Mutex;
use reqwest::StatusCode;
use crate::perf::PerfOp;
use crate::perf_run::client::{
PerfClient, location_last_segment, object_uid_of, strip_weak_quotes,
};
use crate::perf_run::corpus::{
ADHOC_AQL, ANALYTICS_AQL, STORED_QUERY_NAME, SeededCorpus, TERMINOLOGY_AQL, WARD_AQL,
};
use crate::perf_run::pack::{self, JourneyPack};
use crate::perf_run::schedule::{PlannedArrival, WardDoc};
#[derive(Debug, Default, Clone)]
struct JourneyState {
ehr_id: Option<String>,
last_commit_ovid: Option<String>,
directory_ovid: Option<String>,
contribution_uid: Option<String>,
status_ovid: Option<String>,
party_uid: Option<String>,
party_ovid: Option<String>,
relationship_uid: Option<String>,
}
#[derive(Debug, Default)]
#[expect(
clippy::struct_field_names,
reason = "each field IS an ovid of a distinct document"
)]
struct PatientState {
gp_ovid: Option<String>,
medlist_ovid: Option<String>,
directory_ovid: Option<String>,
status_ovid: Option<String>,
}
#[derive(Debug)]
pub(crate) struct CaptureStore {
journeys: Vec<Mutex<HashMap<u64, JourneyState>>>,
patients: Vec<Mutex<HashMap<usize, PatientState>>>,
}
const SHARDS: usize = 64;
fn shard_of(id: u64) -> usize {
#[expect(
clippy::as_conversions,
reason = "the shard count widens exactly: usize is at most 64 bits on every supported target"
)]
let shards = SHARDS as u64;
#[expect(
clippy::expect_used,
reason = "the remainder is below SHARDS, itself a usize, so the narrowing should be total"
)]
let shard = usize::try_from(id % shards).expect("a remainder below SHARDS should fit a usize");
shard
}
impl CaptureStore {
pub(crate) fn new() -> Self {
Self {
journeys: (0..SHARDS).map(|_| Mutex::new(HashMap::new())).collect(),
patients: (0..SHARDS).map(|_| Mutex::new(HashMap::new())).collect(),
}
}
fn journey<R>(&self, id: u64, f: impl FnOnce(&mut JourneyState) -> R) -> Option<R> {
let shard = shard_of(id);
let mut map = self
.journeys
.get(shard)?
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
Some(f(map.entry(id).or_default()))
}
fn patient<R>(&self, index: usize, f: impl FnOnce(&mut PatientState) -> R) -> Option<R> {
let shard = index % SHARDS;
let mut map = self
.patients
.get(shard)?
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
Some(f(map.entry(index).or_default()))
}
fn drop_journey(&self, id: u64) {
let shard = shard_of(id);
if let Some(mutex) = self.journeys.get(shard) {
let mut map = mutex
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
map.remove(&id);
}
}
}
fn note(observed: &mut Option<u16>, status: StatusCode) -> StatusCode {
*observed = Some(status.as_u16());
if status == StatusCode::TOO_MANY_REQUESTS {
crate::perf_run::note_rate_limited();
}
status
}
fn created(status: StatusCode) -> bool {
status == StatusCode::CREATED || status == StatusCode::NO_CONTENT
}
fn updated(status: StatusCode) -> bool {
status == StatusCode::OK || status == StatusCode::NO_CONTENT
}
fn stride(arrival: u64) -> u128 {
u128::from(arrival) * 2_654_435_761
}
fn pool_index(arrival: u64, len: usize) -> usize {
#[expect(
clippy::as_conversions,
reason = "the pool length widens exactly: usize is at most 64 bits on every supported target"
)]
let modulus = u128::from(len.max(1) as u64);
#[expect(
clippy::expect_used,
reason = "the remainder is below `len`, itself a usize, so the narrowing should be total"
)]
let index = usize::try_from(stride(arrival) % modulus)
.expect("a remainder below a usize pool length should fit a usize");
index
}
fn create_ehr(
client: &PerfClient,
journey: u64,
captures: &CaptureStore,
observed: &mut Option<u16>,
) -> Result<bool, String> {
let reply = client.request(reqwest::Method::POST, "/ehr", None, true, None)?;
if created(note(observed, reply.status))
&& let Some(id) = reply.location.as_deref().and_then(location_last_segment)
{
captures.journey(journey, |s| s.ehr_id = Some(id));
Ok(true)
} else {
Ok(false)
}
}
#[expect(
clippy::too_many_lines,
reason = "one match arm per closed-vocabulary operation"
)]
#[expect(
clippy::disallowed_types,
reason = "the AQL request bodies this sends are wire JSON whose shape belongs to the SUT"
)]
pub(crate) fn perform(
client: &PerfClient,
arrival_index: u64,
planned: &PlannedArrival,
corpus: &SeededCorpus,
journey_pack: &JourneyPack,
captures: &CaptureStore,
observed: &mut Option<u16>,
) -> Result<bool, String> {
let offset_s = planned.at.as_secs();
let journey = planned.journey;
let addressed: Option<String> = if planned.op == PerfOp::EhrCreate {
None
} else if let Some(patient) = planned.patient {
Some(
corpus
.ehr_ids
.get(corpus.ward.get(patient).map_or(patient, |w| w.ehr_index))
.cloned()
.ok_or_else(|| "ward patient outside the corpus".to_owned())?,
)
} else {
Some(
captures
.journey(journey, |s| s.ehr_id.clone())
.flatten()
.ok_or_else(|| "prerequisite EHR not yet created (SUT stall)".to_owned())?,
)
};
let Some(ehr_id) = addressed else {
let ok = create_ehr(client, journey, captures, observed)?;
if planned.last {
captures.drop_journey(journey);
}
return Ok(ok);
};
let ward = planned.patient.and_then(|p| corpus.ward.get(p));
let ok = match planned.op {
PerfOp::EhrCreate => create_ehr(client, journey, captures, observed)?,
PerfOp::EhrRead => {
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::EhrStatusRead => {
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/ehr_status"),
None,
false,
None,
)?;
let status = note(observed, reply.status);
if status == StatusCode::OK
&& let Some(ovid) = reply.etag.as_deref().map(strip_weak_quotes)
{
if let Some(patient) = planned.patient {
captures.patient(patient, |s| s.status_ovid = Some(ovid));
} else {
captures.journey(journey, |s| s.status_ovid = Some(ovid));
}
}
status == StatusCode::OK
}
PerfOp::EhrStatusUpdate => {
let preceding = planned
.patient
.and_then(|p| captures.patient(p, |s| s.status_ovid.clone()))
.flatten()
.or_else(|| {
captures
.journey(journey, |s| s.status_ovid.clone())
.flatten()
})
.ok_or_else(|| "prerequisite EHR_STATUS read has not landed".to_owned())?;
let reply = client.request(
reqwest::Method::PUT,
&format!("/ehr/{ehr_id}/ehr_status"),
Some(("application/json", pack::ehr_status_body(offset_s))),
true,
Some(&preceding),
)?;
let ok = updated(note(observed, reply.status));
let ovid = if ok {
reply.etag.as_deref().map(strip_weak_quotes)
} else {
refresh_current_ovid(client, &format!("/ehr/{ehr_id}/ehr_status"))
};
if let Some(ovid) = ovid {
if let Some(patient) = planned.patient {
captures.patient(patient, |s| s.status_ovid = Some(ovid));
} else {
captures.journey(journey, |s| s.status_ovid = Some(ovid));
}
}
ok
}
PerfOp::CompositionCommit => {
let template = planned
.template
.and_then(|i| journey_pack.get(i))
.ok_or_else(|| "commit stage without a pack template".to_owned())?;
let body = pack::composition_body(template, offset_s, arrival_index)?;
let reply = client.request(
reqwest::Method::POST,
&format!("/ehr/{ehr_id}/composition"),
Some(("application/json", body)),
true,
None,
)?;
if created(note(observed, reply.status))
&& let Some(uid) = reply.etag.as_deref().map(strip_weak_quotes)
{
captures.journey(journey, |s| s.last_commit_ovid = Some(uid));
true
} else {
false
}
}
PerfOp::CompositionRead => {
let index = pool_index(arrival_index, corpus.compositions.len());
let (ehr_index, uid) = corpus
.compositions
.get(index)
.ok_or_else(|| "corpus has no compositions".to_owned())?;
let read_ehr = corpus
.ehr_ids
.get(*ehr_index)
.ok_or_else(|| "corpus composition references a missing EHR".to_owned())?;
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{read_ehr}/composition/{uid}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::CompositionReadCurrent => {
let uid = captures
.journey(journey, |s| s.last_commit_ovid.clone())
.flatten()
.map(|ovid| object_uid_of(&ovid))
.or_else(|| ward.map(|w| object_uid_of(&w.gp_ovid)))
.ok_or_else(|| "prerequisite commit has not landed (SUT stall)".to_owned())?;
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/composition/{uid}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::CompositionRevisionHistory => {
let uid = current_doc_object_uid(planned, captures, ward)
.ok_or_else(|| "no document for revision history".to_owned())?;
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/versioned_composition/{uid}/revision_history"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::CompositionUpdate => {
let template = planned
.template
.and_then(|i| journey_pack.get(i))
.ok_or_else(|| "update stage without a pack template".to_owned())?;
let patient = planned
.patient
.ok_or_else(|| "versioned update addresses a ward patient".to_owned())?;
let preceding = captures
.patient(patient, |s| match planned.doc {
WardDoc::MedList => s.medlist_ovid.clone(),
WardDoc::Gp => s.gp_ovid.clone(),
})
.flatten()
.or_else(|| {
ward.map(|w| match planned.doc {
WardDoc::MedList => w.medlist_ovid.clone(),
WardDoc::Gp => w.gp_ovid.clone(),
})
})
.ok_or_else(|| "no seeded ward document to update".to_owned())?;
let object_uid = object_uid_of(&preceding);
let body = pack::composition_body(template, offset_s, arrival_index)?;
let reply = client.request(
reqwest::Method::PUT,
&format!("/ehr/{ehr_id}/composition/{object_uid}"),
Some(("application/json", body)),
true,
Some(&preceding),
)?;
let ok = updated(note(observed, reply.status));
let next = if ok {
reply.etag.as_deref().map(strip_weak_quotes)
} else {
refresh_current_ovid(client, &format!("/ehr/{ehr_id}/composition/{object_uid}"))
};
if let Some(next) = next {
captures.patient(patient, |s| match planned.doc {
WardDoc::MedList => s.medlist_ovid = Some(next),
WardDoc::Gp => s.gp_ovid = Some(next),
});
}
ok
}
PerfOp::CompositionDelete => {
let preceding = captures
.journey(journey, |s| s.last_commit_ovid.clone())
.flatten()
.ok_or_else(|| "prerequisite commit has not landed (SUT stall)".to_owned())?;
let reply = client.request(
reqwest::Method::DELETE,
&format!("/ehr/{ehr_id}/composition/{preceding}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::NO_CONTENT
}
PerfOp::DirectoryCreate => {
let reply = client.request(
reqwest::Method::POST,
&format!("/ehr/{ehr_id}/directory"),
Some(("application/json", pack::folder_body(false))),
true,
None,
)?;
let ok = created(note(observed, reply.status));
if ok && let Some(ovid) = reply.etag.as_deref().map(strip_weak_quotes) {
captures.journey(journey, |s| s.directory_ovid = Some(ovid));
}
ok
}
PerfOp::DirectoryRead => {
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/directory"),
None,
false,
None,
)?;
let status = note(observed, reply.status);
if status == StatusCode::OK
&& let Some(patient) = planned.patient
&& let Some(ovid) = reply.etag.as_deref().map(strip_weak_quotes)
{
captures.patient(patient, |s| s.directory_ovid = Some(ovid));
}
status == StatusCode::OK
}
PerfOp::DirectoryUpdate => {
let preceding = planned
.patient
.and_then(|p| captures.patient(p, |s| s.directory_ovid.clone()))
.flatten()
.or_else(|| ward.map(|w| w.directory_ovid.clone()))
.or_else(|| {
captures
.journey(journey, |s| s.directory_ovid.clone())
.flatten()
})
.ok_or_else(|| "no directory version to update".to_owned())?;
let reply = client.request(
reqwest::Method::PUT,
&format!("/ehr/{ehr_id}/directory"),
Some(("application/json", pack::folder_body(true))),
true,
Some(&preceding),
)?;
let ok = updated(note(observed, reply.status));
let next = if ok {
reply.etag.as_deref().map(strip_weak_quotes)
} else {
refresh_current_ovid(client, &format!("/ehr/{ehr_id}/directory"))
};
if let Some(next) = next {
if let Some(patient) = planned.patient {
captures.patient(patient, |s| s.directory_ovid = Some(next));
} else {
captures.journey(journey, |s| s.directory_ovid = Some(next));
}
}
ok
}
PerfOp::ContributionCommit => {
let template = planned
.template
.and_then(|i| journey_pack.get(i))
.ok_or_else(|| "contribution stage without a pack template".to_owned())?;
let body = pack::contribution_body(template, offset_s, arrival_index)?;
let reply = client.request(
reqwest::Method::POST,
&format!("/ehr/{ehr_id}/contribution"),
Some(("application/json", body)),
true,
None,
)?;
if created(note(observed, reply.status)) {
let uid = reply
.location
.as_deref()
.and_then(location_last_segment)
.or_else(|| reply.etag.as_deref().map(strip_weak_quotes));
if let Some(uid) = uid {
captures.journey(journey, |s| s.contribution_uid = Some(uid));
}
true
} else {
false
}
}
PerfOp::ContributionRead => {
let uid = captures
.journey(journey, |s| s.contribution_uid.clone())
.flatten()
.or_else(|| ward.map(|w| w.contribution_uid.clone()))
.ok_or_else(|| "no contribution to inspect".to_owned())?;
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/contribution/{uid}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::AdhocQuery => {
let body = serde_json::json!({
"q": ADHOC_AQL,
"query_parameters": { "ehr_id": ehr_id }
});
let bytes = serde_json::to_vec(&body).map_err(|e| e.to_string())?;
let reply = client.request(
reqwest::Method::POST,
"/query/aql",
Some(("application/json", bytes)),
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::WardQuery => {
let body = serde_json::json!({ "q": WARD_AQL });
let bytes = serde_json::to_vec(&body).map_err(|e| e.to_string())?;
let reply = client.request(
reqwest::Method::POST,
"/query/aql",
Some(("application/json", bytes)),
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::StoredQueryExecute => {
let reply = client.request(
reqwest::Method::GET,
&format!("/query/{STORED_QUERY_NAME}?ehr_id={ehr_id}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TemplateList => {
let reply = client.request(
reqwest::Method::GET,
"/definition/template/adl1.4",
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TemplateGet => {
let index = pool_index(arrival_index, journey_pack.templates.len());
let template = journey_pack
.get(index)
.ok_or_else(|| "pack is empty".to_owned())?;
let encoded = urlencoding::encode(&template.template_id);
let reply = client.request(
reqwest::Method::GET,
&format!("/definition/template/adl1.4/{encoded}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TagsPut => {
let uid = current_doc_object_uid(planned, captures, ward)
.ok_or_else(|| "no document to tag".to_owned())?;
let reply = client.request(
reqwest::Method::PUT,
&format!("/ehr/{ehr_id}/composition/{uid}/tags"),
Some(("application/json", pack::tags_body(offset_s))),
false,
None,
)?;
let status = note(observed, reply.status);
updated(status) || status == StatusCode::CREATED
}
PerfOp::TagsRead => {
let uid = current_doc_object_uid(planned, captures, ward)
.ok_or_else(|| "no document to read tags from".to_owned())?;
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/composition/{uid}/tags"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::CompositionVersionRead => {
let ovid = current_version_uid(planned, captures, ward)
.ok_or_else(|| "no committed version to read".to_owned())?;
let vo_uid = object_uid_of(&ovid);
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/versioned_composition/{vo_uid}/version/{ovid}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::CompositionCommitFlat => {
let flat = journey_pack
.aux
.flat
.as_ref()
.ok_or_else(|| "the pack carries no Simplified-FLAT payload".to_owned())?;
let reply = client.request_negotiated(
reqwest::Method::POST,
&format!("/ehr/{ehr_id}/composition"),
Some(("application/openehr.wt.flat+json", pack::flat_body(flat)?)),
true,
None,
None,
&[("openehr-template-id", flat.template_id.clone())],
)?;
if created(note(observed, reply.status))
&& let Some(uid) = reply.etag.as_deref().map(strip_weak_quotes)
{
captures.journey(journey, |s| s.last_commit_ovid = Some(uid));
true
} else {
false
}
}
PerfOp::CompositionReadFlat => {
let ovid = current_version_uid(planned, captures, ward)
.ok_or_else(|| "no committed version to read as FLAT".to_owned())?;
let reply = client.request_negotiated(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}/composition/{ovid}"),
None,
false,
None,
Some("application/openehr.wt.flat+json"),
&[],
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::PartyCreate => {
let person = journey_pack
.aux
.person
.as_ref()
.ok_or_else(|| "the pack carries no PERSON payload".to_owned())?;
let reply = client.request(
reqwest::Method::POST,
"/demographic/person",
Some((
"application/json",
pack::person_body(person, arrival_index)?,
)),
true,
None,
)?;
if created(note(observed, reply.status))
&& let Some(ovid) = reply
.etag
.as_deref()
.map(strip_weak_quotes)
.or_else(|| reply.location.as_deref().and_then(location_last_segment))
{
let uid = object_uid_of(&ovid);
captures.journey(journey, |s| {
s.party_uid = Some(uid);
s.party_ovid = Some(ovid);
});
true
} else {
false
}
}
PerfOp::PartyRead => {
let uid = captures
.journey(journey, |s| s.party_uid.clone())
.flatten()
.ok_or_else(|| "prerequisite PARTY has not landed (SUT stall)".to_owned())?;
let reply = client.request(
reqwest::Method::GET,
&format!("/demographic/person/{uid}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::PartyUpdate => {
let amended = journey_pack
.aux
.person_amended
.as_ref()
.ok_or_else(|| "the pack carries no amended PERSON payload".to_owned())?;
let (uid, preceding) = captures
.journey(journey, |s| s.party_uid.clone().zip(s.party_ovid.clone()))
.flatten()
.ok_or_else(|| "prerequisite PARTY has not landed (SUT stall)".to_owned())?;
let reply = client.request(
reqwest::Method::PUT,
&format!("/demographic/person/{uid}"),
Some((
"application/json",
pack::person_body(amended, arrival_index)?,
)),
true,
Some(&preceding),
)?;
let ok = updated(note(observed, reply.status));
if ok && let Some(next) = reply.etag.as_deref().map(strip_weak_quotes) {
captures.journey(journey, |s| s.party_ovid = Some(next));
}
ok
}
PerfOp::PartyRelationshipCreate => {
let relationship = journey_pack
.aux
.party_relationship
.as_ref()
.ok_or_else(|| "the pack carries no PARTY_RELATIONSHIP payload".to_owned())?;
let source = captures
.journey(journey, |s| s.party_uid.clone())
.flatten()
.ok_or_else(|| "prerequisite PARTY has not landed (SUT stall)".to_owned())?;
let reply = client.request(
reqwest::Method::POST,
"/demographic/party_relationship",
Some((
"application/json",
pack::party_relationship_body(relationship, &source)?,
)),
true,
None,
)?;
if created(note(observed, reply.status))
&& let Some(ovid) = reply
.etag
.as_deref()
.map(strip_weak_quotes)
.or_else(|| reply.location.as_deref().and_then(location_last_segment))
{
captures.journey(journey, |s| s.relationship_uid = Some(object_uid_of(&ovid)));
true
} else {
false
}
}
PerfOp::PartyRelationshipRead => {
let uid = captures
.journey(journey, |s| s.relationship_uid.clone())
.flatten()
.ok_or_else(|| {
"prerequisite PARTY_RELATIONSHIP has not landed (SUT stall)".to_owned()
})?;
let reply = client.request(
reqwest::Method::GET,
&format!("/demographic/party_relationship/{uid}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TemplateExample => {
let index = pool_index(arrival_index, journey_pack.templates.len());
let template = journey_pack
.get(index)
.ok_or_else(|| "pack is empty".to_owned())?;
let encoded = urlencoding::encode(&template.template_id);
let reply = client.request(
reqwest::Method::GET,
&format!(
"/definition/template/adl1.4/{encoded}/example?type=input&detail_level=required"
),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TemplateAdl2List => {
let reply = client.request(
reqwest::Method::GET,
"/definition/template/adl2",
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::ArchetypeAdl2List => {
let reply = client.request(
reqwest::Method::GET,
"/definition/archetype/adl2",
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::AdminContributionReport => {
let reply = client.request(
reqwest::Method::GET,
"/admin/report/contribution/count?a_service=Ehr",
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::EhrExtractExport => {
let reply = client.request(
reqwest::Method::GET,
&format!("/message/export/{ehr_id}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TddImport => {
let tdd = journey_pack
.aux
.tdd
.as_ref()
.ok_or_else(|| "the pack carries no TDD payload".to_owned())?;
let reply = client.request(
reqwest::Method::POST,
&format!("/message/tdd/{ehr_id}"),
Some(("application/xml", tdd.document.as_bytes().to_vec())),
false,
None,
)?;
created(note(observed, reply.status))
}
PerfOp::AnalyticsQuery => {
let body = serde_json::json!({
"q": ANALYTICS_AQL,
"query_parameters": { "ehr_id": ehr_id }
});
let bytes = serde_json::to_vec(&body).map_err(|e| e.to_string())?;
let reply = client.request(
reqwest::Method::POST,
"/query/aql",
Some(("application/json", bytes)),
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::TerminologyQuery => {
let body = serde_json::json!({ "q": TERMINOLOGY_AQL });
let bytes = serde_json::to_vec(&body).map_err(|e| e.to_string())?;
let reply = client.request(
reqwest::Method::POST,
"/query/aql",
Some(("application/json", bytes)),
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::SystemOptions => {
let reply = client.request(reqwest::Method::OPTIONS, "/", None, false, None)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::SmartConfigurationRead => {
let reply = client.request(
reqwest::Method::GET,
"/.well-known/smart-configuration",
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::OK
}
PerfOp::UnauthenticatedProbe => {
let reply = client.request(
reqwest::Method::GET,
&format!("/ehr/{ehr_id}"),
None,
false,
None,
)?;
note(observed, reply.status) == StatusCode::UNAUTHORIZED
}
PerfOp::ReadonlyWriteDenied => {
let template = planned
.template
.and_then(|i| journey_pack.get(i))
.ok_or_else(|| "denied-write stage without a pack template".to_owned())?;
let body = pack::composition_body(template, offset_s, arrival_index)?;
let reply = client.request(
reqwest::Method::POST,
&format!("/ehr/{ehr_id}/composition"),
Some(("application/json", body)),
true,
None,
)?;
note(observed, reply.status) == StatusCode::FORBIDDEN
}
};
if planned.last {
captures.drop_journey(journey);
}
Ok(ok)
}
fn refresh_current_ovid(client: &PerfClient, path: &str) -> Option<String> {
let reply = client
.request(reqwest::Method::GET, path, None, false, None)
.ok()?;
if reply.status == StatusCode::OK {
reply.etag.as_deref().map(strip_weak_quotes)
} else {
None
}
}
fn current_version_uid(
planned: &PlannedArrival,
captures: &CaptureStore,
ward: Option<&crate::perf_run::corpus::WardPatient>,
) -> Option<String> {
captures
.journey(planned.journey, |s| s.last_commit_ovid.clone())
.flatten()
.or_else(|| {
ward.map(|w| match planned.doc {
WardDoc::MedList => w.medlist_ovid.clone(),
WardDoc::Gp => w.gp_ovid.clone(),
})
})
}
fn current_doc_object_uid(
planned: &PlannedArrival,
captures: &CaptureStore,
ward: Option<&crate::perf_run::corpus::WardPatient>,
) -> Option<String> {
captures
.journey(planned.journey, |s| s.last_commit_ovid.clone())
.flatten()
.map(|ovid| object_uid_of(&ovid))
.or_else(|| {
ward.map(|w| match planned.doc {
WardDoc::MedList => object_uid_of(&w.medlist_ovid),
WardDoc::Gp => object_uid_of(&w.gp_ovid),
})
})
}
#[cfg(test)]
#[expect(
clippy::disallowed_types,
reason = "the ixit fixtures are authored as wire JSON, the shape the loader reads"
)]
mod tests {
use super::*;
#[test]
fn the_capture_store_scopes_journeys_and_drops_them() {
let store = CaptureStore::new();
store.journey(7, |s| s.ehr_id = Some("e-7".to_owned()));
store.journey(7 + 64, |s| s.ehr_id = Some("e-71".to_owned()));
assert_eq!(
store.journey(7, |s| s.ehr_id.clone()).flatten().as_deref(),
Some("e-7")
);
assert_eq!(
store
.journey(7 + 64, |s| s.ehr_id.clone())
.flatten()
.as_deref(),
Some("e-71")
);
store.drop_journey(7);
assert_eq!(store.journey(7, |s| s.ehr_id.clone()).flatten(), None);
store.patient(3, |s| s.gp_ovid = Some("g::s::1".to_owned()));
store.patient(3, |s| s.gp_ovid = Some("g::s::2".to_owned()));
assert_eq!(
store.patient(3, |s| s.gp_ovid.clone()).flatten().as_deref(),
Some("g::s::2")
);
}
#[test]
fn the_cleanup_path_recovers_a_poisoned_shard() {
let store = CaptureStore::new();
store.journey(7, |s| s.ehr_id = Some("e-7".to_owned()));
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let panicked = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let held = store.journeys[shard_of(7)].lock().unwrap();
assert!(held.contains_key(&7));
panic!("a worker dies holding the shard");
}));
std::panic::set_hook(previous);
assert!(panicked.is_err(), "the holder must have unwound");
assert!(
store.journeys[shard_of(7)].is_poisoned(),
"the shard must be poisoned for the test to mean anything"
);
store.drop_journey(7);
assert_eq!(
store.journey(7, |s| s.ehr_id.clone()).flatten(),
None,
"the poisoned shard leaked the dropped instance"
);
}
#[test]
fn the_failure_sampling_channel_records_a_bare_wire_number() {
let mut observed = None;
let returned = note(&mut observed, StatusCode::NOT_FOUND);
assert_eq!(returned, StatusCode::NOT_FOUND, "the caller compares typed");
assert_eq!(observed, Some(404), "the recorded channel stays a number");
assert_eq!(
observed.map(|status| format!("unexpected wire status {status}")),
Some("unexpected wire status 404".to_owned())
);
}
#[test]
fn the_prefer_minimal_families_accept_exactly_their_codes() {
assert!(created(StatusCode::CREATED) && created(StatusCode::NO_CONTENT));
assert!(!created(StatusCode::OK) && !created(StatusCode::ACCEPTED));
assert!(updated(StatusCode::OK) && updated(StatusCode::NO_CONTENT));
assert!(!updated(StatusCode::CREATED) && !updated(StatusCode::RESET_CONTENT));
}
#[test]
fn a_fresh_ehr_stage_refuses_before_the_wire_when_the_create_has_not_landed() {
let ixit: crate::ixit::Ixit = serde_json::from_value(serde_json::json!({
"instances": { "sut": { "base_url": "http://stub", "auth": { "mode": "none" } } }
}))
.unwrap();
let client = PerfClient::from_instance(ixit.default_instance().unwrap(), &ixit).unwrap();
let corpus = SeededCorpus {
corpus: "cnf.scale.10k".to_owned(),
ehr_ids: Vec::new(),
compositions: Vec::new(),
ward: Vec::new(),
};
let pack = JourneyPack {
templates: Vec::new(),
aux: pack::AuxPayloads::default(),
};
let planned = PlannedArrival {
at: std::time::Duration::ZERO,
op: PerfOp::EhrRead,
template: None,
journey: 3,
patient: None,
doc: WardDoc::Gp,
recorded: true,
last: false,
};
let captures = CaptureStore::new();
let mut observed = None;
let error = perform(
&client,
0,
&planned,
&corpus,
&pack,
&captures,
&mut observed,
)
.expect_err("an unresolved EHR refuses the stage");
assert_eq!(error, "prerequisite EHR not yet created (SUT stall)");
assert_eq!(observed, None, "nothing reached the wire");
}
#[test]
fn strides_cycle_the_pool() {
let a = stride(1) % 97;
let b = stride(2) % 97;
assert_ne!(a, b);
}
}