Phase 3 — //go:embed all:defaults bakes the per-depth default tree into the binary; EmbeddedPolicyTree() loads it (LoadPolicyTreeFromFS, generalized to any fs.FS — embed, disk, or zip). Phase 4 — PolicyTree.Assemble() folds the flat per-depth tree into the single nested paths:-bearing ZddcFile the cascade walker already consumes, so the walker is UNCHANGED. EmbeddedDefaults() now sources from the tree via Assemble() instead of parsing defaults.zddc.yaml. Proven behavior-preserving: TestEmbeddedTreeMatchesYAML asserts Assemble(tree) deep-equals the legacy parsed defaults.zddc.yaml, and the Layer-2 matrix + full suite stay green. defaults.zddc.yaml is kept only as that test's oracle (deleted in phase 6). This same Assemble path is what an operator .zddc.zip mounted at any level will use next (phase 5). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
146 lines
5.2 KiB
Go
146 lines
5.2 KiB
Go
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/<party> 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/<party>: 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
|
|
}
|