Files
puppet-catalog-tools/internal/impact/index_test.go
T
unkin-agent 1c1df7f953 strip source quote-aware and resolve dynamic class references
- strip # comments with quote-state tracking so a hash in a string no
  longer truncates the line
- skip heredoc bodies in every @(EOT) form so documentation text is not
  indexed as a class reference
- take every literal name out of a selector, following a bare variable
  through the assignments in its own file
- record a class reference that stays unresolved against its file, with
  any literal prefix, instead of dropping it
- read a class name case insensitively in a title or type reference
- tag each edge as a declaration or an ordering reference
2026-09-12 17:18:56 +10:00

145 lines
3.6 KiB
Go

package impact
import (
"reflect"
"sort"
"testing"
)
// refs runs a manifest body through the same stripping addManifest does, then
// extracts the class names it resolves.
func refs(body string) []string {
names, _ := scanReferences(stripSource(body))
out := make([]string, 0, len(names))
for name := range names {
out = append(out, name)
}
sort.Strings(out)
return out
}
// unresolvedRefs returns the references the scan could not reduce to names.
func unresolvedRefs(body string) []UnresolvedRef {
_, out := scanReferences(stripSource(body))
return out
}
func TestReferencesReadsWholeArgumentList(t *testing.T) {
tests := []struct {
name string
body string
want []string
}{
{
name: "bare list",
body: "class p {\n include foo, bar, baz\n}\n",
want: []string{"bar", "baz", "foo"},
},
{
name: "top scope names",
body: "class p {\n include ::foo::bar, ::baz\n}\n",
want: []string{"baz", "foo::bar"},
},
{
name: "require function",
body: "class p {\n require foo, bar\n}\n",
want: []string{"bar", "foo"},
},
{
name: "contain",
body: "class p {\n contain foo, bar\n}\n",
want: []string{"bar", "foo"},
},
{
name: "parenthesised",
body: "class p {\n include(foo, bar)\n}\n",
want: []string{"bar", "foo"},
},
{
name: "quoted names",
body: "class p {\n include 'foo::bar', \"baz::qux\"\n}\n",
want: []string{"baz::qux", "foo::bar"},
},
{
name: "multi line with trailing comma",
body: "class p {\n include(\n foo::a,\n foo::b,\n )\n}\n",
want: []string{"foo::a", "foo::b"},
},
{
name: "multi line statement form",
body: "class p {\n include foo::a,\n foo::b\n}\n",
want: []string{"foo::a", "foo::b"},
},
{
name: "array argument",
body: "class p {\n include [foo, bar]\n}\n",
want: []string{"bar", "foo"},
},
{
name: "class resource title list",
body: "class p {\n class { ['foo', 'bar']: }\n}\n",
want: []string{"bar", "foo"},
},
{
name: "class reference title list",
body: "class p {\n Class['foo'] -> Class['bar', 'baz']\n}\n",
want: []string{"bar", "baz", "foo"},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := refs(tc.body); !reflect.DeepEqual(got, tc.want) {
t.Errorf("references = %v, want %v", got, tc.want)
}
})
}
}
func TestReferencesStopsAtStatementEnd(t *testing.T) {
tests := []struct {
name string
body string
want []string
}{
{
name: "next statement is not swallowed",
body: "class p {\n include foo\n notify { 'x': }\n include bar\n}\n",
want: []string{"bar", "foo"},
},
{
name: "trailing comment is not a name",
body: "class p {\n include foo, bar # baz, qux\n}\n",
want: []string{"bar", "foo"},
},
{
name: "block close ends the list",
body: "class p {\n if $x { include foo }\n include bar\n}\n",
want: []string{"bar", "foo"},
},
{
name: "require attribute is not a call",
body: "class p {\n service { 'x':\n require => Class['foo'],\n }\n include bar\n}\n",
want: []string{"bar", "foo"},
},
{
name: "semicolon ends the list",
body: "class p {\n include foo; include bar\n}\n",
want: []string{"bar", "foo"},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := refs(tc.body); !reflect.DeepEqual(got, tc.want) {
t.Errorf("references = %v, want %v", got, tc.want)
}
})
}
}
func TestReferencesInheritsTakesOneName(t *testing.T) {
// inherits is not a function and never takes a list.
if got, want := refs("class p inherits foo::bar {\n}\n"), []string{"foo::bar"}; !reflect.DeepEqual(got, want) {
t.Errorf("references = %v, want %v", got, want)
}
}