//! Native issue4 fixture/oracle. generate DIR | check oa|sa STAGE ENTRY //! snapshot ENTRY DIR | compare ENTRY DIR | self-check use oa_core::{source_project::*, *}; use std::{collections::{BTreeMap, BTreeSet}, fs, path::Path}; fn id(n: u128) -> Id { Id::from_u128(0xc1140000000040008000000000000000 + n) } fn work(layer: Layer) -> Kind { if layer == Layer::Oa { Kind::Activity } else { Kind::Function } } fn fixture(layer: Layer) -> Project { let mut p = Project::empty(); p.id = id(1); p.alias = "child_work_native".into(); p.name = format!("Issue4 {layer:?} child work"); for (n, kind, name) in [(10, if layer == Layer::Oa { Kind::Entity } else { Kind::System }, "Performer"), (11, work(layer), "Parent work")] { p.elements.insert(id(n), Element { id: id(n), alias: format!("seed_{n}"), name: name.into(), description: format!("Preserve original {name}"), parent: None, body: ElementBody::new(kind, layer).unwrap() }); } p.relations.insert(id(20), Relation { id:id(20), alias:"original_allocation".into(), name:"Original responsibility".into(), description:"Preserve allocation engineering rationale 雨".into(), body:RelationBody::Allocation { source:id(10), target:id(11) } }); if layer == Layer::Oa { for n in [12,13] { p.elements.insert(id(n), Element { id:id(n), alias:format!("role_{n}"), name:format!("Responsibility role {n}"), description:"Preserve role".into(), parent:None, body:ElementBody::new(Kind::Role, layer).unwrap() }); for (r, kind, source, target) in [(n+10,RelationKind::RoleAssignment,id(n),id(11)),(n+20,RelationKind::RoleRealization,id(10),id(n))] { p.relations.insert(id(r), Relation { id:id(r), alias:format!("responsibility_{r}"), name:format!("Role link {r}"), description:format!("Retain role link metadata {r}"), body:RelationBody::new(kind,source,target) }); } } } for (index, owner) in [id(20),id(22),id(23)].into_iter().filter(|owner| p.relations.contains_key(owner)).enumerate() { let key=id(50+index as u128); p.artifact_links.insert(key, ArtifactLink { id:key, alias:format!("evidence_{index}"), name:format!("Responsibility evidence {index}"), description:"Keep owner identity and evidence metadata".into(), owner, relation:ArtifactRelation::References, uri:format!("https://example.invalid/evidence/{index}"), revision:Some("review-7".into()), fragment:Some("assignment".into()) }); } p.diagrams.insert(id(2), Diagram { id:id(2), alias:"allocation_view".into(), name:format!("{layer:?} allocated work"), description:"Native child-work fixture".into(), layer, projection:ProjectionKind::Allocation, routes:vec![], view_options:Default::default(), nodes:BTreeMap::new() }); for (n,element,x,y,collapsed) in [(40,id(10),60.,60.,false),(41,id(11),84.,124.,true)] { p.diagrams.get_mut(&id(2)).unwrap().nodes.insert(id(n), NodePlacement { id:id(n), alias:format!("seed_view_{n}"), element, position:Position{x,y}, size:Size{width:224.,height:94.}, collapsed, port_anchors:vec![] }); } execute_batch(&p,&[]).unwrap() } fn bundle(p:&Project)->CompiledSourceProject { let source=language::format(p).replacen("{\n","{\n include \"rationale.arcadia\";\n",1); let raw=SourceBundle { entry:"project.arcadia".into(), files:BTreeMap::from([("project.arcadia".into(),source),("rationale.arcadia".into(),"// Preserve included note bytes: responsibility 雨.\r\n".into())]) }; let result=compile_bundle(&raw,ProjectLimits::default()).unwrap(); assert_eq!(result.bundle,raw); assert_eq!(&result.decoded.project,p); result } fn load(entry:&Path)->CompiledSourceProject { let raw=source_files::load_project(entry,ProjectLimits::default()).unwrap().reachable_bundle(); let result=compile_bundle(&raw,ProjectLimits::default()).unwrap(); assert_eq!(result.bundle,raw,"No source normalization permitted"); execute_batch(&result.decoded.project,&[]).unwrap(); result } fn advance(base:&mut CompiledSourceProject,commands:Vec) { let expected=execute_batch(&base.decoded.project,&commands).unwrap(); *base=update_bundle(base,&expected,ProjectLimits::default()).unwrap(); } #[derive(Clone, Copy)] struct Keys { children:[Id;3], nodes:[Id;3], allocation:Id } fn expected(layer:Layer,stage:usize,k:Keys)->CompiledSourceProject { assert!(stage<=3); let mut base=bundle(&fixture(layer)); for step in 0..stage { let p=&base.decoded.project; let key=k.children[step]; let name=format!("New {}",work(layer).label().to_lowercase()); let element=Element { id:key, alias:p.fresh_alias(&name,"element",key), name, description:String::new(), parent:Some(if step==2 { k.children[0] } else { id(11) }), body:ElementBody::new(work(layer),layer).unwrap() }; let mut commands=vec![Command::CreateElement(element.clone())]; if step==1 { commands.push(Command::CreateRelation(Relation { id:k.allocation, alias:p.fresh_alias("Performs","relation",k.allocation), name:"Performs".into(), description:String::new(), body:RelationBody::Allocation { source:id(10),target:key } })); } else { for original in p.relations.values().filter(|r| [id(20),id(22),id(23)].contains(&r.id)) { let mut r=original.clone(); match &mut r.body { RelationBody::Allocation{target,..}|RelationBody::RoleAssignment{target,..}=>*target=key,_=>unreachable!() } commands.push(Command::EditRelation(r.id,r)); } } if step==0 { let mut node=p.diagrams[&id(2)].nodes[&id(41)].clone(); node.collapsed=false; commands.push(Command::EditNode{diagram:id(2),original:node.id,node}); } let mut with_element=p.clone(); with_element.elements.insert(key,element); let (x,y)=[(108.,188.),(108.,298.),(132.,252.)][step]; commands.push(Command::CreateNode { diagram:id(2), node:NodePlacement { id:k.nodes[step], alias:with_element.fresh_alias(&format!("view_{}",with_element.named(key)),"node",k.nodes[step]), element:key,position:Position{x,y},size:Size{width:224.,height:94.},collapsed:false,port_anchors:vec![] } }); if step==2 { // Grandchild expands first child to bottom370; sibling follows at394 // (24px reflow gap), independently predicted for this fixed fixture. let mut sibling=p.diagrams[&id(2)].nodes[&k.nodes[1]].clone(); sibling.position.y=394.; commands.push(Command::EditNode{diagram:id(2),original:sibling.id,node:sibling}); } advance(&mut base,commands); } base } fn only(ids:Vec,role:&str)->Id { assert_eq!(ids.len(),1,"Exactly one {role}"); ids[0] } fn admit(actual:&Project,layer:Layer,stage:usize)->Keys { let mut k=Keys{children:[Id::nil();3],nodes:[Id::nil();3],allocation:Id::nil()}; if stage==0 { return k; } let target=actual.relations[&id(20)].target(); k.children[0]=if stage==3 { actual.elements[&target].parent.unwrap() } else { target }; if stage>=2 { k.children[1]=only(actual.elements.values().filter(|e|e.parent==Some(id(11))&&e.id!=k.children[0]&&e.kind()==work(layer)).map(|e|e.id).collect(),"second child"); k.allocation=only(actual.relations.values().filter(|r|r.kind()==RelationKind::Allocation&&r.source()==id(10)&&r.target()==k.children[1]).map(|r|r.id).collect(),"new sibling allocation"); } if stage==3 { k.children[2]=target; } for step in 0..stage { let e=&actual.elements[&k.children[step]]; assert_eq!(e.kind(),work(layer)); assert_eq!(e.layer(),layer); assert_eq!(e.parent,Some(if step==2{k.children[0]}else{id(11)})); k.nodes[step]=only(actual.diagrams[&id(2)].nodes.values().filter(|n|n.element==e.id).map(|n|n.id).collect(),"child placement"); } let seed=fixture(layer); let mut used=BTreeSet::new(); for key in k.children[..stage].iter().chain(&k.nodes[..stage]).copied().chain((stage>=2).then_some(k.allocation)) { assert_eq!(key.get_version_num(),4,"Native fresh UUIDv4 required"); assert!(!seed.contains(key)&&used.insert(key),"Fresh distinct role identity required"); } k } fn check(actual:&CompiledSourceProject,layer:Layer,stage:usize) { let prediction=expected(layer,stage,admit(&actual.decoded.project,layer,stage)); assert_eq!(actual.decoded.project,prediction.decoded.project,"Complete typed model mismatch, including placements and references"); assert_eq!(actual.bundle,prediction.bundle,"Exact source mismatch, including included note bytes"); } fn history(actual:&CompiledSourceProject,layer:Layer,stage:usize,first:&CompiledSourceProject,second:Option<&CompiledSourceProject>) { check(first,layer,1); let first_keys=admit(&first.decoded.project,layer,1); let current_keys=admit(&actual.decoded.project,layer,stage); assert_eq!(current_keys.children[0],first_keys.children[0],"First child identity must match original first-commit carrier"); assert_eq!(current_keys.nodes[0],first_keys.nodes[0],"First placement identity must match original first-commit carrier"); if stage==3 { let second=second.expect("Original second-commit carrier required"); check(second,layer,2); history(second,layer,2,first,None); let second_keys=admit(&second.decoded.project,layer,2); assert_eq!(current_keys.children[1],second_keys.children[1],"Sibling identity must persist"); assert_eq!(current_keys.nodes[1],second_keys.nodes[1],"Sibling placement identity must persist"); assert_eq!(current_keys.allocation,second_keys.allocation,"Sibling allocation identity must persist"); } } fn write_bundle(dir:&Path,raw:&SourceBundle) { assert!(!dir.exists(),"Use a new evidence directory"); fs::create_dir_all(dir).unwrap(); for (name,bytes) in &raw.files { let path=dir.join(name); if let Some(parent)=path.parent(){fs::create_dir_all(parent).unwrap();} fs::write(path,bytes).unwrap(); } } fn main() { assert_eq!(SCHEMA_VERSION,7); let args:Vec<_>=std::env::args().collect(); match args.get(1).map(String::as_str) { Some("generate")=>{assert_eq!(args.len(),3);let dir=Path::new(&args[2]);assert!(!dir.exists()); for (name,layer) in [("oa",Layer::Oa),("sa",Layer::Sa)] {write_bundle(&dir.join(name),&bundle(&fixture(layer)).bundle);} println!("PASS deterministic OA/SA native fixtures generated");} Some("check")=>{ let layer=match args[2].as_str(){"oa"=>Layer::Oa,"sa"=>Layer::Sa,_=>panic!("oa|sa")}; let stage=args[3].parse::().unwrap();assert!(stage<=3); assert_eq!(args.len(),5+stage.saturating_sub(1),"Stages2/3 require original stage1/stage2 checkpoint directories"); let actual=load(Path::new(&args[4]));check(&actual,layer,stage); if stage>=2 { let first=load(&Path::new(&args[5]).join("project.arcadia")); let second=if stage==3 {Some(load(&Path::new(&args[6]).join("project.arcadia")))} else {None}; history(&actual,layer,stage,&first,second.as_ref()); } println!("PASS complete model, exact source and pinned history stage {stage}"); } Some("snapshot")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));write_bundle(Path::new(&args[3]),&actual.bundle);println!("PASS immutable source checkpoint");} Some("compare")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));let checkpoint=load(&Path::new(&args[3]).join(&actual.bundle.entry));assert_eq!(actual.bundle,checkpoint.bundle,"Saved reachable source bytes changed");assert_eq!(actual.decoded.project,checkpoint.decoded.project);println!("PASS exact checkpoint bytes and model");} Some("self-check")=>{for layer in [Layer::Oa,Layer::Sa]{let keys=Keys{children:std::array::from_fn(|_|Id::new_v4()),nodes:std::array::from_fn(|_|Id::new_v4()),allocation:Id::new_v4()};for stage in 0..=3 {let candidate=expected(layer,stage,keys);check(&candidate,layer,stage);if stage>=2 {history(&candidate,layer,stage,&expected(layer,1,keys),(stage==3).then(||expected(layer,2,keys)).as_ref());}let mut altered=candidate.decoded.project.clone();altered.elements.get_mut(&id(11)).unwrap().description="Unrequested mutation".into();assert_ne!(expected(layer,stage,admit(&altered,layer,stage)).decoded.project,altered);}}println!("PASS oracle fixed points and unrelated-mutation discriminator");} _=>panic!("generate DIR | check oa|sa STAGE ENTRY [STAGE1DIR] [STAGE2DIR] | snapshot ENTRY DIR | compare ENTRY DIR | self-check"), } }