github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/internal/dependency/resolver_test.go (about) 1 package dependency 2 3 import ( 4 "errors" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/stretchr/testify/assert" 9 10 "github.com/anchore/syft/syft/artifact" 11 "github.com/anchore/syft/syft/pkg" 12 ) 13 14 func Test_resolve(t *testing.T) { 15 a := pkg.Package{ 16 Name: "a", 17 } 18 a.SetID() 19 20 b := pkg.Package{ 21 Name: "b", 22 } 23 b.SetID() 24 25 c := pkg.Package{ 26 Name: "c", 27 } 28 c.SetID() 29 30 subjects := []pkg.Package{a, b, c} 31 32 tests := []struct { 33 name string 34 s Specifier 35 want map[string][]string 36 }{ 37 { 38 name: "find relationships between packages", 39 s: newSpecifierBuilder(). 40 WithProvides(a /* provides */, "a-resource"). 41 WithRequires(b /* requires */, "a-resource"). 42 Specifier(), 43 want: map[string][]string{ 44 "b": /* depends on */ {"a"}, 45 }, 46 }, 47 { 48 name: "deduplicates provider keys", 49 s: newSpecifierBuilder(). 50 WithProvides(a /* provides */, "a-resource", "a-resource", "a-resource"). 51 WithRequires(b /* requires */, "a-resource", "a-resource", "a-resource"). 52 Specifier(), 53 want: map[string][]string{ 54 "b": /* depends on */ {"a"}, 55 // note: we're NOT seeing: 56 // "b": /* depends on */ {"a", "a", "a"}, 57 }, 58 }, 59 { 60 name: "deduplicates crafted relationships", 61 s: newSpecifierBuilder(). 62 WithProvides(a /* provides */, "a1-resource", "a2-resource", "a3-resource"). 63 WithRequires(b /* requires */, "a1-resource", "a2-resource"). 64 Specifier(), 65 want: map[string][]string{ 66 "b": /* depends on */ {"a"}, 67 // note: we're NOT seeing: 68 // "b": /* depends on */ {"a", "a"}, 69 }, 70 }, 71 } 72 for _, tt := range tests { 73 t.Run(tt.name, func(t *testing.T) { 74 relationships := resolve(tt.s, subjects) 75 if d := cmp.Diff(tt.want, abstractRelationships(t, relationships)); d != "" { 76 t.Errorf("unexpected relationships (-want +got):\n%s", d) 77 } 78 }) 79 } 80 } 81 82 type specifierBuilder struct { 83 provides map[string][]string 84 requires map[string][]string 85 } 86 87 func newSpecifierBuilder() *specifierBuilder { 88 return &specifierBuilder{ 89 provides: make(map[string][]string), 90 requires: make(map[string][]string), 91 } 92 } 93 94 func (m *specifierBuilder) WithProvides(p pkg.Package, provides ...string) *specifierBuilder { 95 m.provides[p.Name] = append(m.provides[p.Name], provides...) 96 return m 97 } 98 99 func (m *specifierBuilder) WithRequires(p pkg.Package, requires ...string) *specifierBuilder { 100 m.requires[p.Name] = append(m.requires[p.Name], requires...) 101 return m 102 } 103 104 func (m specifierBuilder) Specifier() Specifier { 105 return func(p pkg.Package) Specification { 106 return Specification{ 107 Provides: m.provides[p.Name], 108 Requires: m.requires[p.Name], 109 } 110 } 111 } 112 113 func abstractRelationships(t testing.TB, relationships []artifact.Relationship) map[string][]string { 114 t.Helper() 115 116 abstracted := make(map[string][]string) 117 for _, relationship := range relationships { 118 fromPkg, ok := relationship.From.(pkg.Package) 119 if !ok { 120 continue 121 } 122 toPkg, ok := relationship.To.(pkg.Package) 123 if !ok { 124 continue 125 } 126 127 // we build this backwards since we use DependencyOfRelationship instead of DependsOn 128 abstracted[toPkg.Name] = append(abstracted[toPkg.Name], fromPkg.Name) 129 } 130 131 return abstracted 132 } 133 134 func Test_Processor(t *testing.T) { 135 a := pkg.Package{ 136 Name: "a", 137 } 138 a.SetID() 139 140 b := pkg.Package{ 141 Name: "b", 142 } 143 b.SetID() 144 145 c := pkg.Package{ 146 Name: "c", 147 } 148 c.SetID() 149 150 tests := []struct { 151 name string 152 sp Specifier 153 pkgs []pkg.Package 154 rels []artifact.Relationship 155 err error 156 wantPkgCount int 157 wantRelCount int 158 wantErr assert.ErrorAssertionFunc 159 }{ 160 { 161 name: "happy path preserves decorated values", 162 sp: newSpecifierBuilder(). 163 WithProvides(b, "b-resource"). 164 WithRequires(c, "b-resource"). 165 Specifier(), 166 pkgs: []pkg.Package{a, b, c}, 167 rels: []artifact.Relationship{ 168 { 169 From: a, 170 To: b, 171 Type: artifact.DependencyOfRelationship, 172 }, 173 }, 174 175 wantPkgCount: 3, 176 wantRelCount: 2, // original + new 177 }, 178 { 179 name: "error from cataloger is propagated", 180 sp: newSpecifierBuilder(). 181 WithProvides(b, "b-resource"). 182 WithRequires(c, "b-resource"). 183 Specifier(), 184 err: errors.New("surprise!"), 185 pkgs: []pkg.Package{a, b, c}, 186 rels: []artifact.Relationship{ 187 { 188 From: a, 189 To: b, 190 Type: artifact.DependencyOfRelationship, 191 }, 192 }, 193 wantPkgCount: 3, 194 wantRelCount: 2, // original + new 195 wantErr: assert.Error, 196 }, 197 } 198 for _, tt := range tests { 199 t.Run(tt.name, func(t *testing.T) { 200 if tt.wantErr == nil { 201 tt.wantErr = assert.NoError 202 } 203 204 gotPkgs, gotRels, err := Processor(tt.sp)(tt.pkgs, tt.rels, tt.err) 205 206 tt.wantErr(t, err) 207 assert.Len(t, gotPkgs, tt.wantPkgCount) 208 assert.Len(t, gotRels, tt.wantRelCount) 209 }) 210 } 211 }