github.com/anchore/syft@v1.38.2/internal/relationship/index_test.go (about)

     1  package relationship
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/anchore/syft/syft/artifact"
    10  	"github.com/anchore/syft/syft/file"
    11  	"github.com/anchore/syft/syft/pkg"
    12  )
    13  
    14  func Test_Index(t *testing.T) {
    15  	p1 := pkg.Package{
    16  		Name: "pkg-1",
    17  	}
    18  	p2 := pkg.Package{
    19  		Name: "pkg-2",
    20  	}
    21  	p3 := pkg.Package{
    22  		Name: "pkg-3",
    23  	}
    24  	c1 := file.Coordinates{
    25  		RealPath: "/coords/1",
    26  	}
    27  	c2 := file.Coordinates{
    28  		RealPath: "/coords/2",
    29  	}
    30  
    31  	for _, p := range []*pkg.Package{&p1, &p2, &p3} {
    32  		p.SetID()
    33  	}
    34  
    35  	r1 := artifact.Relationship{
    36  		From: p1,
    37  		To:   p2,
    38  		Type: artifact.DependencyOfRelationship,
    39  	}
    40  	r2 := artifact.Relationship{
    41  		From: p1,
    42  		To:   p3,
    43  		Type: artifact.DependencyOfRelationship,
    44  	}
    45  	r3 := artifact.Relationship{
    46  		From: p1,
    47  		To:   c1,
    48  		Type: artifact.ContainsRelationship,
    49  	}
    50  	r4 := artifact.Relationship{
    51  		From: p2,
    52  		To:   c2,
    53  		Type: artifact.ContainsRelationship,
    54  	}
    55  	r5 := artifact.Relationship{
    56  		From: p3,
    57  		To:   c2,
    58  		Type: artifact.ContainsRelationship,
    59  	}
    60  
    61  	dup := artifact.Relationship{
    62  		From: p3,
    63  		To:   c2,
    64  		Type: artifact.ContainsRelationship,
    65  	}
    66  
    67  	idx := NewIndex(r1, r2, r3, r4, r5, dup)
    68  	require.ElementsMatch(t, slice(r1, r2, r3, r4, r5), idx.All())
    69  
    70  	require.ElementsMatch(t, slice(r1, r4), idx.References(p2))
    71  	require.ElementsMatch(t, slice(r4), idx.References(p2, artifact.ContainsRelationship))
    72  
    73  	require.ElementsMatch(t, slice(r1), idx.To(p2))
    74  	require.ElementsMatch(t, []artifact.Relationship(nil), idx.To(p2, artifact.ContainsRelationship))
    75  
    76  	require.ElementsMatch(t, slice(r4), idx.From(p2))
    77  	require.ElementsMatch(t, slice(r4), idx.From(p2, artifact.ContainsRelationship))
    78  }
    79  
    80  func Test_sortOrder(t *testing.T) {
    81  	r1 := artifact.Relationship{
    82  		From: id("1"),
    83  		To:   id("2"),
    84  		Type: "1",
    85  	}
    86  	r2 := artifact.Relationship{
    87  		From: id("2"),
    88  		To:   id("3"),
    89  		Type: "1",
    90  	}
    91  	r3 := artifact.Relationship{
    92  		From: id("3"),
    93  		To:   id("4"),
    94  		Type: "1",
    95  	}
    96  	r4 := artifact.Relationship{
    97  		From: id("1"),
    98  		To:   id("2"),
    99  		Type: "2",
   100  	}
   101  	r5 := artifact.Relationship{
   102  		From: id("2"),
   103  		To:   id("3"),
   104  		Type: "2",
   105  	}
   106  	dup := artifact.Relationship{
   107  		From: id("2"),
   108  		To:   id("3"),
   109  		Type: "2",
   110  	}
   111  	r6 := artifact.Relationship{
   112  		From: id("2"),
   113  		To:   id("3"),
   114  		Type: "3",
   115  	}
   116  
   117  	idx := NewIndex(r5, r2, r6, r4, r1, r3, dup)
   118  	require.EqualValues(t, slice(r1, r2, r3, r4, r5, r6), idx.All())
   119  
   120  	require.EqualValues(t, slice(r1, r4), idx.From(id("1")))
   121  
   122  	require.EqualValues(t, slice(r2, r5, r6), idx.To(id("3")))
   123  
   124  	rLast := artifact.Relationship{
   125  		From: id("0"),
   126  		To:   id("3"),
   127  		Type: "9999",
   128  	}
   129  
   130  	rFirst := artifact.Relationship{
   131  		From: id("0"),
   132  		To:   id("3"),
   133  		Type: "1",
   134  	}
   135  
   136  	rMid := artifact.Relationship{
   137  		From: id("0"),
   138  		To:   id("1"),
   139  		Type: "2",
   140  	}
   141  
   142  	idx.Add(rLast, rFirst, rMid)
   143  
   144  	require.EqualValues(t, slice(rFirst, r1, r2, r3, rMid, r4, r5, r6, rLast), idx.All())
   145  
   146  	require.EqualValues(t, slice(rFirst, r2, r5, r6, rLast), idx.To(id("3")))
   147  }
   148  
   149  func Test_Coordinates(t *testing.T) {
   150  	p1 := pkg.Package{
   151  		Name: "pkg-1",
   152  	}
   153  	p2 := pkg.Package{
   154  		Name: "pkg-2",
   155  	}
   156  	p3 := pkg.Package{
   157  		Name: "pkg-3",
   158  	}
   159  	c1 := file.Coordinates{
   160  		RealPath: "/coords/1",
   161  	}
   162  	c2 := file.Coordinates{
   163  		RealPath: "/coords/2",
   164  	}
   165  	c3 := file.Coordinates{
   166  		RealPath: "/coords/3",
   167  	}
   168  	c4 := file.Coordinates{
   169  		RealPath: "/coords/4",
   170  	}
   171  
   172  	for _, p := range []*pkg.Package{&p1, &p2, &p3} {
   173  		p.SetID()
   174  	}
   175  
   176  	r1 := artifact.Relationship{
   177  		From: p1,
   178  		To:   p2,
   179  		Type: artifact.DependencyOfRelationship,
   180  	}
   181  	r2 := artifact.Relationship{
   182  		From: p1,
   183  		To:   p3,
   184  		Type: artifact.DependencyOfRelationship,
   185  	}
   186  	r3 := artifact.Relationship{
   187  		From: p1,
   188  		To:   c1,
   189  		Type: artifact.ContainsRelationship,
   190  	}
   191  	r4 := artifact.Relationship{
   192  		From: p2,
   193  		To:   c2,
   194  		Type: artifact.ContainsRelationship,
   195  	}
   196  	r5 := artifact.Relationship{
   197  		From: p3,
   198  		To:   c1,
   199  		Type: artifact.ContainsRelationship,
   200  	}
   201  	r6 := artifact.Relationship{
   202  		From: p3,
   203  		To:   c2,
   204  		Type: artifact.ContainsRelationship,
   205  	}
   206  	r7 := artifact.Relationship{
   207  		From: c1,
   208  		To:   c3,
   209  		Type: artifact.ContainsRelationship,
   210  	}
   211  	r8 := artifact.Relationship{
   212  		From: c3,
   213  		To:   c4,
   214  		Type: artifact.ContainsRelationship,
   215  	}
   216  
   217  	idx := NewIndex(r1, r2, r3, r4, r5, r6, r7, r8)
   218  
   219  	got := idx.Coordinates(p1)
   220  	require.ElementsMatch(t, slice(c1), got)
   221  
   222  	got = idx.Coordinates(p3)
   223  	require.ElementsMatch(t, slice(c1, c2), got)
   224  }
   225  
   226  type id string
   227  
   228  func (i id) ID() artifact.ID {
   229  	return artifact.ID(i)
   230  }
   231  
   232  func slice[T any](values ...T) []T {
   233  	return values
   234  }
   235  
   236  func TestRemove(t *testing.T) {
   237  	p1 := pkg.Package{Name: "pkg-1"}
   238  	p2 := pkg.Package{Name: "pkg-2"}
   239  	p3 := pkg.Package{Name: "pkg-3"}
   240  	c1 := file.Coordinates{RealPath: "/coords/1"}
   241  	c2 := file.Coordinates{RealPath: "/coords/2"}
   242  	c3 := file.Coordinates{RealPath: "/coords/3"}
   243  	c4 := file.Coordinates{RealPath: "/coords/4"}
   244  
   245  	for _, p := range []*pkg.Package{&p1, &p2, &p3} {
   246  		p.SetID()
   247  	}
   248  
   249  	r1 := artifact.Relationship{
   250  		From: p1,
   251  		To:   p2,
   252  		Type: artifact.DependencyOfRelationship,
   253  	}
   254  	r2 := artifact.Relationship{
   255  		From: p1,
   256  		To:   p3,
   257  		Type: artifact.DependencyOfRelationship,
   258  	}
   259  	r3 := artifact.Relationship{
   260  		From: p1,
   261  		To:   c1,
   262  		Type: artifact.ContainsRelationship,
   263  	}
   264  	r4 := artifact.Relationship{
   265  		From: p2,
   266  		To:   c2,
   267  		Type: artifact.ContainsRelationship,
   268  	}
   269  	r5 := artifact.Relationship{
   270  		From: p3,
   271  		To:   c1,
   272  		Type: artifact.ContainsRelationship,
   273  	}
   274  	r6 := artifact.Relationship{
   275  		From: p3,
   276  		To:   c2,
   277  		Type: artifact.ContainsRelationship,
   278  	}
   279  	r7 := artifact.Relationship{
   280  		From: c1,
   281  		To:   c3,
   282  		Type: artifact.ContainsRelationship,
   283  	}
   284  	r8 := artifact.Relationship{
   285  		From: c3,
   286  		To:   c4,
   287  		Type: artifact.ContainsRelationship,
   288  	}
   289  
   290  	index := NewIndex(r1, r2, r3, r4, r5, r6, r7, r8)
   291  
   292  	assert.Equal(t, 8, len(index.All()))
   293  
   294  	// removal of p1 should remove r1, r2, and r3
   295  	index.Remove(p1.ID())
   296  	remaining := index.All()
   297  
   298  	assert.Equal(t, 5, len(remaining))
   299  	assert.NotContains(t, remaining, r1)
   300  	assert.NotContains(t, remaining, r2)
   301  	assert.NotContains(t, remaining, r3)
   302  
   303  	assert.Empty(t, index.From(p1))
   304  	assert.Empty(t, index.To(p1))
   305  
   306  	// removal of c1 should remove r5 and r7
   307  	index.Remove(c1.ID())
   308  	remaining = index.All()
   309  
   310  	// r8 remains since c3->c4 should still exist
   311  	assert.Equal(t, 3, len(remaining))
   312  	assert.NotContains(t, remaining, r5)
   313  	assert.NotContains(t, remaining, r7)
   314  	assert.Contains(t, remaining, r8)
   315  
   316  	assert.Empty(t, index.From(c1))
   317  	assert.Empty(t, index.To(c1))
   318  
   319  	// removal of c3 should remove r8
   320  	index.Remove(c3.ID())
   321  	remaining = index.All()
   322  
   323  	assert.Equal(t, 2, len(remaining))
   324  	assert.Contains(t, remaining, r4)
   325  	assert.Contains(t, remaining, r6)
   326  
   327  	assert.Empty(t, index.From(c3))
   328  	assert.Empty(t, index.To(c3))
   329  }
   330  
   331  func TestReplace(t *testing.T) {
   332  	p1 := pkg.Package{Name: "pkg-1"}
   333  	p2 := pkg.Package{Name: "pkg-2"}
   334  	p3 := pkg.Package{Name: "pkg-3"}
   335  	p4 := pkg.Package{Name: "pkg-4"}
   336  
   337  	for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
   338  		p.SetID()
   339  	}
   340  
   341  	r1 := artifact.Relationship{
   342  		From: p1,
   343  		To:   p2,
   344  		Type: artifact.DependencyOfRelationship,
   345  	}
   346  	r2 := artifact.Relationship{
   347  		From: p3,
   348  		To:   p1,
   349  		Type: artifact.DependencyOfRelationship,
   350  	}
   351  	r3 := artifact.Relationship{
   352  		From: p2,
   353  		To:   p3,
   354  		Type: artifact.ContainsRelationship,
   355  	}
   356  
   357  	index := NewIndex(r1, r2, r3)
   358  
   359  	// replace p1 with p4 in the relationships
   360  	index.Replace(p1.ID(), &p4)
   361  
   362  	expectedRels := []artifact.Relationship{
   363  		{
   364  			From: p4, // replaced
   365  			To:   p2,
   366  			Type: artifact.DependencyOfRelationship,
   367  		},
   368  		{
   369  			From: p3,
   370  			To:   p4, // replaced
   371  			Type: artifact.DependencyOfRelationship,
   372  		},
   373  		{
   374  			From: p2,
   375  			To:   p3,
   376  			Type: artifact.ContainsRelationship,
   377  		},
   378  	}
   379  
   380  	compareRelationships(t, expectedRels, index.All())
   381  }
   382  
   383  func compareRelationships(t testing.TB, expected, actual []artifact.Relationship) {
   384  	assert.Equal(t, len(expected), len(actual), "number of relationships should match")
   385  	for _, e := range expected {
   386  		found := false
   387  		for _, a := range actual {
   388  			if a.From.ID() == e.From.ID() && a.To.ID() == e.To.ID() && a.Type == e.Type {
   389  				found = true
   390  				break
   391  			}
   392  		}
   393  		assert.True(t, found, "expected relationship not found: %+v", e)
   394  	}
   395  }
   396  
   397  func TestReplace_NoExistingRelations(t *testing.T) {
   398  	p1 := pkg.Package{Name: "pkg-1"}
   399  	p2 := pkg.Package{Name: "pkg-2"}
   400  
   401  	p1.SetID()
   402  	p2.SetID()
   403  
   404  	index := NewIndex()
   405  
   406  	index.Replace(p1.ID(), &p2)
   407  
   408  	allRels := index.All()
   409  	assert.Len(t, allRels, 0)
   410  }