use super::{admit_with_annotations, admit_with_native_unknowns, RHINO_DRAFT_CHECKS};
use crate::unknown::{NativeUnknownRecord, UnknownRecord};
#[test]
fn native_unknown_admission_matches_product_projection_without_mutation() {
let ctx = cadmpeg_test_support::service_decode_context();
let mut ir = crate::examples::unit_cube().unwrap();
let old_id = "test:source:unknown#old";
let new_id = "test:source:unknown#new";
ir.set_native_unknowns(
&ctx,
"rhino",
&[NativeUnknownRecord {
id: old_id.try_into().unwrap(),
links: vec!["test:model:missing#old".try_into().unwrap()],
}],
)
.unwrap();
for (format, arena) in [("alpha", "records"), ("rhino", "zz"), ("zeta", "records")] {
let id = format!("test:native:record#{format}");
ir.native.namespace_mut(format).arenas_mut().insert(
arena.into(),
vec![
crate::NativeRecord::new(id.as_str().try_into().unwrap(), serde_json::Map::new())
.unwrap(),
],
);
}
let raw = vec![UnknownRecord::retained(
new_id.try_into().unwrap(),
72,
vec![23; 65536],
vec![ir.model.points[0].id.as_str().to_owned()],
)];
let pointer = raw[0].data().unwrap().as_ptr();
let mut builder = crate::AnnotationBuilder::new();
let stream = crate::annotations::StreamHandle::new(crate::stream_name!("test:source"));
builder.note(new_id, &stream, 72, None);
for path in ["id", "links", "links.0", "links.1", "offset", "retention"] {
builder.derived(new_id, path).unwrap();
}
let annotations = builder.build();
let before = serde_json::to_value(&ir).unwrap();
let actual = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
Some(&annotations),
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap()
.unwrap();
let mut projected = ir.clone();
projected
.set_native_unknowns_from(
&ctx,
"rhino",
raw.iter()
.map(|record| NativeUnknownRecord::try_from(record).unwrap()),
)
.unwrap();
let expected = admit_with_annotations(
&ctx,
&projected,
&annotations,
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap();
assert_eq!(
serde_json::to_value(&actual).unwrap(),
serde_json::to_value(&expected).unwrap()
);
assert_eq!(serde_json::to_value(&ir).unwrap(), before);
assert_eq!(raw[0].data().unwrap().as_ptr(), pointer);
assert_eq!(
actual
.findings
.iter()
.filter(|finding| finding.check == crate::report::check::Check::Annotations)
.count(),
3
);
assert!(!actual
.findings
.iter()
.any(|finding| finding.message.contains("missing#old")));
}
#[test]
fn native_unknown_admission_keeps_unresolved_outgoing_link_findings() {
let ctx = cadmpeg_test_support::service_decode_context();
let ir = crate::CadIr::empty();
let raw = [UnknownRecord::retained(
"test:source:unknown#0".try_into().unwrap(),
0,
Vec::new(),
vec!["test:model:missing#0".into()],
)];
let actual = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
None,
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap()
.unwrap();
let links = actual
.findings
.iter()
.filter(|finding| finding.check == crate::report::check::Check::NativeLinks)
.collect::<Vec<_>>();
assert_eq!(links.len(), 1);
assert_eq!(links[0].entity.as_deref(), Some("test:source:unknown#0"));
assert_eq!(
links[0].message,
"native-record link `test:model:missing#0` does not resolve"
);
}
#[test]
fn native_unknown_admission_preserves_outer_depth_refusal() {
use cadmpeg_core::decode::{DecodeArena, DecodeContext, DecodePolicy, ResourceDimension};
use cadmpeg_core::CodecError;
let ir = crate::CadIr::empty();
let raw = [UnknownRecord::retained(
"test:source:unknown#0".try_into().unwrap(),
0,
vec![42; 8192],
Vec::new(),
)];
let arena = DecodeArena::new();
let mut policy = DecodePolicy::service();
policy.limits.max_recursion_depth = 0;
let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &policy).unwrap();
let Err(CodecError::ResourceLimit(limit)) = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
None,
RHINO_DRAFT_CHECKS,
Vec::new(),
) else {
panic!("resource refusal remains outside semantic result");
};
assert_eq!(limit.dimension, ResourceDimension::RecursionDepth);
assert!(
matches!(ctx.finish_session(), Err(CodecError::ResourceLimit(sticky)) if sticky == limit)
);
assert!(ir.native.0.is_empty());
assert_eq!(raw[0].data().unwrap(), &[42; 8192]);
}
#[test]
fn native_unknown_admission_preserves_product_link_grammar_failures() {
let ir = crate::CadIr::empty();
for link in [
"",
"missing",
"test:model:body#",
"test:model:body#with space",
] {
let ctx = cadmpeg_test_support::service_decode_context();
let raw = [UnknownRecord::retained(
"test:source:unknown#0".try_into().unwrap(),
0,
vec![9],
vec![link.into()],
)];
let expected = NativeUnknownRecord::try_from(&raw[0]).unwrap_err();
let error = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
None,
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap()
.unwrap_err();
assert_eq!(error.to_string(), expected.to_string());
ctx.finish_session().unwrap();
assert!(ir.native.0.is_empty());
}
}
#[test]
fn native_unknown_annotation_collision_preserves_first_native_arena() {
let ctx = cadmpeg_test_support::service_decode_context();
for native_format in ["alpha", "zeta"] {
let mut ir = crate::CadIr::empty();
let id = "test:source:unknown#collision";
ir.native.namespace_mut(native_format).arenas_mut().insert(
"records".into(),
vec![crate::NativeRecord::new(
id.try_into().unwrap(),
serde_json::Map::from_iter([("native_only".into(), serde_json::Value::Bool(true))]),
)
.unwrap()],
);
let raw = [UnknownRecord::retained(
id.try_into().unwrap(),
0,
Vec::new(),
Vec::new(),
)];
let mut builder = crate::AnnotationBuilder::new();
builder.derived(id, "native_only").unwrap();
let annotations = builder.build();
let actual = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
Some(&annotations),
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap()
.unwrap();
let mut projected = ir.clone();
projected
.set_native_unknowns_from(
&ctx,
"rhino",
raw.iter()
.map(|record| NativeUnknownRecord::try_from(record).unwrap()),
)
.unwrap();
let expected = admit_with_annotations(
&ctx,
&projected,
&annotations,
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap();
assert_eq!(
serde_json::to_value(actual).unwrap(),
serde_json::to_value(expected).unwrap(),
"{native_format}"
);
}
}
#[test]
fn native_unknown_admission_preserves_sorted_source_finding_order() {
let ctx = cadmpeg_test_support::service_decode_context();
let ir = crate::CadIr::empty();
let raw = (0..12)
.map(|position| {
UnknownRecord::retained(
format!("test:source:unknown#{position}")
.as_str()
.try_into()
.unwrap(),
0,
Vec::new(),
vec![format!("test:model:missing#{position}")],
)
})
.collect::<Vec<_>>();
let actual = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
None,
&[
crate::report::check::Check::NativeLinks,
crate::report::check::Check::ArenaOrder,
],
Vec::new(),
)
.unwrap()
.unwrap();
let mut projected = ir.clone();
projected
.set_native_unknowns_from(
&ctx,
"rhino",
raw.iter()
.map(|record| NativeUnknownRecord::try_from(record).unwrap()),
)
.unwrap();
let expected = super::admit(
&ctx,
&projected,
&[
crate::report::check::Check::NativeLinks,
crate::report::check::Check::ArenaOrder,
],
Vec::new(),
)
.unwrap();
assert_eq!(
serde_json::to_value(&actual).unwrap(),
serde_json::to_value(expected).unwrap()
);
assert_eq!(
actual.findings[2].entity.as_deref(),
Some("test:source:unknown#10")
);
assert_eq!(raw[2].id().as_str(), "test:source:unknown#2");
}
#[test]
fn native_unknown_admission_preserves_duplicate_source_product_failure() {
let ctx = cadmpeg_test_support::service_decode_context();
let ir = crate::CadIr::empty();
let raw = [7_u8, 19].map(|offset| {
UnknownRecord::retained(
"test:source:unknown#duplicate".try_into().unwrap(),
u64::from(offset),
vec![offset],
Vec::new(),
)
});
let actual = admit_with_native_unknowns(
&ctx,
&ir,
("rhino", &raw),
None,
RHINO_DRAFT_CHECKS,
Vec::new(),
)
.unwrap()
.unwrap_err();
let mut projected = ir.clone();
let expected = projected
.set_native_unknowns_from(
&ctx,
"rhino",
raw.iter()
.map(|record| NativeUnknownRecord::try_from(record).unwrap()),
)
.unwrap_err();
assert_eq!(actual.to_string(), expected.to_string());
assert_eq!(actual.to_string(), "native collection is invalid: duplicate native unknown record test:source:unknown#duplicate");
assert!(ir.native.0.is_empty());
}