ZDDC/zddc/internal/zddc/zippolicy_test.go
ZDDC 1e0e403f1e feat(zddc): retire defaults.zddc.yaml; .zddc.zip is the policy carrier (phase 6)
Completes the migration. The embedded per-depth tree (internal/zddc/defaults/)
is now the sole source of the shipped baseline; defaults.zddc.yaml is deleted.

  - EmbeddedDefaults() assembles the tree (no yaml). show-defaults now emits a
    .zddc.zip (per-depth, "*" wildcard members) via EmbeddedDefaultsZip() —
    operators redirect it to <ROOT>/.zddc.zip (or any directory) and edit/add/
    delete individual members.
  - Dropped EmbeddedDefaultsBytes; reworked the dumpable test to validate the
    emitted zip; removed the now-redundant tree-vs-yaml oracle (the Layer-2
    matrix is the ongoing behavioral guarantee, and it stays green).
  - Swept stale "defaults.zddc.yaml" comment references to the embedded tree.
  - GRAMMAR.md §1/§6 updated: .zddc.zip is a policy bundle mountable at ANY
    directory (subtree mount; inherit:false + acl.inherit:false = island); the
    shipped baseline is the embedded bundle at the root.

Net of the 6-phase migration: policy is per-depth .zddc files in a .zddc.zip
that an operator can drop at any level to override the cascade; the engine
(Assemble + the unchanged walker) enforces it. Full Go suite + matrix green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 11:35:21 -05:00

128 lines
4.6 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)
}
}
func keysOf(t PolicyTree) []string {
out := make([]string, 0, len(t))
for k := range t {
out = append(out, k)
}
return out
}