package taskloop import ( "crypto/sha256" "encoding/hex" "os" "path/filepath" "strings" "testing" ) func TestParityEmbeddedManifestIsCompleteAndCurrent(t *testing.T) { manifest, err := LoadEmbeddedParityManifest() if err != nil { t.Fatal(err) } if err := ValidateParityManifest(repoRoot(t), manifest); err != nil { t.Fatal(err) } } func TestParityManifestRejectsMissingDuplicateUnclassifiedAndStaleEvidence(t *testing.T) { root := parityFixtureRoot(t) manifest := validParityFixture(t, root) cases := []struct { name string mutate func(*ParityManifest) want string }{ {"missing disposal", func(m *ParityManifest) { m.Disposal.ReferenceFiles = m.Disposal.ReferenceFiles[:3] }, "disposal inventory is missing classified reference"}, {"duplicate behavior", func(m *ParityManifest) { m.Behaviors = append(m.Behaviors, m.Behaviors[0]) }, "duplicate behavior"}, {"unclassified", func(m *ParityManifest) { m.Behaviors[0].Disposition = "" }, "unclassified disposition"}, {"stale source", func(m *ParityManifest) { m.Behaviors[0].GoSource[0] = "missing.go" }, "stale path"}, {"python caller", func(m *ParityManifest) { m.Behaviors[0].ProductionCaller = "python" }, "lacks Go ownership evidence"}, {"hash drift", func(m *ParityManifest) { m.Disposal.ReferenceFiles[0].SHA256 = strings.Repeat("0", 64) }, "checksum drift"}, {"unknown disposal state", func(m *ParityManifest) { m.Disposal.State = "transitioning" }, "unknown disposal state"}, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { copy := manifest copy.Behaviors = append([]ParityBehavior(nil), manifest.Behaviors...) copy.Disposal.ReferenceFiles = append([]ParityReferenceFile(nil), manifest.Disposal.ReferenceFiles...) for i := range copy.Behaviors { copy.Behaviors[i].GoSource = append([]string(nil), copy.Behaviors[i].GoSource...) } tt.mutate(©) if err := ValidateParityManifest(root, copy); err == nil || !strings.Contains(err.Error(), tt.want) { t.Fatalf("ValidateParityManifest error = %v, want %q", err, tt.want) } }) } } func TestDispositionReportIsStable(t *testing.T) { manifest, err := LoadEmbeddedParityManifest() if err != nil { t.Fatal(err) } report := FormatParityReport(manifest) for _, want := range []string{"parity: ok", "behaviors: 13", "disposal_state: retained", "disposal_files: 9", "milestone-completion-transition"} { if !strings.Contains(report, want) { t.Fatalf("report %q does not contain %q", report, want) } } } func parityFixtureRoot(t *testing.T) string { t.Helper() root := t.TempDir() for _, path := range append(append([]string{}, parityFixtureReferenceFiles...), "owner.go") { fullPath := filepath.Join(root, filepath.FromSlash(path)) if err := os.MkdirAll(filepath.Dir(fullPath), 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(fullPath, []byte(path), 0600); err != nil { t.Fatal(err) } } return root } func validParityFixture(t *testing.T, root string) ParityManifest { t.Helper() manifest := ParityManifest{SchemaVersion: paritySchemaVersion, ReferenceRoot: "references", DeletionGate: "completion"} for index, path := range parityFixtureReferenceFiles { contents, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(path))) if err != nil { t.Fatal(err) } manifest.Behaviors = append(manifest.Behaviors, ParityBehavior{ ID: "behavior-" + string(rune('a'+index)), ReferenceSources: []string{path}, Disposition: "replace", GoOwner: "Go", GoSource: []string{"owner.go"}, GoTest: []string{"TestOwner"}, Invariants: "bounded", ProductionCaller: "go-only", }) manifest.Disposal.ReferenceFiles = append(manifest.Disposal.ReferenceFiles, ParityReferenceFile{ Path: path, SHA256: sha256Hex(contents), Disposition: "replace", CoveredBy: []string{"behavior-" + string(rune('a'+index))}, }) } manifest.Disposal.State = "retained" manifest.Disposal.Procedure = "retain" return manifest } var parityFixtureReferenceFiles = []string{ "references/dispatch.py", "references/observer.py", "references/tests/test_dispatch.py", "references/tests/test_dispatch.py.orig", } func TestParityManifestRejectsUnrecordedRetainedFixture(t *testing.T) { root := parityFixtureRoot(t) manifest := validParityFixture(t, root) extra := filepath.Join(root, "references", "tests", "extra.py") if err := os.WriteFile(extra, []byte("fixture\n"), 0600); err != nil { t.Fatal(err) } if err := ValidateParityManifest(root, manifest); err == nil || !strings.Contains(err.Error(), "unrecorded retained fixture") { t.Fatalf("ValidateParityManifest error = %v, want unrecorded retained fixture", err) } } func TestParityManifestDisposalLifecycle(t *testing.T) { root := parityFixtureRoot(t) manifest := validParityFixture(t, root) if err := ValidateParityManifest(root, manifest); err != nil { t.Fatal(err) } for _, reference := range manifest.Disposal.ReferenceFiles { if err := os.Remove(filepath.Join(root, filepath.FromSlash(reference.Path))); err != nil { t.Fatal(err) } } manifest.Disposal.State = "disposed" if err := ValidateParityManifest(root, manifest); err != nil { t.Fatalf("disposed manifest: %v", err) } root = parityFixtureRoot(t) manifest = validParityFixture(t, root) manifest.Disposal.State = "disposed" if err := ValidateParityManifest(root, manifest); err == nil || !strings.Contains(err.Error(), "retained fixture") { t.Fatalf("disposed manifest with survivors error = %v", err) } root = parityFixtureRoot(t) manifest = validParityFixture(t, root) if err := os.Remove(filepath.Join(root, filepath.FromSlash(manifest.Disposal.ReferenceFiles[0].Path))); err != nil { t.Fatal(err) } if err := ValidateParityManifest(root, manifest); err == nil || !strings.Contains(err.Error(), "missing fixture") { t.Fatalf("retained manifest with missing fixture error = %v", err) } } func TestParityManifestDisposedRejectsMissingHistoricalInventory(t *testing.T) { root := parityFixtureRoot(t) manifest := validParityFixture(t, root) for _, reference := range manifest.Disposal.ReferenceFiles { if err := os.Remove(filepath.Join(root, filepath.FromSlash(reference.Path))); err != nil { t.Fatal(err) } } manifest.Disposal.State = "disposed" if err := ValidateParityManifest(root, manifest); err != nil { t.Fatalf("complete disposed manifest: %v", err) } manifest.Disposal.ReferenceFiles = manifest.Disposal.ReferenceFiles[:len(manifest.Disposal.ReferenceFiles)-1] if err := ValidateParityManifest(root, manifest); err == nil || !strings.Contains(err.Error(), "disposal inventory is missing classified reference") { t.Fatalf("disposed manifest with truncated history error = %v", err) } } func sha256Hex(contents []byte) string { digest := sha256.Sum256(contents) return hex.EncodeToString(digest[:]) } func repoRoot(t *testing.T) string { t.Helper() directory, err := os.Getwd() if err != nil { t.Fatal(err) } for { if _, err := os.Stat(filepath.Join(directory, "go.mod")); err == nil { return directory } parent := filepath.Dir(directory) if parent == directory { t.Fatal("repository root not found") } directory = parent } }