package engine import ( "reflect" "testing" ) func TestSecretDependencyClosureMatchesFixedPointScan(t *testing.T) { tests := []struct { name string paths map[string]Path seed map[string]bool dependencies []secretDependency inherited []secretDependency wantSensitivity map[string]bool }{ { name: "forward reverse chains and cycles", paths: secretPaths(Keys("input"), Keys("derived"), Keys("output"), Keys("cycle")), seed: secretSet(Keys("input")), dependencies: []secretDependency{ {dependency: Keys("input"), target: Keys("derived")}, {dependency: Keys("derived"), target: Keys("output")}, {dependency: Keys("output"), target: Keys("cycle")}, {dependency: Keys("cycle"), target: Keys("derived")}, {dependency: Keys("input"), target: Keys("derived")}, }, }, { name: "ordinary ancestor descendant and root relations", paths: secretPaths(Path{}, Keys("branch", "leaf"), Keys("parent", "child")), seed: secretSet(Keys("branch", "leaf"), Keys("parent")), dependencies: []secretDependency{ {dependency: Keys("branch"), target: Keys("from-descendant")}, {dependency: Keys("parent", "child"), target: Keys("from-ancestor")}, {dependency: Path{}, target: Keys("from-root")}, }, }, { name: "unindexed seed only activates inherited relations", paths: secretPaths(Keys("ordinary"), Keys("copy")), seed: secretSet(Keys("untracked")), dependencies: []secretDependency{ {dependency: Keys("untracked"), target: Keys("ordinary")}, }, inherited: []secretDependency{ {dependency: Keys("untracked"), target: Keys("copy")}, }, wantSensitivity: map[string]bool{"/copy": true, "/ordinary": false}, }, { name: "inheritance direction root escapes and numeric spellings", paths: secretPaths(Path{}, Keys("a/b", "01"), Keys("source", "value")), seed: secretSet(Path{}), dependencies: []secretDependency{ {dependency: Keys("a/b", "01"), target: Keys("ordinary-copy")}, }, inherited: []secretDependency{ {dependency: Keys("a/b", "01"), target: Keys("destination", "01")}, {dependency: Keys("source"), target: Keys("destination")}, }, }, { name: "inherited links do not trigger from classified descendants", paths: secretPaths(Keys("parent", "secret", "nested")), seed: secretSet(Keys("parent", "secret", "nested")), inherited: []secretDependency{ {dependency: Keys("parent", "secret"), target: Keys("child", "secret")}, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { want := referenceSecretDependencyClosure(test.paths, test.seed, test.dependencies, test.inherited) got := closeSecretDependencies(test.paths, test.seed, test.dependencies, test.inherited) if !reflect.DeepEqual(got, want) { t.Fatalf("closure = %#v, want %#v", got, want) } for pointer, sensitive := range test.wantSensitivity { if got[pointer] != sensitive { t.Errorf("sensitivity at %q = %v, want %v", pointer, got[pointer], sensitive) } } }) } } func referenceSecretDependencyClosure(paths map[string]Path, seed map[string]bool, dependencies, inherited []secretDependency) map[string]bool { secret := make(map[string]bool, len(seed)) for pointer, classified := range seed { if classified { secret[pointer] = true } } for { changed := false for _, dependency := range dependencies { if referenceSecretIntersectsPath(paths, secret, dependency.dependency) && !secret[dependency.target.String()] { secret[dependency.target.String()] = true changed = true } } for _, link := range inherited { if pathIsSecret(secret, link.dependency) && !secret[link.target.String()] { secret[link.target.String()] = true changed = true } if pathIsSecret(secret, link.target) && !secret[link.dependency.String()] { secret[link.dependency.String()] = true changed = true } } if !changed { return secret } } } func referenceSecretIntersectsPath(paths map[string]Path, secret map[string]bool, candidate Path) bool { for pointer := range secret { classified, ok := paths[pointer] if !ok { continue } if pathContains(classified, candidate) || pathContains(candidate, classified) { return true } } return false } func secretPaths(paths ...Path) map[string]Path { result := make(map[string]Path, len(paths)) for _, path := range paths { result[path.String()] = path } return result } func secretSet(paths ...Path) map[string]bool { result := make(map[string]bool, len(paths)) for _, path := range paths { result[path.String()] = true } return result }