github.com/anchore/syft@v1.38.2/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: "find relationships between packages with variants", 49 s: newSpecifierBuilder(). 50 WithProvides(a /* provides */, "a-resource"). 51 WithRequires(b /* requires */, "a[variant]"). 52 WithProvides(c /* provides */, "c-resource"). 53 WithVariant(a /* provides */, "variant" /* which requires */, "c-resource"). 54 Specifier(), 55 want: map[string][]string{ 56 "b":/* depends on */ {"a"}, 57 "a":/* depends on */ {"c"}, 58 }, 59 }, 60 { 61 name: "deduplicates provider keys", 62 s: newSpecifierBuilder(). 63 WithProvides(a /* provides */, "a-resource", "a-resource", "a-resource"). 64 WithRequires(b /* requires */, "a-resource", "a-resource", "a-resource"). 65 Specifier(), 66 want: map[string][]string{ 67 "b": /* depends on */ {"a"}, 68 // note: we're NOT seeing: 69 // "b": /* depends on */ {"a", "a", "a"}, 70 }, 71 }, 72 { 73 name: "deduplicates crafted relationships", 74 s: newSpecifierBuilder(). 75 WithProvides(a /* provides */, "a1-resource", "a2-resource", "a3-resource"). 76 WithRequires(b /* requires */, "a1-resource", "a2-resource"). 77 Specifier(), 78 want: map[string][]string{ 79 "b": /* depends on */ {"a"}, 80 // note: we're NOT seeing: 81 // "b": /* depends on */ {"a", "a"}, 82 }, 83 }, 84 } 85 for _, tt := range tests { 86 t.Run(tt.name, func(t *testing.T) { 87 relationships := Resolve(tt.s, subjects) 88 if d := cmp.Diff(tt.want, abstractRelationships(t, relationships)); d != "" { 89 t.Errorf("unexpected relationships (-want +got):\n%s", d) 90 } 91 }) 92 } 93 } 94 95 type specifierBuilder struct { 96 provides map[string][]string 97 requires map[string][]string 98 variants map[string]map[string][]string 99 } 100 101 func newSpecifierBuilder() *specifierBuilder { 102 return &specifierBuilder{ 103 provides: make(map[string][]string), 104 requires: make(map[string][]string), 105 variants: make(map[string]map[string][]string), 106 } 107 } 108 109 func (m *specifierBuilder) WithProvides(p pkg.Package, provides ...string) *specifierBuilder { 110 m.provides[p.Name] = append(m.provides[p.Name], provides...) 111 return m 112 } 113 114 func (m *specifierBuilder) WithRequires(p pkg.Package, requires ...string) *specifierBuilder { 115 m.requires[p.Name] = append(m.requires[p.Name], requires...) 116 return m 117 } 118 119 func (m *specifierBuilder) WithVariant(p pkg.Package, variantName string, requires ...string) *specifierBuilder { 120 if _, ok := m.variants[p.Name]; !ok { 121 m.variants[p.Name] = make(map[string][]string) 122 } 123 m.variants[p.Name][variantName] = append(m.variants[p.Name][variantName], requires...) 124 return m 125 } 126 127 func (m specifierBuilder) Specifier() Specifier { 128 return func(p pkg.Package) Specification { 129 var prs []ProvidesRequires 130 for variantName, requires := range m.variants[p.Name] { 131 prs = append(prs, ProvidesRequires{ 132 Provides: []string{p.Name + "[" + variantName + "]"}, 133 Requires: requires, 134 }) 135 } 136 137 return Specification{ 138 ProvidesRequires: ProvidesRequires{ 139 Provides: m.provides[p.Name], 140 Requires: m.requires[p.Name], 141 }, 142 Variants: prs, 143 } 144 } 145 } 146 147 func abstractRelationships(t testing.TB, relationships []artifact.Relationship) map[string][]string { 148 t.Helper() 149 150 abstracted := make(map[string][]string) 151 for _, relationship := range relationships { 152 fromPkg, ok := relationship.From.(pkg.Package) 153 if !ok { 154 continue 155 } 156 toPkg, ok := relationship.To.(pkg.Package) 157 if !ok { 158 continue 159 } 160 161 // we build this backwards since we use DependencyOfRelationship instead of DependsOn 162 abstracted[toPkg.Name] = append(abstracted[toPkg.Name], fromPkg.Name) 163 } 164 165 return abstracted 166 } 167 168 func Test_Processor(t *testing.T) { 169 a := pkg.Package{ 170 Name: "a", 171 } 172 a.SetID() 173 174 b := pkg.Package{ 175 Name: "b", 176 } 177 b.SetID() 178 179 c := pkg.Package{ 180 Name: "c", 181 } 182 c.SetID() 183 184 tests := []struct { 185 name string 186 sp Specifier 187 pkgs []pkg.Package 188 rels []artifact.Relationship 189 err error 190 wantPkgCount int 191 wantRelCount int 192 wantErr assert.ErrorAssertionFunc 193 }{ 194 { 195 name: "happy path preserves decorated values", 196 sp: newSpecifierBuilder(). 197 WithProvides(b, "b-resource"). 198 WithRequires(c, "b-resource"). 199 Specifier(), 200 pkgs: []pkg.Package{a, b, c}, 201 rels: []artifact.Relationship{ 202 { 203 From: a, 204 To: b, 205 Type: artifact.DependencyOfRelationship, 206 }, 207 }, 208 209 wantPkgCount: 3, 210 wantRelCount: 2, // original + new 211 }, 212 { 213 name: "error from cataloger is propagated", 214 sp: newSpecifierBuilder(). 215 WithProvides(b, "b-resource"). 216 WithRequires(c, "b-resource"). 217 Specifier(), 218 err: errors.New("surprise!"), 219 pkgs: []pkg.Package{a, b, c}, 220 rels: []artifact.Relationship{ 221 { 222 From: a, 223 To: b, 224 Type: artifact.DependencyOfRelationship, 225 }, 226 }, 227 wantPkgCount: 3, 228 wantRelCount: 2, // original + new 229 wantErr: assert.Error, 230 }, 231 } 232 for _, tt := range tests { 233 t.Run(tt.name, func(t *testing.T) { 234 if tt.wantErr == nil { 235 tt.wantErr = assert.NoError 236 } 237 238 gotPkgs, gotRels, err := Processor(tt.sp)(tt.pkgs, tt.rels, tt.err) 239 240 tt.wantErr(t, err) 241 assert.Len(t, gotPkgs, tt.wantPkgCount) 242 assert.Len(t, gotRels, tt.wantRelCount) 243 }) 244 } 245 }