Files
ARCADIA-Studio/crates/oa-core/tests/release1_teaching.rs
T

257 lines
9.8 KiB
Rust

//! Engineering oracles for the distributable current-profile lesson.
#[path = "../../../examples/release1/model.rs"]
mod teaching;
use oa_core::{projection, source_project, transitions::*, validation, *};
use teaching::id;
#[test]
fn release1_teaching_regeneration_and_shipped_source_are_exact_with_one_intentional_warning() {
let first = teaching::build();
let second = teaching::build();
assert_eq!(
first.project, second.project,
"fresh planner UUIDs must not churn the teaching source"
);
assert_eq!(first.before_transition, second.before_transition);
assert_eq!(first.request, second.request);
let bundle = teaching::checked_bundle(&first.project);
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples/release1");
for (name, bytes) in bundle.files {
assert_eq!(
std::fs::read_to_string(root.join(&name)).unwrap(),
bytes,
"regenerate {name}"
);
}
assert_eq!(
persistence::decode(&std::fs::read(root.join("model.json")).unwrap()).unwrap(),
first.project
);
for (path, expected) in [
(
"transition-before.json",
serde_json::to_value(&first.before_transition).unwrap(),
),
(
"transition-request.json",
serde_json::to_value(&first.request).unwrap(),
),
(
"expected-diagnostics.json",
serde_json::to_value(validation::validate(&first.project)).unwrap(),
),
] {
let shipped: serde_json::Value =
serde_json::from_slice(&std::fs::read(root.join(path)).unwrap()).unwrap();
assert_eq!(shipped, expected, "regenerate {path}");
}
let issues = validation::validate(&first.project);
assert_eq!(issues.len(), 1, "{issues:?}");
assert_eq!(issues[0].severity, validation::Severity::Warning);
assert_eq!(issues[0].code, "work_without_behavior_context");
assert_eq!(issues[0].element, Some(id("sa_audit_thresholds")));
assert!(
first
.project
.elements
.values()
.filter(|e| e.layer() == Layer::Oa)
.all(|e| e.kind() != Kind::Port)
);
}
#[test]
fn release1_teaching_preserves_explicit_transport_direction_reuse_and_view_identity() {
let lesson = teaching::build();
let p = &lesson.project;
for prefix in ["oa", "sa"] {
assert!(
matches!(p.elements[&id(&format!("{prefix}_assess_step"))].shared(), Some(SharedBody::Occurrence { body: OccurrenceBody::SequenceReference { sequence: Some(target) }, .. }) if *target == id(&format!("{prefix}_assessment_procedure")))
);
assert!(
matches!(p.elements[&id(&format!("{prefix}_assessment_action"))].shared(), Some(SharedBody::Occurrence { body: OccurrenceBody::Action { action: Some(target), .. }, .. }) if *target == id(&format!("{prefix}_assess")))
);
assert!(
p.relations
.values()
.any(|relation| relation.kind() == RelationKind::ChainInvolvement
&& relation.source() == id(&format!("{prefix}_assessment_procedure"))
&& relation.target() == id(&format!("{prefix}_triage")))
);
assert!(
matches!(p.elements[&id(&format!("{prefix}_conversation"))].shared(), Some(SharedBody::Scenario { context: Some(context), .. }) if *context == id(&format!("{prefix}_restore")))
);
assert!(p.relations.values().any(|relation| relation.kind()
== RelationKind::CapabilityInvolvement
&& relation.source() == id(&format!("{prefix}_assess"))
&& relation.target() == id(&format!("{prefix}_triage"))));
let call = &p.elements[&id(&format!("{prefix}_call"))];
let reply = &p.elements[&id(&format!("{prefix}_reply"))];
let body = |e: &Element| {
if let Some(SharedBody::Occurrence { body, .. }) = e.shared() {
body.clone()
} else {
panic!()
}
};
let validation::MessageEndpointResolution::Resolved { source, target } =
validation::resolve_message_endpoints(p, &body(call))
else {
panic!("call not resolved")
};
assert_eq!(
validation::resolve_message_endpoints(p, &body(reply)),
validation::MessageEndpointResolution::Resolved {
source: target,
target: source
}
);
assert!(
matches!(p.elements[&id(&format!("{prefix}_use_field"))].shared(), Some(SharedBody::Occurrence { body: OccurrenceBody::SequenceReference { sequence: Some(target) }, .. }) if *target == id(&format!("{prefix}_field_procedure")))
);
assert!(
matches!(p.elements[&id(&format!("{prefix}_use_conversation"))].shared(), Some(SharedBody::Occurrence { body: OccurrenceBody::ScenarioUse { scenario: Some(target), bindings }, .. }) if *target == id(&format!("{prefix}_conversation")) && bindings.len() == 2)
);
}
let full = projection::project_view(p, id("sa_allocation_view"));
let focused = projection::project_view(p, id("sa_service_view"));
let edges: std::collections::BTreeSet<_> = full.edges.iter().map(|e| e.id).collect();
assert_eq!(
edges,
[
id("sa_contact"),
id("sa_repair_request"),
id("sa_repair_report")
]
.into()
);
assert_eq!(focused.edges.len(), 1);
assert_eq!(focused.edges[0].id, id("sa_contact"));
assert!(
p.diagrams[&id("sa_service_view")]
.view_options
.exchange_labels
.label(p, focused.edges[0].id)
.contains("Valve repair")
);
let ports = |view: &projection::ProjectedView| {
view.ports
.iter()
.map(|v| (v.element, v.position))
.collect::<std::collections::BTreeMap<_, _>>()
};
assert_eq!(
ports(&full),
ports(&focused),
"filtering must not move the shared technical port geometry"
);
assert!(
p.traces
.values()
.any(|t| t.oa == id("oa_contact") && t.sa == id("sa_contact"))
);
}
#[test]
fn release1_teaching_rejects_guessed_ports_and_wrong_performer_without_changing_source() {
let lesson = teaching::build();
let mut missing_decisions = lesson.request.clone();
missing_decisions.communication_endpoints.clear();
let plan = plan_transition(&lesson.before_transition, &missing_decisions).unwrap();
assert!(
!plan.conflicts.is_empty(),
"an operational channel does not determine technical ports"
);
let compiled = source_project::compile_bundle(
&teaching::checked_bundle(&lesson.project),
source_project::ProjectLimits::default(),
)
.unwrap();
let mut wrong = lesson.project.relations[&id("oa_repair_request")].clone();
wrong.body.set_endpoints(id("oa_assess"), id("oa_reset"));
assert!(
execute_batch(
&lesson.project,
&[Command::EditRelation(wrong.id, wrong.clone())]
)
.is_err()
);
let mut candidate = lesson.project.clone();
candidate.relations.insert(wrong.id, wrong);
assert!(
validation::validate(&candidate)
.iter()
.any(|i| i.severity == validation::Severity::Error
&& i.code == "exchange_allocation_performer")
);
assert!(
source_project::update_bundle(
&compiled,
&candidate,
source_project::ProjectLimits::default()
)
.is_err()
);
assert_eq!(compiled.decoded.project, lesson.project);
}
#[test]
fn release1_teaching_review_changes_only_the_selected_counterpart_and_preserves_modules() {
let lesson = teaching::build();
let mut changed = lesson.project.clone();
changed.elements.get_mut(&id("oa_confirm")).unwrap().name = "Confirm stable pressure".into();
let compiled = source_project::compile_bundle(
&teaching::checked_bundle(&changed),
source_project::ProjectLimits::default(),
)
.unwrap();
let mut transition =
TransitionRequest::new(TransitionDirection::OaToSa, vec![id("oa_confirm")]);
transition.fields.clear();
transition.mappings.push(TransitionMapping {
source: id("oa_confirm"),
target: Some(id("sa_confirm")),
target_kind: None,
});
let mut request = SemanticTransitionRequest::new(transition);
request.reviews.push(SemanticReviewRequest {
source: id("oa_confirm"),
resolutions: vec![FacetResolution {
facet: FacetSelector::Header(HeaderFacet::Name),
decision: SemanticDecision::TakeSource,
}],
reset: vec![],
});
let plan = plan_semantic_transition(&changed, request).unwrap();
assert!(plan.conflicts.is_empty(), "{:?}", plan.conflicts);
let accepted = apply_semantic_transition(&changed, &plan).unwrap();
assert_eq!(
accepted.elements[&id("sa_confirm")].name,
"Confirm stable pressure"
);
assert_eq!(
accepted.elements[&id("sa_control")].name,
lesson.project.elements[&id("sa_control")].name
);
let updated = source_project::update_bundle(
&compiled,
&accepted,
source_project::ProjectLimits::default(),
)
.unwrap();
assert_eq!(updated.decoded.project, accepted);
assert_eq!(
updated.bundle.files.keys().collect::<Vec<_>>(),
compiled.bundle.files.keys().collect::<Vec<_>>()
);
for (path, original) in &compiled.bundle.files {
if path != "sa/functions/function_sa_restore_work.arcadia" && path != "traceability.arcadia"
{
assert_eq!(
&updated.bundle.files[path], original,
"unrelated module changed: {path}"
);
}
}
}