Strengthen mixed Project public-wrapper and storage oracles

This commit is contained in:
2026-09-23 11:27:45 -07:00
parent 8f0d96ca63
commit b9a31bb848
@@ -578,6 +578,35 @@ fn complete_static_architecture_and_unicode_warning_mapping() {
let warning = &resolved.warnings()[0].diagnostic; let warning = &resolved.warnings()[0].diagnostic;
assert_eq!(warning.line, 6); assert_eq!(warning.line, 6);
assert_eq!(warning.column, 1); assert_eq!(warning.column, 1);
// Same physical line: the two header-name scalars occupy seven UTF-8
// bytes. Independently authored coordinates distinguish column60 from
// byte-column65; the warning covers the complete Work declaration.
let inline = SourceBundle {
entry: "main.arcadia".into(),
files: BTreeMap::from([(
"main.arcadia".into(),
format!(
"arcadia 8;\r\nproject p \"港🌊\" id \"{}\" {{ oa_activity w \"Work\" id \"{}\" {{}} }}\r\n",
id(1),
id(2)
),
)]),
};
let inline_value = accepted(&inline);
assert_eq!(inline_value.warnings().len(), 1);
let inline_warning = &inline_value.warnings()[0];
assert_eq!(inline_warning.path, "main.arcadia");
assert_eq!(inline_warning.diagnostic.code, "unallocated_leaf");
assert_eq!(inline_warning.diagnostic.object_id, Some(id(2)));
assert_eq!(inline_warning.diagnostic.line, 2);
assert_eq!(inline_warning.diagnostic.column, 60);
assert_eq!(
inline_warning.diagnostic.span,
Span {
start: 76,
end: 141
}
);
let mut bad = bundle.clone(); let mut bad = bundle.clone();
*bad.files.get_mut("main.arcadia").unwrap() = *bad.files.get_mut("main.arcadia").unwrap() =
bad.files["main.arcadia"].replace("base 1;", "base true + 1;"); bad.files["main.arcadia"].replace("base 1;", "base true + 1;");
@@ -646,19 +675,32 @@ fn mapped_warning_path_copies_are_admitted_before_retention() {
assert_eq!(roomy.warnings().len(), 1); assert_eq!(roomy.warnings().len(), 1);
assert_eq!(roomy.warnings()[0].diagnostic.code, "unnamed"); assert_eq!(roomy.warnings()[0].diagnostic.code, "unnamed");
assert_eq!(roomy.warnings()[0].path, bundle.entry); assert_eq!(roomy.warnings()[0].path, bundle.entry);
let profile = super::super::super::budget::Limits { // Retained syntax owns alias "p" (1 byte). The mapped warning owns path188
bytes: source.len(), // + code "unnamed"7 + field "name"4 + message36 =235 additional bytes.
..super::super::super::budget::Limits::source8(language::ParseLimits::UNLIMITED) // A per-producer cap would accept235; their simultaneous live total is236.
}; assert_eq!(bundle.entry.len(), 188);
let parsed = parse_with_profile(&bundle, ProjectLimits::default(), profile).unwrap(); assert_eq!("Give the project a descriptive name.".len(), 36);
let failure = match resolve(parsed) { for (bytes, fits) in [(source.len(), false), (235, false), (236, true)] {
Ok(_) => panic!("truncated warnings or uncharged copied path"), let profile = super::super::super::budget::Limits {
Err(e) => e, bytes,
}; ..super::super::super::budget::Limits::source8(language::ParseLimits::UNLIMITED)
assert_eq!( };
failure.diagnostics[0].diagnostic.message, let parsed = parse_with_profile(&bundle, ProjectLimits::default(), profile).unwrap();
"Source8 index/output byte limit exceeded." match resolve(parsed) {
); Ok(value) => {
assert!(fits, "mapped diagnostics lost their live syntax overlap");
assert_eq!(value.warnings(), roomy.warnings());
assert_eq!(value.storage.live.text, 236);
}
Err(failure) => {
assert!(!fits, "exact total must fit: {failure:?}");
assert_eq!(
failure.diagnostics[0].diagnostic.message,
"Source8 index/output byte limit exceeded."
);
}
}
}
} }
#[test] #[test]
@@ -796,20 +838,13 @@ fn retained_nested_record_policy_cannot_be_replaced_at_resolution() {
} }
#[test] #[test]
fn shared_ordinary_construction_preserves_legacy_scratch_and_existing_errors() { fn public_source3_and_source7_preserve_legacy_scratch_defaults_and_errors() {
let bundle = single(&format!("oa_activity w \"Work\" id \"{}\" {{}}", id(2))); let bundle = single(&format!("oa_activity w \"Work\" id \"{}\" {{}}", id(2)));
let parsed = parse(&bundle, ProjectLimits::default()).unwrap(); // Existing source3_bridge tests compare complete public source3/JSON3
let file = &parsed.files[0]; // migration. This small shared-decoder regression uses that public wrapper
let mut old = file.ordinary.header.as_ref().unwrap().project.clone(); // too, retaining the independent exact Activity expectation below.
old.schema_version = 3; let old_source = bundle.files["main.arcadia"].replacen("arcadia 8;", "arcadia 3;", 1);
let aliases = BTreeMap::from([("p".into(), id(1)), ("w".into(), id(2))]); let migrated = language::parse_with_report(&old_source).unwrap();
let migrated = language::resolve_declarations(
old,
&file.ordinary.declarations,
&aliases,
language::ParseLimits::UNLIMITED,
)
.unwrap();
assert_eq!(migrated.project.schema_version, SCHEMA_VERSION); assert_eq!(migrated.project.schema_version, SCHEMA_VERSION);
assert_eq!( assert_eq!(
migrated.project.elements[&id(2)], migrated.project.elements[&id(2)],
@@ -823,9 +858,11 @@ fn shared_ordinary_construction_preserves_legacy_scratch_and_existing_errors() {
} }
); );
let scenario = single(&format!("oa_scenario q \"Scenario\" id \"{}\" {{}}", id(2))); let scenario = single(&format!("oa_scenario q \"Scenario\" id \"{}\" {{}}", id(2)));
let value = accepted(&scenario); let scenario_source = scenario.files["main.arcadia"].replacen("arcadia 8;", "arcadia 7;", 1);
let value =
language::parse_limited(&scenario_source, language::ParseLimits::UNLIMITED).unwrap();
assert!( assert!(
matches!(value.project.elements[&id(2)].shared(), Some(SharedBody::Scenario { viewpoint: ScenarioViewpoint::MixedLegacy, steps, .. }) if steps.is_empty()) matches!(value.elements[&id(2)].shared(), Some(SharedBody::Scenario { viewpoint: ScenarioViewpoint::MixedLegacy, steps, .. }) if steps.is_empty())
); );
let missing = single(&format!( let missing = single(&format!(
"node n id \"{}\" {{ diagram @d; }} diagram d \"D\" id \"{}\" {{}}", "node n id \"{}\" {{ diagram @d; }} diagram d \"D\" id \"{}\" {{}}",
@@ -833,7 +870,9 @@ fn shared_ordinary_construction_preserves_legacy_scratch_and_existing_errors() {
id(3) id(3)
)); ));
assert_eq!( assert_eq!(
rejected(&missing).diagnostics[0].diagnostic.message, language::parse(&missing.files["main.arcadia"].replacen("arcadia 8;", "arcadia 7;", 1))
.unwrap_err()[0]
.message,
"Missing required field 'element'." "Missing required field 'element'."
); );
let conflict = single(&format!( let conflict = single(&format!(
@@ -842,7 +881,9 @@ fn shared_ordinary_construction_preserves_legacy_scratch_and_existing_errors() {
id(3) id(3)
)); ));
assert_eq!( assert_eq!(
rejected(&conflict).diagnostics[0].diagnostic.code, language::parse(&conflict.files["main.arcadia"].replacen("arcadia 8;", "arcadia 7;", 1))
.unwrap_err()[0]
.code,
"conflicting_parent" "conflicting_parent"
); );
} }