use super::*;
#[test]
fn correspondence_validation_enforces_reviewable_overlap_invariants() {
let mut correspondence = correspondence_fixture(
CorrespondenceKind::ConstraintOverlap,
CorrespondencePolarity::Conflicting,
ReviewStatus::Candidate,
);
correspondence.overlap_witnesses.clear();
assert_eq!(
correspondence
.validate()
.expect_err("conflict without overlap should fail")
.code(),
"malformed_field"
);
correspondence.overlap_witnesses = vec![overlap_witness()];
correspondence.review_status = ReviewStatus::Accepted;
correspondence.evidence.clear();
assert_eq!(
correspondence
.validate()
.expect_err("accepted without evidence should fail")
.code(),
"malformed_field"
);
}
#[test]
fn accepted_semantic_correspondence_requires_explicit_semantic_witness() {
let mut correspondence = correspondence_fixture(
CorrespondenceKind::SemanticOverlap,
CorrespondencePolarity::Agreeing,
ReviewStatus::Accepted,
);
correspondence.overlap_witnesses = vec![OverlapWitness {
id: id("witness:context"),
witness_kind: OverlapWitnessKind::ContextRestriction,
shared_structure: SharedStructure::ContextRestriction(ContextRestriction::default()),
participant_mappings: Vec::new(),
scope: Scope::default(),
context: id("ctx:architecture-review"),
evidence: vec![id("evidence:architecture-doc")],
confidence: Confidence::new(0.8).expect("confidence"),
status: ReviewStatus::Candidate,
}];
assert_eq!(
correspondence
.validate()
.expect_err("accepted semantic overlap needs semantic witness")
.code(),
"malformed_field"
);
correspondence.overlap_witnesses = vec![overlap_witness()];
correspondence
.validate()
.expect("normalized claim witness should validate");
}
#[test]
fn accepted_semantic_correspondence_requires_evidence_and_witness_together() {
let mut correspondence = correspondence_fixture(
CorrespondenceKind::SemanticOverlap,
CorrespondencePolarity::Agreeing,
ReviewStatus::Accepted,
);
correspondence.evidence.clear();
assert_eq!(
correspondence
.validate()
.expect_err("accepted semantic overlap without evidence should fail")
.code(),
"malformed_field"
);
correspondence.evidence = vec![id("evidence:architecture-doc")];
correspondence.overlap_witnesses.clear();
assert_eq!(
correspondence
.validate()
.expect_err("accepted semantic overlap without witness should fail")
.code(),
"malformed_field"
);
}
#[test]
fn gluing_success_requires_preservation_and_blocks_silent_merge() {
let mut correspondence = correspondence_fixture(
CorrespondenceKind::StructuralOverlap,
CorrespondencePolarity::Agreeing,
ReviewStatus::Candidate,
);
correspondence.difference_witnesses = vec![blocking_difference()];
correspondence.gluing = Some(GluingAttempt {
id: id("glue:order-service-billing-db-access"),
participants: vec![
ParticipantRef::Claim(id("claim:architecture-doc-order-billing-access")),
ParticipantRef::Invariant(id("invariant:no-cross-context-db-access")),
],
overlap_witnesses: vec![id("witness:shared-order-billing-access")],
difference_witnesses: vec![id("diff:observed-vs-forbidden")],
context: id("ctx:architecture-review"),
invariant_checks: Vec::new(),
preservation_report: PreservationReport::default(),
result: GluingResult::Success {
merged_complex: Some(id("complex:merged")),
preservation_report: PreservationReport::default(),
},
evidence: vec![id("evidence:architecture-doc")],
confidence: Confidence::new(0.8).expect("confidence"),
status: ReviewStatus::Candidate,
override_review: None,
});
assert_eq!(
correspondence
.validate()
.expect_err("success without preservation should fail")
.code(),
"malformed_field"
);
if let Some(gluing) = &mut correspondence.gluing {
gluing.result = GluingResult::Success {
merged_complex: Some(id("complex:merged")),
preservation_report: PreservationReport {
preserved_invariants: vec![id("invariant:no-cross-context-db-access")],
preserved_structures: Vec::new(),
summary: Some("invariant checked".to_owned()),
},
};
}
assert_eq!(
correspondence
.validate()
.expect_err("blocking difference should prevent silent success")
.code(),
"malformed_field"
);
let report = correspondence.validate_report();
assert_eq!(report.findings.len(), 1);
assert_eq!(
report.findings[0].code,
CorrespondenceValidationCode::BlockingDifferenceSilentMerge
);
}
#[test]
fn correspondence_schema_fixture_loads_as_core_model_after_schema_envelope() {
let mut value: serde_json::Value = serde_json::from_str(include_str!(
"../../../../schemas/highergraphen/correspondence.graph.example.json"
))
.expect("fixture json is valid");
assert_eq!(
value["schema"],
json!("highergraphen.correspondence.cell.v1")
);
assert_eq!(value["kind"], json!("CorrespondenceCell"));
let object = value.as_object_mut().expect("fixture is object");
object.remove("schema");
object.remove("kind");
let correspondence: CorrespondenceCell =
serde_json::from_value(value).expect("fixture matches core model payload");
assert_eq!(
correspondence.correspondence_kind,
CorrespondenceKind::ConstraintOverlap
);
assert_eq!(correspondence.polarity, CorrespondencePolarity::Conflicting);
assert!(matches!(
correspondence.participants[0].participant,
ParticipantRef::Claim(_)
));
assert!(correspondence.validate_report().is_valid());
let gluing = correspondence.gluing.expect("fixture has gluing");
assert!(matches!(
gluing.result,
GluingResult::Failure { ref obstruction }
if obstruction == &id("obstruction:direct-db-access-violates-boundary")
));
}
#[test]
fn wire_enum_kind_accessors_match_serde_discriminants() {
for kind in [
OverlapWitnessKind::FeatureSet,
OverlapWitnessKind::PredicateSet,
OverlapWitnessKind::NormalizedClaim,
OverlapWitnessKind::Subgraph,
OverlapWitnessKind::Subcomplex,
OverlapWitnessKind::ConstraintSet,
OverlapWitnessKind::EvidenceSet,
OverlapWitnessKind::Boundary,
OverlapWitnessKind::ProjectionTrace,
OverlapWitnessKind::CausalPattern,
OverlapWitnessKind::ContextRestriction,
] {
let serialized = serde_json::to_value(kind).expect("serialize witness kind");
assert_eq!(serialized, serde_json::json!(kind.kind()));
}
for severity in [
DifferenceSeverity::Informational,
DifferenceSeverity::Minor,
DifferenceSeverity::Major,
DifferenceSeverity::Blocking,
] {
let serialized = serde_json::to_value(severity).expect("serialize severity");
assert_eq!(serialized, serde_json::json!(severity.kind()));
}
let gluing_results = [
GluingResult::Success {
merged_complex: None,
preservation_report: PreservationReport {
preserved_invariants: vec![id("invariant:kept")],
preserved_structures: Vec::new(),
summary: None,
},
},
GluingResult::Candidate {
completion_candidate: id("candidate:gluing"),
required_review: ReviewRequirement::new(true)
.with_decision_reason("review candidate")
.expect("review reason"),
},
GluingResult::Failure {
obstruction: id("obstruction:gluing"),
},
];
for result in gluing_results {
let serialized = serde_json::to_value(&result).expect("serialize gluing result");
assert_eq!(serialized["kind"], result.kind());
}
}
fn correspondence_fixture(
correspondence_kind: CorrespondenceKind,
polarity: CorrespondencePolarity,
review_status: ReviewStatus,
) -> CorrespondenceCell {
CorrespondenceCell {
id: id("corr:order-service-billing-db-access"),
participants: vec![
CorrespondenceParticipant::new(
"observed_claim",
ParticipantRef::Claim(id("claim:architecture-doc-order-billing-access")),
)
.expect("participant"),
CorrespondenceParticipant::new(
"constraint",
ParticipantRef::Invariant(id("invariant:no-cross-context-db-access")),
)
.expect("participant"),
],
correspondence_kind,
polarity,
overlap_witnesses: vec![overlap_witness()],
difference_witnesses: Vec::new(),
context: id("ctx:architecture-review"),
evidence: vec![id("evidence:architecture-doc")],
provenance: id("provenance:architecture-review"),
confidence: Confidence::new(0.9).expect("confidence"),
review_status,
gluing: None,
}
}
fn overlap_witness() -> OverlapWitness {
OverlapWitness {
id: id("witness:shared-order-billing-access"),
witness_kind: OverlapWitnessKind::NormalizedClaim,
shared_structure: SharedStructure::NormalizedClaim(NormalizedClaim {
subject: "OrderService".to_owned(),
relation: "accesses".to_owned(),
object: "BillingDB".to_owned(),
modality: None,
temporal_scope: None,
}),
participant_mappings: Vec::new(),
scope: Scope::default(),
context: id("ctx:architecture-review"),
evidence: vec![id("evidence:architecture-doc")],
confidence: Confidence::new(0.91).expect("confidence"),
status: ReviewStatus::Candidate,
}
}
fn blocking_difference() -> DifferenceWitness {
DifferenceWitness {
id: id("diff:observed-vs-forbidden"),
difference_kind: DifferenceKind::ModalityMismatch,
differing_structure: DifferingStructure::ModalityMismatch(BTreeMap::from([
("observed_claim".to_owned(), "observed".to_owned()),
("constraint".to_owned(), "forbidden".to_owned()),
])),
participant_mappings: Vec::new(),
severity: DifferenceSeverity::Blocking,
context: id("ctx:architecture-review"),
evidence: vec![id("evidence:architecture-doc")],
confidence: Confidence::new(0.93).expect("confidence"),
status: ReviewStatus::Candidate,
}
}