Repoint default-tool/history/canonical-folder/auto-own/virtual/declared, role-grant, and WORM-zone expectations to the top-level peer layout: archive is now blanket-WORM (DC = rc there), the workspace/register peers carry the DC grants directly, and incoming/working/staging/reviewing/mdl/rsk/ssr are physical peers. ensure_test repointed to top-level paths + the virtual-reject test inverted (peers are physical now). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
165 lines
6.4 KiB
Go
165 lines
6.4 KiB
Go
package zddc
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestEnsureCanonicalAncestors_LazyCreation(t *testing.T) {
|
|
root := t.TempDir()
|
|
// working/ is a top-level peer; its <party> folder auto-owns the
|
|
// creator (unfenced — party admins still cascade in). Per-user email
|
|
// homes were abandoned in the reshape.
|
|
target := filepath.Join(root, "Proj", "working", "ACME", "notes.md")
|
|
|
|
resolved, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755)
|
|
if err != nil {
|
|
t.Fatalf("ensure: %v", err)
|
|
}
|
|
if resolved != target {
|
|
t.Errorf("resolved=%q, target=%q (no case variant exists, should be identical)", resolved, target)
|
|
}
|
|
|
|
autoZ := filepath.Join(root, "Proj", "working", "ACME", ".zddc")
|
|
data, err := os.ReadFile(autoZ)
|
|
if err != nil {
|
|
t.Fatalf("auto-own .zddc not written at working/ACME/: %v", err)
|
|
}
|
|
body := string(data)
|
|
if !strings.Contains(body, "alice@x.com: rwcda") {
|
|
t.Errorf("auto-own grant missing: %s", body)
|
|
}
|
|
if !strings.Contains(body, "created_by: alice@x.com") {
|
|
t.Errorf("created_by missing: %s", body)
|
|
}
|
|
if strings.Contains(body, "inherit: false") {
|
|
t.Errorf("working/<party>/ .zddc should be UNFENCED; got: %s", body)
|
|
}
|
|
// The working/ peer root itself does NOT auto-own (auto_own is at the
|
|
// <party> level).
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "working", ".zddc")); !os.IsNotExist(err) {
|
|
t.Errorf("working/ peer root should not have auto-own .zddc; got err=%v", err)
|
|
}
|
|
}
|
|
|
|
// staging/<party>/<folder>/ is NOT auto-owned — only the <party> level is.
|
|
func TestEnsureCanonicalAncestors_StagingChildNotFenced(t *testing.T) {
|
|
root := t.TempDir()
|
|
target := filepath.Join(root, "Proj", "staging", "ACME",
|
|
"2025-10-31_proj-EM-TRN-0042 (RSA) - Outbound", "doc.pdf")
|
|
if _, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755); err != nil {
|
|
t.Fatalf("ensure: %v", err)
|
|
}
|
|
childZddc := filepath.Join(root, "Proj", "staging", "ACME",
|
|
"2025-10-31_proj-EM-TRN-0042 (RSA) - Outbound", ".zddc")
|
|
if _, err := os.Stat(childZddc); !os.IsNotExist(err) {
|
|
t.Errorf("staging child should NOT have auto-own .zddc; got err=%v", err)
|
|
}
|
|
stagingZddc := filepath.Join(root, "Proj", "staging", "ACME", ".zddc")
|
|
if _, err := os.Stat(stagingZddc); err != nil {
|
|
t.Errorf("staging/<party> auto-own .zddc missing: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCanonicalAncestors_CaseFoldReuse(t *testing.T) {
|
|
root := t.TempDir()
|
|
// Pre-create Archive/ (PascalCase) — case-fold reuse applies to the
|
|
// canonical project-root peer + the received/issued slots.
|
|
if err := os.MkdirAll(filepath.Join(root, "Proj", "Archive", "ACME", "received"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
target := filepath.Join(root, "Proj", "archive", "ACME", "received", "foo.pdf")
|
|
resolved, err := EnsureCanonicalAncestors(root, target, "dc@x.com", 0o755)
|
|
if err != nil {
|
|
t.Fatalf("ensure: %v", err)
|
|
}
|
|
want := filepath.Join(root, "Proj", "Archive", "ACME", "received", "foo.pdf")
|
|
if resolved != want {
|
|
t.Errorf("resolved=%q, want %q", resolved, want)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "archive")); !os.IsNotExist(err) {
|
|
t.Errorf("lowercase sibling should not exist; got err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCanonicalAncestors_PerPartyIncoming(t *testing.T) {
|
|
root := t.TempDir()
|
|
// incoming/ is a top-level peer; its <party> folder auto-owns.
|
|
target := filepath.Join(root, "Proj", "incoming", "ACME", "submission.pdf")
|
|
|
|
if _, err := EnsureCanonicalAncestors(root, target, "rep@acme.com", 0o755); err != nil {
|
|
t.Fatalf("ensure: %v", err)
|
|
}
|
|
autoZ := filepath.Join(root, "Proj", "incoming", "ACME", ".zddc")
|
|
data, err := os.ReadFile(autoZ)
|
|
if err != nil {
|
|
t.Fatalf("auto-own .zddc at incoming/ACME/ not written: %v", err)
|
|
}
|
|
if !strings.Contains(string(data), "rep@acme.com: rwcda") {
|
|
t.Errorf("incoming/ACME auto-own missing rep grant: %s", data)
|
|
}
|
|
if strings.Contains(string(data), "inherit: false") {
|
|
t.Errorf("incoming/ACME auto-own should be UNFENCED; got: %s", data)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCanonicalAncestors_WormFoldersNoAutoOwn(t *testing.T) {
|
|
root := t.TempDir()
|
|
target := filepath.Join(root, "Proj", "archive", "ACME", "issued", "spec.pdf")
|
|
|
|
if _, err := EnsureCanonicalAncestors(root, target, "dc@mycompany.com", 0o755); err != nil {
|
|
t.Fatalf("ensure: %v", err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "archive", "ACME", "issued")); err != nil {
|
|
t.Errorf("issued/ not created: %v", err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "archive", "ACME", "issued", ".zddc")); !os.IsNotExist(err) {
|
|
t.Errorf("issued/ should NOT have auto-own .zddc (WORM); got err=%v", err)
|
|
}
|
|
// archive/ and archive/<party>/ are plain containers — no auto-own.
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "archive", ".zddc")); !os.IsNotExist(err) {
|
|
t.Errorf("archive/ should not have auto-own .zddc; got err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestEnsureCanonicalAncestors_NoPrincipalSkipsAutoOwn(t *testing.T) {
|
|
root := t.TempDir()
|
|
target := filepath.Join(root, "Proj", "working", "ACME", "anon.md")
|
|
|
|
if _, err := EnsureCanonicalAncestors(root, target, "" /* no email */, 0o755); err != nil {
|
|
t.Fatalf("ensure: %v", err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "working", "ACME")); err != nil {
|
|
t.Errorf("working/ACME not created: %v", err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", "working", "ACME", ".zddc")); !os.IsNotExist(err) {
|
|
t.Errorf("auto-own .zddc must not be written without principalEmail; got err=%v", err)
|
|
}
|
|
}
|
|
|
|
// Top-level peers are physical now — a write under <project>/<peer>/<party>/
|
|
// is created normally (no virtual-aggregator rejection).
|
|
func TestEnsureCanonicalAncestors_TopLevelPeersCreated(t *testing.T) {
|
|
root := t.TempDir()
|
|
for _, peer := range []string{"working", "staging", "reviewing", "incoming", "mdl", "rsk", "ssr"} {
|
|
target := filepath.Join(root, "Proj", peer, "ACME", "x.md")
|
|
if _, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755); err != nil {
|
|
t.Errorf("%s: unexpected error: %v", peer, err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "Proj", peer, "ACME")); err != nil {
|
|
t.Errorf("%s: <project>/%s/ACME/ should be created; got err=%v", peer, peer, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnsureCanonicalAncestors_RejectsTraversal(t *testing.T) {
|
|
root := t.TempDir()
|
|
other := t.TempDir()
|
|
target := filepath.Join(other, "evil.md")
|
|
if _, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755); err == nil {
|
|
t.Errorf("expected error for target outside fsRoot")
|
|
}
|
|
}
|