package zddc import ( "reflect" "testing" ) // resolver matching mechanics: literal beats "*", length must match, most- // specific wins, empty path → root. func TestPolicyTreeResolve(t *testing.T) { tree := PolicyTree{ "": {Title: "root"}, "*": {Title: "project"}, "working": {Title: "literal-working"}, "*/working": {Title: "any-working"}, "*/working/*": {Title: "any-working-any"}, "*/mdl": {Title: "any-mdl"}, "Proj/working": {Title: "proj-working"}, } cases := []struct { segs []string want string // resolved key ok bool }{ {nil, "", true}, // root {[]string{"Proj"}, "*", true}, // no literal "Proj" at level 1 → "*" {[]string{"working"}, "working", true}, // literal beats "*" {[]string{"Proj", "working"}, "Proj/working", true}, // both literal beats "*/working" {[]string{"Other", "working"}, "*/working", true}, // first seg "*", second literal {[]string{"Proj", "mdl"}, "*/mdl", true}, {[]string{"Proj", "working", "Acme"}, "*/working/*", true}, {[]string{"Proj", "nope"}, "", false}, // no len-2 key matches "*/nope" } for _, c := range cases { got, ok := tree.resolveTreeDir(c.segs) if ok != c.ok || (ok && got != c.want) { t.Errorf("resolveTreeDir(%v) = (%q,%v), want (%q,%v)", c.segs, got, ok, c.want, c.ok) } } } // Load the real embedded-default source tree and assert the split content // reproduces the intended document-control policy (the faithfulness check at // the data level; full effective-policy parity is the Layer-2 matrix once the // cascade is wired in Phase 4). func TestLoadPolicyTreeFromDir(t *testing.T) { tree, err := LoadPolicyTreeFromDir("defaults") if err != nil { t.Fatalf("load defaults tree: %v", err) } wantKeys := []string{ "", "*", "*/archive", "*/ssr", "*/incoming", "*/incoming/*", "*/reviewing", "*/reviewing/*", "*/working", "*/working/*", "*/staging", "*/staging/*", "*/mdl", "*/mdl/*", "*/rsk", "*/rsk/*", } for _, k := range wantKeys { if _, ok := tree[k]; !ok { t.Errorf("missing tree key %q", k) } } if len(tree) != len(wantKeys) { t.Errorf("tree has %d keys, want %d: %v", len(tree), len(wantKeys), keysOf(tree)) } // Spot-check the policy each member carries. if got := tree[""].AvailableTools; !reflect.DeepEqual(got, []string{"archive", "browse", "landing"}) { t.Errorf("root available_tools = %v", got) } if _, ok := tree[""].Roles["document_controller"]; !ok { t.Errorf("root missing document_controller role") } if got := tree["*"].ACL.Permissions["project_team"]; got != "r" { t.Errorf("project-level project_team = %q, want r", got) } if got := tree["*"].ACL.Permissions["document_controller"]; got != "rw" { t.Errorf("project-level document_controller = %q, want rw", got) } if got := tree["*/working"].ACL.Permissions["document_controller"]; got != "rwcda" { t.Errorf("working document_controller = %q, want rwcda", got) } if got := tree["*/working"].ACL.Permissions["project_team"]; got != "cr" { t.Errorf("working project_team = %q, want cr", got) } if tree["*/working"].PartySource != "ssr" { t.Errorf("working party_source = %q, want ssr", tree["*/working"].PartySource) } if h := tree["*/working"].History; h == nil || !*h { t.Errorf("working history not true") } if got := tree["*/mdl"].ACL.Permissions["project_team"]; got != "rwc" { t.Errorf("mdl project_team = %q, want rwc", got) } if got := tree["*/archive"].Worm; !reflect.DeepEqual(got, []string{"document_controller"}) { t.Errorf("archive worm = %v, want [document_controller]", got) } if ao := tree["*/working/*"].AutoOwn; ao == nil || !*ao { t.Errorf("working/ auto_own not true") } } // Along returns the governing members in root→leaf order for a path. func TestPolicyTreeAlong(t *testing.T) { tree, err := LoadPolicyTreeFromDir("defaults") if err != nil { t.Fatalf("load: %v", err) } levels := tree.Along([]string{"Proj", "working", "Acme"}) // "", "*", "*/working", "*/working/*" → 4 contributing levels. if len(levels) != 4 { t.Fatalf("Along returned %d levels, want 4", len(levels)) } // Leaf level is working/: auto_own. if ao := levels[3].AutoOwn; ao == nil || !*ao { t.Errorf("leaf level should carry auto_own") } // The working level grants the DC full authority. if got := levels[2].ACL.Permissions["document_controller"]; got != "rwcda" { t.Errorf("level 2 (working) document_controller = %q, want rwcda", got) } } // Phase-4 gate: the embedded per-depth tree assembles to EXACTLY the legacy // defaults.zddc.yaml ZddcFile, so pointing EmbeddedDefaults at the tree is a // behavioral no-op. (The Layer-2 matrix is the decision-level confirmation.) func TestEmbeddedTreeMatchesYAML(t *testing.T) { tree, err := EmbeddedPolicyTree() if err != nil { t.Fatalf("embedded tree: %v", err) } assembled := tree.Assemble() legacy, err := parseBytes(defaultsBytes) if err != nil { t.Fatalf("parse legacy yaml: %v", err) } if !reflect.DeepEqual(assembled, legacy) { t.Errorf("assembled per-depth tree != legacy defaults.zddc.yaml\n assembled=%+v\n legacy=%+v", assembled, legacy) } } func keysOf(t PolicyTree) []string { out := make([]string, 0, len(t)) for k := range t { out = append(out, k) } return out }