diff --git a/internal/catalogue/placed_paths_test.go b/internal/catalogue/placed_paths_test.go index 2852546..779c151 100644 --- a/internal/catalogue/placed_paths_test.go +++ b/internal/catalogue/placed_paths_test.go @@ -60,6 +60,11 @@ func TestPlacedDirectoriesKeepTheirPaths(t *testing.T) { // (issue 119's conversion), and what must not move is the path a machine sees. was = resolvedTree(was, dirsFor(earlier, Rendering{}), module, t) resolved := resolvedTree(is, dirsFor(m, Rendering{}), module, t) + // An access named by id resolves to the path the definition still carries as its default + // (issue 153) — the same rule as a placed directory: an assignment that says nothing moves + // nothing. + was = accessesResolved(was, earlier) + resolved = accessesResolved(resolved, m) if !reflect.DeepEqual(was, resolved) { wasJSON, _ := json.MarshalIndent(was, "", " ") isJSON, _ := json.MarshalIndent(resolved, "", " ") @@ -128,3 +133,48 @@ func firstDifference(a, b string) string { } return "(the difference is beyond the shorter document)" } + +// accessesResolved fills every ${access:} with the default path the definition carries for that +// access, and drops the id, so a manifest that names its accesses is compared by the paths a machine +// with no placement receives. +func accessesResolved(node any, m Manifest) any { + defaults := map[string]string{} + for _, a := range m.Accesses { + if a.ID != "" && a.Path != "" { + defaults[a.ID] = a.Path + } + } + var walk func(any) any + walk = func(n any) any { + switch v := n.(type) { + case map[string]any: + out := map[string]any{} + for k, child := range v { + if k == "id" { + if _, isAccess := v["mode"]; isAccess && v["type"] == nil { + if _, hasPath := v["path"]; hasPath { + continue + } + } + } + out[k] = walk(child) + } + return out + case []any: + out := make([]any, len(v)) + for i, child := range v { + out[i] = walk(child) + } + return out + case string: + return accessRef.ReplaceAllStringFunc(v, func(ref string) string { + if p, ok := defaults[accessRef.FindStringSubmatch(ref)[1]]; ok { + return p + } + return ref + }) + } + return n + } + return walk(node) +}