github.com/quay/claircore@v1.5.28/indexer/controller/scanlayers_test.go (about)

     1  package controller
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/quay/zlog"
    11  
    12  	"github.com/quay/claircore"
    13  	"github.com/quay/claircore/indexer"
    14  	"github.com/quay/claircore/internal/wart"
    15  	"github.com/quay/claircore/test"
    16  	indexer_mock "github.com/quay/claircore/test/mock/indexer"
    17  )
    18  
    19  func TestScanLayers(t *testing.T) {
    20  	ctx := context.Background()
    21  	tt := []struct {
    22  		mock          func(t *testing.T) indexer.Store
    23  		name          string
    24  		expectedState State
    25  	}{
    26  		{
    27  			name:          "Success",
    28  			expectedState: Coalesce,
    29  			mock: func(t *testing.T) indexer.Store {
    30  				ctrl := gomock.NewController(t)
    31  				s := indexer_mock.NewMockStore(ctrl)
    32  
    33  				s.EXPECT().LayerScanned(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(true, nil)
    34  				return s
    35  			},
    36  		},
    37  	}
    38  
    39  	for _, table := range tt {
    40  		t.Run(table.name, func(t *testing.T) {
    41  			ctx := zlog.Test(ctx, t)
    42  			s := table.mock(t)
    43  			opts := &indexer.Options{
    44  				Store: s,
    45  			}
    46  			scnr := New(opts)
    47  			var err error
    48  			scnr.LayerScanner, err = indexer.NewLayerScanner(ctx, 1, opts)
    49  			if err != nil {
    50  				t.Error(err)
    51  			}
    52  
    53  			state, err := scanLayers(ctx, scnr)
    54  			if err != nil {
    55  				t.Error(err)
    56  			}
    57  			if got, want := state, table.expectedState; got != want {
    58  				t.Errorf("got: %v, want: %v", got, want)
    59  			}
    60  		})
    61  	}
    62  }
    63  
    64  func TestScanNoErrors(t *testing.T) {
    65  	ctx := zlog.Test(context.Background(), t)
    66  	ctrl := gomock.NewController(t)
    67  
    68  	store := indexer_mock.NewMockStore(ctrl)
    69  	pkg := indexer_mock.NewMockPackageScanner(ctrl)
    70  	dist := indexer_mock.NewMockDistributionScanner(ctrl)
    71  	repo := indexer_mock.NewMockRepositoryScanner(ctrl)
    72  	_, descs := test.ServeLayers(t, 2)
    73  	descMatch := make([]*test.LayerMatcher, len(descs))
    74  	for i := range descs {
    75  		descMatch[i] = test.NewLayerMatcher(&descs[i])
    76  	}
    77  
    78  	// These type parameters are needed for go1.20.
    79  	setupCalls[*indexer_mock.MockPackageScannerMockRecorder, *indexer_mock.MockStoreMockRecorder](pkg, store, descMatch)
    80  	setupCalls[*indexer_mock.MockDistributionScannerMockRecorder, *indexer_mock.MockStoreMockRecorder](dist, store, descMatch)
    81  	setupCalls[*indexer_mock.MockRepositoryScannerMockRecorder, *indexer_mock.MockStoreMockRecorder](repo, store, descMatch)
    82  
    83  	ecosystem := &indexer.Ecosystem{
    84  		Name: "test-ecosystem",
    85  		PackageScanners: func(ctx context.Context) ([]indexer.PackageScanner, error) {
    86  			return []indexer.PackageScanner{pkg}, nil
    87  		},
    88  		DistributionScanners: func(ctx context.Context) ([]indexer.DistributionScanner, error) {
    89  			return []indexer.DistributionScanner{dist}, nil
    90  		},
    91  		RepositoryScanners: func(ctx context.Context) ([]indexer.RepositoryScanner, error) {
    92  			return []indexer.RepositoryScanner{repo}, nil
    93  		},
    94  	}
    95  
    96  	ls := wart.DescriptionsToLayers(descs)
    97  	d, err := claircore.NewDigest("sha256", make([]byte, sha256.Size))
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  	m := &claircore.Manifest{
   102  		Hash:   d,
   103  		Layers: ls,
   104  	}
   105  
   106  	opts := &indexer.Options{
   107  		Store:      store,
   108  		Ecosystems: []*indexer.Ecosystem{ecosystem},
   109  	}
   110  	c := New(opts)
   111  	c.manifest = m
   112  	c.LayerScanner, err = indexer.NewLayerScanner(ctx, 1, opts)
   113  	if err != nil {
   114  		t.Error(err)
   115  	}
   116  
   117  	state, err := scanLayers(ctx, c)
   118  	if err != nil {
   119  		t.Fatalf("failed to scan test layers: %v", err)
   120  	}
   121  
   122  	if state != Coalesce {
   123  		t.Errorf("got: %v state, wanted: %v state", state, Coalesce)
   124  	}
   125  }
   126  
   127  type scanRecorder interface {
   128  	Kind() *gomock.Call
   129  	Name() *gomock.Call
   130  	Version() *gomock.Call
   131  	// Abuse the fact that we overloaded the "Scan" name. That's finally useful.
   132  	Scan(any, any) *gomock.Call
   133  }
   134  
   135  type storeRecorder interface {
   136  	LayerScanned(any, any, any) *gomock.Call
   137  	SetLayerScanned(any, any, any) *gomock.Call
   138  	IndexPackages(any, any, any, any) *gomock.Call
   139  	IndexDistributions(any, any, any, any) *gomock.Call
   140  	IndexRepositories(any, any, any, any) *gomock.Call
   141  }
   142  
   143  type mock[R any] interface {
   144  	EXPECT() R
   145  }
   146  
   147  // SetupCalls is a helper for doing all the setup for a VersionedScanner mock.
   148  func setupCalls[C scanRecorder, T storeRecorder, Mc mock[C], Mt mock[T]](m Mc, s Mt, ls []*test.LayerMatcher) Mc {
   149  	// In hindsight, this function gains little from being written with
   150  	// generics.
   151  	var retVal any
   152  	var kind string
   153  	scan := m.EXPECT()
   154  	store := s.EXPECT()
   155  	switch t := any(m).(type) {
   156  	case *indexer_mock.MockPackageScanner:
   157  		retVal = []*claircore.Package{}
   158  		kind = "package"
   159  	case *indexer_mock.MockDistributionScanner:
   160  		retVal = []*claircore.Distribution{}
   161  		kind = "distribution"
   162  	case *indexer_mock.MockRepositoryScanner:
   163  		retVal = []*claircore.Repository{}
   164  		kind = "repository"
   165  	default:
   166  		panic(fmt.Sprintf("unreachable: passed %T", t))
   167  	}
   168  	for _, l := range ls {
   169  		scan.Scan(gomock.Any(), l).Return(retVal, nil)
   170  		d := l.DigestMatcher()
   171  		store.LayerScanned(gomock.Any(), d, m).Return(false, nil)
   172  		store.SetLayerScanned(gomock.Any(), d, m).Return(nil)
   173  		switch t := any(m).(type) {
   174  		case *indexer_mock.MockPackageScanner:
   175  			store.IndexPackages(gomock.Any(), gomock.Any(), l, m).Return(nil)
   176  		case *indexer_mock.MockDistributionScanner:
   177  			store.IndexDistributions(gomock.Any(), gomock.Any(), l, m).Return(nil)
   178  		case *indexer_mock.MockRepositoryScanner:
   179  			store.IndexRepositories(gomock.Any(), gomock.Any(), l, m).Return(nil)
   180  		default:
   181  			panic(fmt.Sprintf("unreachable: passed %T", t))
   182  		}
   183  	}
   184  	scan.Kind().MinTimes(1).Return(kind)
   185  	scan.Name().AnyTimes().Return(kind)
   186  	scan.Version().AnyTimes().Return("1")
   187  	return m
   188  }