zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/extensions/search/resolver_test.go (about)

     1  //go:build search
     2  
     3  package search //nolint
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/99designs/gqlgen/graphql"
    13  	godigest "github.com/opencontainers/go-digest"
    14  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    15  	. "github.com/smartystreets/goconvey/convey"
    16  
    17  	"zotregistry.io/zot/pkg/common"
    18  	"zotregistry.io/zot/pkg/extensions/search/convert"
    19  	cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
    20  	cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
    21  	"zotregistry.io/zot/pkg/extensions/search/gql_generated"
    22  	"zotregistry.io/zot/pkg/log"
    23  	"zotregistry.io/zot/pkg/meta/boltdb"
    24  	mConvert "zotregistry.io/zot/pkg/meta/convert"
    25  	mTypes "zotregistry.io/zot/pkg/meta/types"
    26  	reqCtx "zotregistry.io/zot/pkg/requestcontext"
    27  	"zotregistry.io/zot/pkg/storage"
    28  	. "zotregistry.io/zot/pkg/test/image-utils"
    29  	"zotregistry.io/zot/pkg/test/mocks"
    30  	ociutils "zotregistry.io/zot/pkg/test/oci-utils"
    31  )
    32  
    33  var ErrTestError = errors.New("TestError")
    34  
    35  func TestResolverGlobalSearch(t *testing.T) {
    36  	Convey("globalSearch", t, func() {
    37  		const query = "repo1"
    38  		Convey("MetaDB SearchRepos error", func() {
    39  			mockMetaDB := mocks.MetaDBMock{
    40  				SearchReposFn: func(ctx context.Context, searchText string,
    41  				) ([]mTypes.RepoMeta, error) {
    42  					return []mTypes.RepoMeta{}, ErrTestError
    43  				},
    44  			}
    45  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
    46  				graphql.DefaultRecover)
    47  			mockCve := mocks.CveInfoMock{}
    48  			repos, images, layers, err := globalSearch(responseContext, query, mockMetaDB, &gql_generated.Filter{},
    49  				&gql_generated.PageInput{}, mockCve, log.NewLogger("debug", ""))
    50  			So(err, ShouldNotBeNil)
    51  			So(images, ShouldBeEmpty)
    52  			So(layers, ShouldBeEmpty)
    53  			So(repos.Results, ShouldBeEmpty)
    54  		})
    55  
    56  		Convey("paginated fail", func() {
    57  			pageInput := &gql_generated.PageInput{
    58  				Limit: ref(-1),
    59  			}
    60  
    61  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
    62  				graphql.DefaultRecover)
    63  
    64  			_, _, _, err := globalSearch(responseContext, "repo", mocks.MetaDBMock{}, &gql_generated.Filter{},
    65  				pageInput, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
    66  			So(err, ShouldNotBeNil)
    67  
    68  			_, _, _, err = globalSearch(responseContext, "repo:tag", mocks.MetaDBMock{}, &gql_generated.Filter{},
    69  				pageInput, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
    70  			So(err, ShouldNotBeNil)
    71  		})
    72  
    73  		Convey("MetaDB SearchTags gives error", func() {
    74  			mockMetaDB := mocks.MetaDBMock{
    75  				SearchTagsFn: func(ctx context.Context, searchText string) ([]mTypes.FullImageMeta, error) {
    76  					return []mTypes.FullImageMeta{}, ErrTestError
    77  				},
    78  			}
    79  			const query = "repo1:1.0.1"
    80  			mockCve := mocks.CveInfoMock{}
    81  
    82  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
    83  				graphql.DefaultRecover)
    84  			repos, images, layers, err := globalSearch(responseContext, query, mockMetaDB, &gql_generated.Filter{},
    85  				&gql_generated.PageInput{}, mockCve, log.NewLogger("debug", ""))
    86  			So(err, ShouldNotBeNil)
    87  			So(images, ShouldBeEmpty)
    88  			So(layers, ShouldBeEmpty)
    89  			So(repos.Results, ShouldBeEmpty)
    90  		})
    91  	})
    92  }
    93  
    94  func TestRepoListWithNewestImage(t *testing.T) {
    95  	Convey("RepoListWithNewestImage", t, func() {
    96  		Convey("MetaDB SearchRepos error", func() {
    97  			mockMetaDB := mocks.MetaDBMock{
    98  				SearchReposFn: func(ctx context.Context, searchText string) ([]mTypes.RepoMeta, error) {
    99  					return []mTypes.RepoMeta{}, ErrTestError
   100  				},
   101  			}
   102  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   103  				graphql.DefaultRecover)
   104  			mockCve := mocks.CveInfoMock{}
   105  
   106  			pageInput := gql_generated.PageInput{
   107  				Limit:  ref(1),
   108  				Offset: ref(0),
   109  				SortBy: ref(gql_generated.SortCriteriaUpdateTime),
   110  			}
   111  			repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), &pageInput, mockMetaDB)
   112  			So(err, ShouldNotBeNil)
   113  			So(repos.Results, ShouldBeEmpty)
   114  		})
   115  
   116  		Convey("paginated fail", func() {
   117  			pageInput := &gql_generated.PageInput{
   118  				Limit: ref(-1),
   119  			}
   120  
   121  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   122  				graphql.DefaultRecover)
   123  
   124  			_, err := repoListWithNewestImage(responseContext, mocks.CveInfoMock{}, log.NewLogger("debug", ""),
   125  				pageInput, mocks.MetaDBMock{})
   126  			So(err, ShouldNotBeNil)
   127  		})
   128  
   129  		Convey("Working SearchRepo function", func() {
   130  			createTime := time.Now()
   131  			createTime2 := createTime.Add(time.Second)
   132  			img1 := CreateImageWith().DefaultLayers().
   133  				ImageConfig(ispec.Image{
   134  					Config: ispec.ImageConfig{
   135  						Labels: map[string]string{},
   136  					},
   137  					Created: &createTime,
   138  				}).Build()
   139  
   140  			img2 := CreateImageWith().DefaultLayers().
   141  				ImageConfig(ispec.Image{
   142  					Config: ispec.ImageConfig{
   143  						Labels: map[string]string{},
   144  					},
   145  					Created: &createTime2,
   146  				}).Build()
   147  
   148  			mockMetaDB := mocks.MetaDBMock{
   149  				SearchReposFn: func(ctx context.Context, searchText string) ([]mTypes.RepoMeta, error) {
   150  					repos := []mTypes.RepoMeta{
   151  						{
   152  							Name: "repo1",
   153  							Tags: map[string]mTypes.Descriptor{
   154  								"1.0.1": {
   155  									Digest:    img1.DigestStr(),
   156  									MediaType: ispec.MediaTypeImageManifest,
   157  								},
   158  							},
   159  							Signatures: map[string]mTypes.ManifestSignatures{
   160  								img1.DigestStr(): {
   161  									"cosign": []mTypes.SignatureInfo{
   162  										{SignatureManifestDigest: "testSignature", LayersInfo: []mTypes.LayerInfo{}},
   163  									},
   164  								},
   165  							},
   166  							StarCount: 100,
   167  							LastUpdatedImage: &mTypes.LastUpdatedImage{
   168  								Descriptor: mTypes.Descriptor{
   169  									Digest:    img1.DigestStr(),
   170  									MediaType: ispec.MediaTypeImageManifest,
   171  								},
   172  								Tag:         "1.0.1",
   173  								LastUpdated: &createTime,
   174  							},
   175  						},
   176  						{
   177  							Name: "repo2",
   178  							Tags: map[string]mTypes.Descriptor{
   179  								"1.0.2": {
   180  									Digest:    img2.DigestStr(),
   181  									MediaType: ispec.MediaTypeImageManifest,
   182  								},
   183  							},
   184  							Signatures: map[string]mTypes.ManifestSignatures{
   185  								img1.DigestStr(): {
   186  									"cosign": []mTypes.SignatureInfo{
   187  										{SignatureManifestDigest: "testSignature", LayersInfo: []mTypes.LayerInfo{}},
   188  									},
   189  								},
   190  							},
   191  							StarCount: 100,
   192  							LastUpdatedImage: &mTypes.LastUpdatedImage{
   193  								Descriptor: mTypes.Descriptor{
   194  									Digest:    img2.DigestStr(),
   195  									MediaType: ispec.MediaTypeImageManifest,
   196  								},
   197  								Tag:         "1.0.2",
   198  								LastUpdated: &createTime2,
   199  							},
   200  						},
   201  					}
   202  
   203  					return repos, nil
   204  				},
   205  				FilterImageMetaFn: func(ctx context.Context, digests []string,
   206  				) (map[string]mTypes.ImageMeta, error) {
   207  					return map[string]mTypes.ImageMeta{
   208  						img1.DigestStr(): mConvert.GetImageManifestMeta(img1.Manifest, img1.Config,
   209  							img1.ManifestDescriptor.Size, img1.ManifestDescriptor.Digest),
   210  						img2.DigestStr(): mConvert.GetImageManifestMeta(img2.Manifest, img2.Config,
   211  							img2.ManifestDescriptor.Size, img2.ManifestDescriptor.Digest),
   212  					}, nil
   213  				},
   214  			}
   215  			Convey("MetaDB missing requestedPage", func() {
   216  				responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   217  					graphql.DefaultRecover)
   218  				mockCve := mocks.CveInfoMock{}
   219  				repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), nil, mockMetaDB)
   220  				So(err, ShouldBeNil)
   221  				So(repos.Results, ShouldNotBeEmpty)
   222  			})
   223  
   224  			Convey("MetaDB SearchRepo is successful", func() {
   225  				pageInput := gql_generated.PageInput{
   226  					Limit:  ref(2),
   227  					Offset: ref(0),
   228  					SortBy: ref(gql_generated.SortCriteriaUpdateTime),
   229  				}
   230  
   231  				responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   232  					graphql.DefaultRecover)
   233  
   234  				mockCve := mocks.CveInfoMock{}
   235  				repos, err := repoListWithNewestImage(responseContext, mockCve,
   236  					log.NewLogger("debug", ""), &pageInput, mockMetaDB)
   237  				So(err, ShouldBeNil)
   238  				So(repos, ShouldNotBeEmpty)
   239  				So(len(repos.Results), ShouldEqual, 2)
   240  				So(*repos.Results[0].Name, ShouldEqual, "repo2")
   241  				So(*repos.Results[0].LastUpdated, ShouldEqual, createTime2)
   242  			})
   243  		})
   244  	})
   245  }
   246  
   247  func TestGetFilteredPaginatedRepos(t *testing.T) {
   248  	ctx := context.Background()
   249  	log := log.NewLogger("debug", "")
   250  
   251  	Convey("getFilteredPaginatedRepos", t, func() {
   252  		metaDB := mocks.MetaDBMock{}
   253  
   254  		Convey("FilterRepos", func() {
   255  			metaDB.FilterReposFn = func(ctx context.Context, rankName mTypes.FilterRepoNameFunc,
   256  				filterFunc mTypes.FilterFullRepoFunc,
   257  			) ([]mTypes.RepoMeta, error) {
   258  				return nil, ErrTestError
   259  			}
   260  			_, err := getFilteredPaginatedRepos(ctx, nil, func(repo string) bool { return true }, log,
   261  				&gql_generated.PageInput{}, metaDB)
   262  			So(err, ShouldNotBeNil)
   263  		})
   264  		Convey("FilterImageMeta", func() {
   265  			metaDB.FilterImageMetaFn = func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
   266  				return nil, ErrTestError
   267  			}
   268  			_, err := getFilteredPaginatedRepos(ctx, nil, func(repo string) bool { return true }, log,
   269  				&gql_generated.PageInput{}, metaDB)
   270  			So(err, ShouldNotBeNil)
   271  		})
   272  		Convey("PaginatedRepoMeta2RepoSummaries", func() {
   273  			_, err := getFilteredPaginatedRepos(ctx, nil, func(repo string) bool { return true }, log,
   274  				&gql_generated.PageInput{Limit: ref(-10)}, metaDB)
   275  			So(err, ShouldNotBeNil)
   276  		})
   277  	})
   278  }
   279  
   280  func TestGetBookmarkedRepos(t *testing.T) {
   281  	Convey("getBookmarkedRepos", t, func() {
   282  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   283  			graphql.DefaultRecover)
   284  		_, err := getBookmarkedRepos(
   285  			responseContext,
   286  			mocks.CveInfoMock{},
   287  			log.NewLogger("debug", ""),
   288  			nil,
   289  			mocks.MetaDBMock{
   290  				GetBookmarkedReposFn: func(ctx context.Context) ([]string, error) {
   291  					return []string{}, ErrTestError
   292  				},
   293  			},
   294  		)
   295  		So(err, ShouldNotBeNil)
   296  	})
   297  }
   298  
   299  func TestGetStarredRepos(t *testing.T) {
   300  	Convey("getStarredRepos", t, func() {
   301  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   302  			graphql.DefaultRecover)
   303  		_, err := getStarredRepos(
   304  			responseContext,
   305  			mocks.CveInfoMock{},
   306  			log.NewLogger("debug", ""),
   307  			nil,
   308  			mocks.MetaDBMock{
   309  				GetStarredReposFn: func(ctx context.Context) ([]string, error) {
   310  					return []string{}, ErrTestError
   311  				},
   312  			},
   313  		)
   314  		So(err, ShouldNotBeNil)
   315  	})
   316  }
   317  
   318  func getTestRepoMetaWithImages(repo string, images []Image) mTypes.RepoMeta {
   319  	tags := map[string]mTypes.Descriptor{"": {}}
   320  	statistics := map[string]mTypes.DescriptorStatistics{"": {}}
   321  	signatures := map[string]mTypes.ManifestSignatures{"": {}}
   322  	referrers := map[string][]mTypes.ReferrerInfo{"": {}}
   323  
   324  	for i := range images {
   325  		tags[images[i].DigestStr()] = mTypes.Descriptor{}
   326  		statistics[images[i].DigestStr()] = mTypes.DescriptorStatistics{}
   327  		signatures[images[i].DigestStr()] = mTypes.ManifestSignatures{}
   328  		referrers[images[i].DigestStr()] = []mTypes.ReferrerInfo{}
   329  	}
   330  
   331  	return mTypes.RepoMeta{
   332  		Name:       repo,
   333  		Tags:       tags,
   334  		Statistics: statistics,
   335  		Signatures: signatures,
   336  		Referrers:  referrers,
   337  	}
   338  }
   339  
   340  func TestImageListForDigest(t *testing.T) {
   341  	Convey("getImageList", t, func() {
   342  		Convey("no page requested, FilterTagsFn returns error", func() {
   343  			mockMetaDB := mocks.MetaDBMock{
   344  				FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
   345  					filterFunc mTypes.FilterFunc,
   346  				) ([]mTypes.FullImageMeta, error) {
   347  					return []mTypes.FullImageMeta{}, ErrTestError
   348  				},
   349  			}
   350  
   351  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   352  				graphql.DefaultRecover)
   353  			_, err := getImageListForDigest(responseContext, "invalid", mockMetaDB, mocks.CveInfoMock{}, nil)
   354  			So(err, ShouldNotBeNil)
   355  		})
   356  
   357  		Convey("Paginated convert fails", func() {
   358  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   359  				graphql.DefaultRecover)
   360  			_, err := getImageListForDigest(responseContext, "invalid", mocks.MetaDBMock{}, mocks.CveInfoMock{},
   361  				&gql_generated.PageInput{Limit: ref(-1)})
   362  			So(err, ShouldNotBeNil)
   363  		})
   364  
   365  		Convey("valid imageListForDigest returned for matching manifest digest", func() {
   366  			img1, img2 := CreateRandomImage(), CreateRandomImage()
   367  			mockMetaDB := mocks.MetaDBMock{
   368  				FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
   369  					filterFunc mTypes.FilterFunc,
   370  				) ([]mTypes.FullImageMeta, error) {
   371  					fullImageMetaList := []mTypes.ImageMeta{img1.AsImageMeta(), img2.AsImageMeta()}
   372  					repoMeta := getTestRepoMetaWithImages("repo", []Image{img1, img2})
   373  					tags := []string{"tag1", "tag2"}
   374  
   375  					acceptedImages := []mTypes.FullImageMeta{}
   376  
   377  					for i := range fullImageMetaList {
   378  						if filterFunc(repoMeta, fullImageMetaList[i]) {
   379  							acceptedImages = append(acceptedImages,
   380  								convert.GetFullImageMeta(tags[i], repoMeta, fullImageMetaList[i]))
   381  
   382  							continue
   383  						}
   384  					}
   385  
   386  					return acceptedImages, nil
   387  				},
   388  			}
   389  
   390  			pageInput := gql_generated.PageInput{
   391  				Limit:  ref(1),
   392  				Offset: ref(0),
   393  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
   394  			}
   395  
   396  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   397  				graphql.DefaultRecover)
   398  			imageSummaries, err := getImageListForDigest(responseContext, img1.DigestStr(),
   399  				mockMetaDB, mocks.CveInfoMock{}, &pageInput)
   400  			So(err, ShouldBeNil)
   401  			So(len(imageSummaries.Results), ShouldEqual, 1)
   402  
   403  			imageSummaries, err = getImageListForDigest(responseContext, "invalid",
   404  				mockMetaDB, mocks.CveInfoMock{}, &pageInput)
   405  			So(err, ShouldBeNil)
   406  			So(len(imageSummaries.Results), ShouldEqual, 0)
   407  
   408  			imageSummaries, err = getImageListForDigest(responseContext, img1.Manifest.Config.Digest.String(),
   409  				mockMetaDB, mocks.CveInfoMock{}, &pageInput)
   410  			So(err, ShouldBeNil)
   411  			So(len(imageSummaries.Results), ShouldEqual, 1)
   412  
   413  			imageSummaries, err = getImageListForDigest(responseContext, img1.Manifest.Layers[0].Digest.String(),
   414  				mockMetaDB, mocks.CveInfoMock{}, &pageInput)
   415  			So(err, ShouldBeNil)
   416  			So(len(imageSummaries.Results), ShouldEqual, 1)
   417  		})
   418  
   419  		Convey("valid imageListForDigest, multiple matching tags", func() {
   420  			img1 := CreateRandomImage()
   421  
   422  			mockMetaDB := mocks.MetaDBMock{
   423  				FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
   424  					filterFunc mTypes.FilterFunc,
   425  				) ([]mTypes.FullImageMeta, error) {
   426  					fullImageMetaList := []mTypes.ImageMeta{img1.AsImageMeta()}
   427  					repoMeta := getTestRepoMetaWithImages("repo", []Image{img1, img1})
   428  					tags := []string{"tag1", "tag2"}
   429  
   430  					acceptedImages := []mTypes.FullImageMeta{}
   431  
   432  					for i := range fullImageMetaList {
   433  						if filterFunc(repoMeta, fullImageMetaList[i]) {
   434  							acceptedImages = append(acceptedImages,
   435  								convert.GetFullImageMeta(tags[i], repoMeta, fullImageMetaList[i]))
   436  
   437  							continue
   438  						}
   439  					}
   440  
   441  					return acceptedImages, nil
   442  				},
   443  			}
   444  
   445  			pageInput := gql_generated.PageInput{
   446  				Limit:  ref(1),
   447  				Offset: ref(0),
   448  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
   449  			}
   450  
   451  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   452  				graphql.DefaultRecover)
   453  
   454  			imageSummaries, err := getImageListForDigest(responseContext, img1.DigestStr(),
   455  				mockMetaDB, mocks.CveInfoMock{}, &pageInput)
   456  			So(err, ShouldBeNil)
   457  			So(len(imageSummaries.Results), ShouldEqual, 1)
   458  		})
   459  	})
   460  }
   461  
   462  func TestGetImageSummaryError(t *testing.T) {
   463  	Convey("getImageSummary", t, func() {
   464  		metaDB := mocks.MetaDBMock{
   465  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
   466  				return mTypes.RepoMeta{Tags: map[string]mTypes.Descriptor{"tag": {}}}, nil
   467  			},
   468  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
   469  				return nil, ErrTestError
   470  			},
   471  		}
   472  		log := log.NewLogger("debug", "")
   473  
   474  		_, err := getImageSummary(context.Background(), "repo", "tag", nil, convert.SkipQGLField{},
   475  			metaDB, nil, log)
   476  		So(err, ShouldNotBeNil)
   477  	})
   478  }
   479  
   480  func TestImageListError(t *testing.T) {
   481  	Convey("getImageList", t, func() {
   482  		testLogger := log.NewLogger("debug", "/dev/null")
   483  		Convey("no page requested, SearchRepoFn returns error", func() {
   484  			mockMetaDB := mocks.MetaDBMock{
   485  				FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
   486  				) ([]mTypes.FullImageMeta, error) {
   487  					return []mTypes.FullImageMeta{}, ErrTestError
   488  				},
   489  			}
   490  
   491  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   492  				graphql.DefaultRecover)
   493  
   494  			_, err := getImageList(responseContext, "test", mockMetaDB, mocks.CveInfoMock{}, nil, testLogger)
   495  			So(err, ShouldNotBeNil)
   496  		})
   497  
   498  		Convey("Paginated convert fails", func() {
   499  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   500  				graphql.DefaultRecover)
   501  
   502  			_, err := getImageList(responseContext, "test", mocks.MetaDBMock{}, mocks.CveInfoMock{},
   503  				&gql_generated.PageInput{Limit: ref(-1)}, log.NewLogger("debug", ""))
   504  
   505  			So(err, ShouldNotBeNil)
   506  		})
   507  
   508  		Convey("valid repoList returned", func() {
   509  			mockMetaDB := mocks.MetaDBMock{
   510  				FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
   511  				) ([]mTypes.FullImageMeta, error) {
   512  					repoName := "correct-repo"
   513  
   514  					if !filterRepoTag(repoName, "tag") {
   515  						return []mTypes.FullImageMeta{}, nil
   516  					}
   517  
   518  					image := CreateDefaultImage()
   519  					repoMeta := mTypes.RepoMeta{
   520  						Name: "repo",
   521  						Tags: map[string]mTypes.Descriptor{image.DigestStr(): {
   522  							Digest:    image.DigestStr(),
   523  							MediaType: ispec.MediaTypeImageManifest,
   524  						}},
   525  					}
   526  
   527  					return []mTypes.FullImageMeta{convert.GetFullImageMeta("tag", repoMeta, image.AsImageMeta())}, nil
   528  				},
   529  			}
   530  
   531  			pageInput := gql_generated.PageInput{
   532  				Limit:  ref(1),
   533  				Offset: ref(0),
   534  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
   535  			}
   536  
   537  			responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   538  				graphql.DefaultRecover)
   539  
   540  			imageSummaries, err := getImageList(responseContext, "correct-repo", mockMetaDB,
   541  				mocks.CveInfoMock{}, &pageInput, testLogger)
   542  			So(err, ShouldBeNil)
   543  			So(len(imageSummaries.Results), ShouldEqual, 1)
   544  
   545  			imageSummaries, err = getImageList(responseContext, "invalid", mockMetaDB,
   546  				mocks.CveInfoMock{}, &pageInput, testLogger)
   547  			So(err, ShouldBeNil)
   548  			So(len(imageSummaries.Results), ShouldEqual, 0)
   549  		})
   550  	})
   551  }
   552  
   553  func TestGetReferrers(t *testing.T) {
   554  	Convey("getReferrers", t, func() {
   555  		referredDigest := godigest.FromString("t").String()
   556  
   557  		Convey("referredDigest is empty", func() {
   558  			testLogger := log.NewLogger("debug", "")
   559  
   560  			_, err := getReferrers(mocks.MetaDBMock{}, "test", "", nil, testLogger)
   561  			So(err, ShouldNotBeNil)
   562  		})
   563  
   564  		Convey("GetReferrers returns error", func() {
   565  			testLogger := log.NewLogger("debug", "")
   566  			mockedStore := mocks.MetaDBMock{
   567  				GetReferrersInfoFn: func(repo string, referredDigest godigest.Digest, artifactTypes []string,
   568  				) ([]mTypes.ReferrerInfo, error) {
   569  					return nil, ErrTestError
   570  				},
   571  			}
   572  
   573  			_, err := getReferrers(mockedStore, "test", referredDigest, nil, testLogger)
   574  			So(err, ShouldNotBeNil)
   575  		})
   576  
   577  		Convey("GetReferrers return index of descriptors", func() {
   578  			testLogger := log.NewLogger("debug", "")
   579  			referrerDescriptor := ispec.Descriptor{
   580  				MediaType:    ispec.MediaTypeImageManifest,
   581  				ArtifactType: "com.artifact.test",
   582  				Size:         403,
   583  				Digest:       godigest.FromString("test"),
   584  				Annotations: map[string]string{
   585  					"key": "value",
   586  				},
   587  			}
   588  			mockedStore := mocks.MetaDBMock{
   589  				GetReferrersInfoFn: func(repo string, referredDigest godigest.Digest, artifactTypes []string,
   590  				) ([]mTypes.ReferrerInfo, error) {
   591  					return []mTypes.ReferrerInfo{
   592  						{
   593  							Digest:       referrerDescriptor.Digest.String(),
   594  							MediaType:    referrerDescriptor.MediaType,
   595  							ArtifactType: referrerDescriptor.ArtifactType,
   596  							Size:         int(referrerDescriptor.Size),
   597  							Annotations:  referrerDescriptor.Annotations,
   598  						},
   599  					}, nil
   600  				},
   601  			}
   602  
   603  			referrers, err := getReferrers(mockedStore, "test", referredDigest, nil, testLogger)
   604  			So(err, ShouldBeNil)
   605  			So(*referrers[0].ArtifactType, ShouldEqual, referrerDescriptor.ArtifactType)
   606  			So(*referrers[0].MediaType, ShouldEqual, referrerDescriptor.MediaType)
   607  			So(*referrers[0].Size, ShouldEqual, referrerDescriptor.Size)
   608  			So(*referrers[0].Digest, ShouldEqual, referrerDescriptor.Digest.String())
   609  			So(*referrers[0].Annotations[0].Value, ShouldEqual, referrerDescriptor.Annotations["key"])
   610  		})
   611  	})
   612  }
   613  
   614  func TestQueryResolverErrors(t *testing.T) {
   615  	Convey("Errors", t, func() {
   616  		log := log.NewLogger("debug", "")
   617  		ctx := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
   618  			graphql.DefaultRecover)
   619  
   620  		Convey("GlobalSearch error bad requested page", func() {
   621  			resolverConfig := NewResolver(log, storage.StoreController{}, mocks.MetaDBMock{}, mocks.CveInfoMock{})
   622  			resolver := queryResolver{resolverConfig}
   623  			pageInput := gql_generated.PageInput{
   624  				Limit:  ref(-1),
   625  				Offset: ref(0),
   626  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
   627  			}
   628  
   629  			_, err := resolver.GlobalSearch(ctx, "some_string", &gql_generated.Filter{}, &pageInput)
   630  			So(err, ShouldNotBeNil)
   631  
   632  			pageInput = gql_generated.PageInput{
   633  				Limit:  ref(0),
   634  				Offset: ref(-1),
   635  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
   636  			}
   637  
   638  			_, err = resolver.GlobalSearch(ctx, "some_string", &gql_generated.Filter{}, &pageInput)
   639  			So(err, ShouldNotBeNil)
   640  		})
   641  
   642  		Convey("GlobalSearch error filte image meta", func() {
   643  			resolverConfig := NewResolver(log, storage.StoreController{}, mocks.MetaDBMock{
   644  				FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
   645  					return nil, ErrTestError
   646  				},
   647  			}, mocks.CveInfoMock{})
   648  			resolver := queryResolver{resolverConfig}
   649  
   650  			_, err := resolver.GlobalSearch(ctx, "some_string", &gql_generated.Filter{}, getPageInput(1, 1))
   651  			So(err, ShouldNotBeNil)
   652  		})
   653  
   654  		Convey("ImageListForCve error in GetMultipleRepoMeta", func() {
   655  			resolverConfig := NewResolver(
   656  				log,
   657  				storage.StoreController{
   658  					DefaultStore: mocks.MockedImageStore{},
   659  				},
   660  				mocks.MetaDBMock{
   661  					GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
   662  					) ([]mTypes.RepoMeta, error) {
   663  						return []mTypes.RepoMeta{}, ErrTestError
   664  					},
   665  				},
   666  				mocks.CveInfoMock{},
   667  			)
   668  
   669  			qr := queryResolver{resolverConfig}
   670  
   671  			_, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.Filter{}, &gql_generated.PageInput{})
   672  			So(err, ShouldNotBeNil)
   673  		})
   674  
   675  		Convey("ImageListForCve error in FilterTags", func() {
   676  			resolverConfig := NewResolver(
   677  				log,
   678  				storage.StoreController{
   679  					DefaultStore: mocks.MockedImageStore{},
   680  				},
   681  				mocks.MetaDBMock{
   682  					FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
   683  					) ([]mTypes.FullImageMeta, error) {
   684  						return []mTypes.FullImageMeta{}, ErrTestError
   685  					},
   686  				},
   687  				mocks.CveInfoMock{},
   688  			)
   689  
   690  			qr := queryResolver{resolverConfig}
   691  
   692  			_, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.Filter{}, &gql_generated.PageInput{})
   693  			So(err, ShouldNotBeNil)
   694  		})
   695  
   696  		Convey("ImageListWithCVEFixed error in FilterTags", func() {
   697  			resolverConfig := NewResolver(
   698  				log,
   699  				storage.StoreController{
   700  					DefaultStore: mocks.MockedImageStore{},
   701  				},
   702  				mocks.MetaDBMock{
   703  					FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
   704  					) ([]mTypes.FullImageMeta, error) {
   705  						return []mTypes.FullImageMeta{}, ErrTestError
   706  					},
   707  				},
   708  				mocks.CveInfoMock{},
   709  			)
   710  
   711  			qr := queryResolver{resolverConfig}
   712  
   713  			_, err := qr.ImageListWithCVEFixed(ctx, "cve1", "image", &gql_generated.Filter{}, &gql_generated.PageInput{})
   714  			So(err, ShouldNotBeNil)
   715  		})
   716  
   717  		Convey("RepoListWithNewestImage repoListWithNewestImage() filter image meta error", func() {
   718  			resolverConfig := NewResolver(
   719  				log,
   720  				storage.StoreController{
   721  					DefaultStore: mocks.MockedImageStore{},
   722  				},
   723  				mocks.MetaDBMock{
   724  					SearchReposFn: func(ctx context.Context, searchText string,
   725  					) ([]mTypes.RepoMeta, error) {
   726  						return []mTypes.RepoMeta{}, nil
   727  					},
   728  					FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
   729  						return nil, ErrTestError
   730  					},
   731  				},
   732  				mocks.CveInfoMock{},
   733  			)
   734  
   735  			qr := queryResolver{resolverConfig}
   736  
   737  			_, err := qr.RepoListWithNewestImage(ctx, &gql_generated.PageInput{})
   738  			So(err, ShouldNotBeNil)
   739  		})
   740  
   741  		Convey("RepoListWithNewestImage repoListWithNewestImage() errors mocked StoreController", func() {
   742  			resolverConfig := NewResolver(
   743  				log,
   744  				storage.StoreController{
   745  					DefaultStore: mocks.MockedImageStore{},
   746  				},
   747  				mocks.MetaDBMock{
   748  					SearchReposFn: func(ctx context.Context, searchText string,
   749  					) ([]mTypes.RepoMeta, error) {
   750  						return nil, ErrTestError
   751  					},
   752  				},
   753  				mocks.CveInfoMock{},
   754  			)
   755  
   756  			qr := queryResolver{resolverConfig}
   757  
   758  			_, err := qr.RepoListWithNewestImage(ctx, &gql_generated.PageInput{})
   759  			So(err, ShouldNotBeNil)
   760  		})
   761  
   762  		Convey("RepoListWithNewestImage repoListWithNewestImage() errors valid StoreController", func() {
   763  			resolverConfig := NewResolver(
   764  				log,
   765  				storage.StoreController{},
   766  				mocks.MetaDBMock{
   767  					SearchReposFn: func(ctx context.Context, searchText string,
   768  					) ([]mTypes.RepoMeta, error) {
   769  						return nil, ErrTestError
   770  					},
   771  				},
   772  				mocks.CveInfoMock{},
   773  			)
   774  
   775  			qr := queryResolver{resolverConfig}
   776  
   777  			_, err := qr.RepoListWithNewestImage(ctx, &gql_generated.PageInput{})
   778  			So(err, ShouldNotBeNil)
   779  		})
   780  
   781  		Convey("ImageList getImageList() errors", func() {
   782  			resolverConfig := NewResolver(
   783  				log,
   784  				storage.StoreController{},
   785  				mocks.MetaDBMock{
   786  					FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
   787  					) ([]mTypes.FullImageMeta, error) {
   788  						return []mTypes.FullImageMeta{}, ErrTestError
   789  					},
   790  				},
   791  				mocks.CveInfoMock{},
   792  			)
   793  
   794  			qr := queryResolver{resolverConfig}
   795  
   796  			_, err := qr.ImageList(ctx, "repo", &gql_generated.PageInput{})
   797  			So(err, ShouldNotBeNil)
   798  		})
   799  
   800  		Convey("DerivedImageList ExpandedRepoInfo() errors", func() {
   801  			resolverConfig := NewResolver(
   802  				log,
   803  				storage.StoreController{
   804  					DefaultStore: mocks.MockedImageStore{
   805  						GetRepositoriesFn: func() ([]string, error) {
   806  							return []string{"sub1/repo"}, nil
   807  						},
   808  						GetImageManifestFn: func(repo, reference string) ([]byte, godigest.Digest, string, error) {
   809  							return []byte("{}"), "digest", "str", nil
   810  						},
   811  					},
   812  				},
   813  				mocks.MetaDBMock{
   814  					GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
   815  						return mTypes.RepoMeta{}, ErrTestError
   816  					},
   817  				},
   818  				mocks.CveInfoMock{},
   819  			)
   820  
   821  			qr := queryResolver{resolverConfig}
   822  
   823  			_, err := qr.DerivedImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
   824  			So(err, ShouldNotBeNil)
   825  		})
   826  
   827  		Convey("BaseImageList ExpandedRepoInfo() errors", func() {
   828  			resolverConfig := NewResolver(
   829  				log,
   830  				storage.StoreController{
   831  					DefaultStore: mocks.MockedImageStore{
   832  						GetRepositoriesFn: func() ([]string, error) {
   833  							return []string{"sub1/repo"}, nil
   834  						},
   835  						GetImageManifestFn: func(repo, reference string) ([]byte, godigest.Digest, string, error) {
   836  							return []byte("{}"), "digest", "str", nil
   837  						},
   838  					},
   839  				},
   840  				mocks.MetaDBMock{
   841  					GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
   842  						return mTypes.RepoMeta{}, ErrTestError
   843  					},
   844  				},
   845  				mocks.CveInfoMock{},
   846  			)
   847  
   848  			qr := queryResolver{resolverConfig}
   849  
   850  			_, err := qr.BaseImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
   851  			So(err, ShouldNotBeNil)
   852  		})
   853  
   854  		Convey("DerivedImageList and BaseImage List FilterTags() errors", func() {
   855  			image := CreateDefaultImage()
   856  
   857  			resolverConfig := NewResolver(
   858  				log,
   859  				storage.StoreController{},
   860  				mocks.MetaDBMock{
   861  					FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
   862  					) ([]mTypes.FullImageMeta, error) {
   863  						return []mTypes.FullImageMeta{}, ErrTestError
   864  					},
   865  					GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
   866  						return mTypes.RepoMeta{
   867  							Name: "repo",
   868  							Tags: map[string]mTypes.Descriptor{
   869  								"tag": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
   870  							},
   871  						}, nil
   872  					},
   873  					GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
   874  						return image.AsImageMeta(), nil
   875  					},
   876  				},
   877  				mocks.CveInfoMock{},
   878  			)
   879  
   880  			resolver := queryResolver{resolverConfig}
   881  
   882  			_, err := resolver.DerivedImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
   883  			So(err, ShouldNotBeNil)
   884  
   885  			_, err = resolver.BaseImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
   886  			So(err, ShouldNotBeNil)
   887  		})
   888  
   889  		Convey("GetReferrers error", func() {
   890  			resolverConfig := NewResolver(
   891  				log,
   892  				storage.StoreController{
   893  					DefaultStore: mocks.MockedImageStore{
   894  						GetReferrersFn: func(repo string, digest godigest.Digest, artifactTypes []string) (ispec.Index, error) {
   895  							return ispec.Index{}, ErrTestError
   896  						},
   897  					},
   898  				},
   899  				mocks.MetaDBMock{},
   900  				mocks.CveInfoMock{},
   901  			)
   902  
   903  			qr := queryResolver{resolverConfig}
   904  
   905  			_, err := qr.Referrers(ctx, "repo", "", nil)
   906  			So(err, ShouldNotBeNil)
   907  		})
   908  	})
   909  }
   910  
   911  func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
   912  	ctx := context.Background()
   913  	log := log.NewLogger("debug", "")
   914  	LINUX := "linux"
   915  	AMD := "amd"
   916  	ARM := "arm64"
   917  
   918  	boltDriver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: t.TempDir()})
   919  	if err != nil {
   920  		panic(err)
   921  	}
   922  
   923  	metaDB, err := boltdb.New(boltDriver, log)
   924  	if err != nil {
   925  		panic(err)
   926  	}
   927  
   928  	image1 := CreateImageWith().RandomLayers(5, 2).ImageConfig(ispec.Image{
   929  		Created: DateRef(2008, 1, 1, 12, 0, 0, 0, time.UTC),
   930  		Platform: ispec.Platform{
   931  			Architecture: AMD,
   932  			OS:           LINUX,
   933  		},
   934  	}).Build()
   935  	digest1 := image1.Digest()
   936  
   937  	image2 := CreateImageWith().RandomLayers(5, 2).ImageConfig(ispec.Image{
   938  		Created: DateRef(2009, 1, 1, 12, 0, 0, 0, time.UTC),
   939  		Platform: ispec.Platform{
   940  			Architecture: AMD,
   941  			OS:           LINUX,
   942  		},
   943  	}).Build()
   944  	digest2 := image2.Digest()
   945  
   946  	image3 := CreateImageWith().RandomLayers(5, 2).ImageConfig(ispec.Image{
   947  		Created: DateRef(2010, 1, 1, 12, 0, 0, 0, time.UTC),
   948  		Platform: ispec.Platform{
   949  			Architecture: ARM,
   950  			OS:           LINUX,
   951  		},
   952  	}).Build()
   953  	digest3 := image3.Digest()
   954  
   955  	ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB,
   956  		ociutils.Repo{
   957  			Name: "repo1", Images: []ociutils.RepoImage{
   958  				{Image: image1, Reference: "1.0.0"},
   959  				{Image: image2, Reference: "1.0.1"},
   960  				{Image: image3, Reference: "1.1.0"},
   961  				{Image: image3, Reference: "latest"},
   962  			},
   963  		},
   964  		ociutils.Repo{
   965  			Name: "repo2", Images: []ociutils.RepoImage{
   966  				{Image: image1, Reference: "2.0.0"},
   967  				{Image: image2, Reference: "2.0.1"},
   968  				{Image: image3, Reference: "2.1.0"},
   969  				{Image: image3, Reference: "latest"},
   970  			},
   971  		},
   972  		ociutils.Repo{
   973  			Name: "repo3", Images: []ociutils.RepoImage{
   974  				{Image: image2, Reference: "3.0.1"},
   975  				{Image: image3, Reference: "3.1.0"},
   976  				{Image: image3, Reference: "latest"},
   977  			},
   978  		},
   979  	)
   980  	if err != nil {
   981  		panic(err)
   982  	}
   983  
   984  	getCveResults := func(digestStr string) map[string]cvemodel.CVE {
   985  		if digestStr == digest1.String() {
   986  			return map[string]cvemodel.CVE{
   987  				"CVE1": {
   988  					ID:          "CVE1",
   989  					Severity:    "HIGH",
   990  					Title:       "Title CVE1",
   991  					Description: "Description CVE1",
   992  				},
   993  				"CVE2": {
   994  					ID:          "CVE2",
   995  					Severity:    "MEDIUM",
   996  					Title:       "Title CVE2",
   997  					Description: "Description CVE2",
   998  				},
   999  				"CVE3": {
  1000  					ID:          "CVE3",
  1001  					Severity:    "LOW",
  1002  					Title:       "Title CVE3",
  1003  					Description: "Description CVE3",
  1004  				},
  1005  				"CVE34": {
  1006  					ID:          "CVE34",
  1007  					Severity:    "LOW",
  1008  					Title:       "Title for CVE34",
  1009  					Description: "Description CVE34",
  1010  				},
  1011  			}
  1012  		}
  1013  
  1014  		if digestStr == digest2.String() {
  1015  			return map[string]cvemodel.CVE{
  1016  				"CVE2": {
  1017  					ID:          "CVE2",
  1018  					Severity:    "MEDIUM",
  1019  					Title:       "Title CVE2",
  1020  					Description: "Description CVE2",
  1021  				},
  1022  				"CVE3": {
  1023  					ID:          "CVE3",
  1024  					Severity:    "LOW",
  1025  					Title:       "Title CVE3",
  1026  					Description: "Description CVE3",
  1027  				},
  1028  			}
  1029  		}
  1030  
  1031  		if digestStr == digest3.String() {
  1032  			return map[string]cvemodel.CVE{
  1033  				"CVE3": {
  1034  					ID:          "CVE3",
  1035  					Severity:    "LOW",
  1036  					Title:       "Title CVE3",
  1037  					Description: "Description CVE3",
  1038  				},
  1039  			}
  1040  		}
  1041  
  1042  		// By default the image has no vulnerabilities
  1043  		return map[string]cvemodel.CVE{}
  1044  	}
  1045  
  1046  	// MetaDB loaded with initial data, now mock the scanner
  1047  	// Setup test CVE data in mock scanner
  1048  	scanner := mocks.CveScannerMock{
  1049  		ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) {
  1050  			repo, ref, _, _ := common.GetRepoReference(image)
  1051  
  1052  			if common.IsDigest(ref) {
  1053  				return getCveResults(ref), nil
  1054  			}
  1055  
  1056  			repoMeta, _ := metaDB.GetRepoMeta(ctx, repo)
  1057  
  1058  			if _, ok := repoMeta.Tags[ref]; !ok {
  1059  				panic("unexpected tag '" + ref + "', test might be wrong")
  1060  			}
  1061  
  1062  			return getCveResults(repoMeta.Tags[ref].Digest), nil
  1063  		},
  1064  		GetCachedResultFn: func(digestStr string) map[string]cvemodel.CVE {
  1065  			return getCveResults(digestStr)
  1066  		},
  1067  		IsResultCachedFn: func(digestStr string) bool {
  1068  			return true
  1069  		},
  1070  	}
  1071  
  1072  	cveInfo := &cveinfo.BaseCveInfo{
  1073  		Log:     log,
  1074  		Scanner: scanner,
  1075  		MetaDB:  metaDB,
  1076  	}
  1077  
  1078  	Convey("Get CVE list for image ", t, func() {
  1079  		Convey("Unpaginated request to get all CVEs in an image", func() {
  1080  			pageInput := &gql_generated.PageInput{
  1081  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
  1082  			}
  1083  
  1084  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1085  				graphql.DefaultRecover)
  1086  
  1087  			dig := godigest.FromString("dig")
  1088  			repoWithDigestRef := fmt.Sprintf("repo@%s", dig)
  1089  
  1090  			_, err := getCVEListForImage(responseContext, repoWithDigestRef, cveInfo, pageInput, "", log)
  1091  			So(err, ShouldBeNil)
  1092  
  1093  			cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "", log)
  1094  			So(err, ShouldBeNil)
  1095  			So(*cveResult.Tag, ShouldEqual, "1.0.0")
  1096  
  1097  			expectedCves := []string{"CVE1", "CVE2", "CVE3", "CVE34"}
  1098  			So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
  1099  
  1100  			for _, cve := range cveResult.CVEList {
  1101  				So(expectedCves, ShouldContain, *cve.ID)
  1102  			}
  1103  
  1104  			// test searching CVE by id in results
  1105  			cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE3", log)
  1106  			So(err, ShouldBeNil)
  1107  			So(*cveResult.Tag, ShouldEqual, "1.0.0")
  1108  
  1109  			expectedCves = []string{"CVE3", "CVE34"}
  1110  			So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
  1111  
  1112  			for _, cve := range cveResult.CVEList {
  1113  				So(expectedCves, ShouldContain, *cve.ID)
  1114  			}
  1115  
  1116  			// test searching CVE by id in results - no matches
  1117  			cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE100", log)
  1118  			So(err, ShouldBeNil)
  1119  			So(*cveResult.Tag, ShouldEqual, "1.0.0")
  1120  			So(len(cveResult.CVEList), ShouldEqual, 0)
  1121  
  1122  			// test searching CVE by id in results - partial name
  1123  			cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "VE3", log)
  1124  			So(err, ShouldBeNil)
  1125  			So(*cveResult.Tag, ShouldEqual, "1.0.0")
  1126  
  1127  			expectedCves = []string{"CVE3", "CVE34"}
  1128  			So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
  1129  
  1130  			for _, cve := range cveResult.CVEList {
  1131  				So(expectedCves, ShouldContain, *cve.ID)
  1132  			}
  1133  
  1134  			// test searching CVE by title in results
  1135  			cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE", log)
  1136  			So(err, ShouldBeNil)
  1137  			So(*cveResult.Tag, ShouldEqual, "1.0.0")
  1138  
  1139  			expectedCves = []string{"CVE1", "CVE2", "CVE3"}
  1140  			So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
  1141  
  1142  			for _, cve := range cveResult.CVEList {
  1143  				So(expectedCves, ShouldContain, *cve.ID)
  1144  			}
  1145  
  1146  			cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.1", cveInfo, pageInput, "", log)
  1147  			So(err, ShouldBeNil)
  1148  			So(*cveResult.Tag, ShouldEqual, "1.0.1")
  1149  
  1150  			expectedCves = []string{"CVE2", "CVE3"}
  1151  			So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
  1152  
  1153  			for _, cve := range cveResult.CVEList {
  1154  				So(expectedCves, ShouldContain, *cve.ID)
  1155  			}
  1156  
  1157  			cveResult, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", log)
  1158  			So(err, ShouldBeNil)
  1159  			So(*cveResult.Tag, ShouldEqual, "1.1.0")
  1160  
  1161  			expectedCves = []string{"CVE3"}
  1162  			So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
  1163  
  1164  			for _, cve := range cveResult.CVEList {
  1165  				So(expectedCves, ShouldContain, *cve.ID)
  1166  			}
  1167  		})
  1168  
  1169  		Convey("paginated fail", func() {
  1170  			pageInput := &gql_generated.PageInput{
  1171  				Limit: ref(-1),
  1172  			}
  1173  
  1174  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1175  				graphql.DefaultRecover)
  1176  
  1177  			_, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", log)
  1178  			So(err, ShouldNotBeNil)
  1179  		})
  1180  	})
  1181  
  1182  	Convey("Get a list of images affected by a particular CVE ", t, func() {
  1183  		Convey("Unpaginated request", func() {
  1184  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1185  				graphql.DefaultRecover)
  1186  
  1187  			images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, nil, metaDB, log)
  1188  			So(err, ShouldBeNil)
  1189  
  1190  			expectedImages := []string{
  1191  				"repo1:1.0.0",
  1192  				"repo2:2.0.0",
  1193  			}
  1194  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1195  
  1196  			for _, image := range images.Results {
  1197  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1198  			}
  1199  
  1200  			images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, nil, metaDB, log)
  1201  			So(err, ShouldBeNil)
  1202  
  1203  			expectedImages = []string{
  1204  				"repo1:1.0.0", "repo1:1.0.1",
  1205  				"repo2:2.0.0", "repo2:2.0.1",
  1206  				"repo3:3.0.1",
  1207  			}
  1208  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1209  
  1210  			for _, image := range images.Results {
  1211  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1212  			}
  1213  
  1214  			images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, nil, metaDB, log)
  1215  			So(err, ShouldBeNil)
  1216  
  1217  			expectedImages = []string{
  1218  				"repo1:1.0.0", "repo1:1.0.1", "repo1:1.1.0", "repo1:latest",
  1219  				"repo2:2.0.0", "repo2:2.0.1", "repo2:2.1.0", "repo2:latest",
  1220  				"repo3:3.0.1", "repo3:3.1.0", "repo3:latest",
  1221  			}
  1222  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1223  
  1224  			for _, image := range images.Results {
  1225  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1226  			}
  1227  		})
  1228  
  1229  		Convey("paginated fail", func() {
  1230  			pageInput := &gql_generated.PageInput{
  1231  				Limit: ref(-1),
  1232  			}
  1233  
  1234  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1235  				graphql.DefaultRecover)
  1236  
  1237  			_, err = getImageListForCVE(responseContext, "repo1:1.1.0", cveInfo, &gql_generated.Filter{},
  1238  				pageInput, mocks.MetaDBMock{}, log)
  1239  			So(err, ShouldNotBeNil)
  1240  		})
  1241  
  1242  		Convey("context done", func() {
  1243  			pageInput := getPageInput(1, 0)
  1244  
  1245  			ctx, cancel := context.WithCancel(ctx)
  1246  			cancel()
  1247  
  1248  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1249  				graphql.DefaultRecover)
  1250  
  1251  			canceledScanner := scanner
  1252  
  1253  			canceledScanner.ScanImageFn = func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) {
  1254  				return nil, ctx.Err()
  1255  			}
  1256  
  1257  			cveInfo.Scanner = canceledScanner
  1258  
  1259  			defer func() {
  1260  				cveInfo.Scanner = scanner
  1261  			}()
  1262  
  1263  			_, err = getImageListForCVE(responseContext, "repo1:1.1.0", cveInfo, &gql_generated.Filter{},
  1264  				pageInput, metaDB, log)
  1265  			So(err, ShouldEqual, ctx.Err())
  1266  		})
  1267  
  1268  		Convey("Paginated requests", func() {
  1269  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1270  				graphql.DefaultRecover,
  1271  			)
  1272  
  1273  			pageInput := getPageInput(1, 0)
  1274  
  1275  			images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1276  			So(err, ShouldBeNil)
  1277  
  1278  			expectedImages := []string{
  1279  				"repo1:1.0.0",
  1280  			}
  1281  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1282  
  1283  			for _, image := range images.Results {
  1284  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1285  			}
  1286  
  1287  			pageInput = getPageInput(1, 1)
  1288  
  1289  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1290  			So(err, ShouldBeNil)
  1291  
  1292  			expectedImages = []string{
  1293  				"repo2:2.0.0",
  1294  			}
  1295  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1296  
  1297  			for _, image := range images.Results {
  1298  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1299  			}
  1300  
  1301  			pageInput = getPageInput(1, 2)
  1302  
  1303  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1304  			So(err, ShouldBeNil)
  1305  			So(len(images.Results), ShouldEqual, 0)
  1306  
  1307  			pageInput = getPageInput(1, 5)
  1308  
  1309  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1310  			So(err, ShouldBeNil)
  1311  			So(len(images.Results), ShouldEqual, 0)
  1312  
  1313  			pageInput = getPageInput(2, 0)
  1314  
  1315  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1316  			So(err, ShouldBeNil)
  1317  
  1318  			expectedImages = []string{
  1319  				"repo1:1.0.0",
  1320  				"repo2:2.0.0",
  1321  			}
  1322  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1323  
  1324  			for _, image := range images.Results {
  1325  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1326  			}
  1327  
  1328  			pageInput = getPageInput(5, 0)
  1329  
  1330  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1331  			So(err, ShouldBeNil)
  1332  
  1333  			expectedImages = []string{
  1334  				"repo1:1.0.0",
  1335  				"repo2:2.0.0",
  1336  			}
  1337  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1338  
  1339  			for _, image := range images.Results {
  1340  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1341  			}
  1342  
  1343  			pageInput = getPageInput(5, 1)
  1344  
  1345  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1346  			So(err, ShouldBeNil)
  1347  
  1348  			expectedImages = []string{
  1349  				"repo2:2.0.0",
  1350  			}
  1351  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1352  
  1353  			for _, image := range images.Results {
  1354  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1355  			}
  1356  
  1357  			pageInput = getPageInput(5, 2)
  1358  
  1359  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1360  			So(err, ShouldBeNil)
  1361  			So(len(images.Results), ShouldEqual, 0)
  1362  
  1363  			pageInput = getPageInput(5, 5)
  1364  
  1365  			images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
  1366  			So(err, ShouldBeNil)
  1367  			So(len(images.Results), ShouldEqual, 0)
  1368  
  1369  			pageInput = getPageInput(5, 0)
  1370  
  1371  			images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, pageInput, metaDB, log)
  1372  			So(err, ShouldBeNil)
  1373  
  1374  			expectedImages = []string{
  1375  				"repo1:1.0.0", "repo1:1.0.1",
  1376  				"repo2:2.0.0", "repo2:2.0.1",
  1377  				"repo3:3.0.1",
  1378  			}
  1379  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1380  
  1381  			for _, image := range images.Results {
  1382  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1383  			}
  1384  
  1385  			pageInput = getPageInput(5, 3)
  1386  
  1387  			images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, pageInput, metaDB, log)
  1388  			So(err, ShouldBeNil)
  1389  
  1390  			expectedImages = []string{
  1391  				"repo2:2.0.1",
  1392  				"repo3:3.0.1",
  1393  			}
  1394  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1395  
  1396  			for _, image := range images.Results {
  1397  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1398  			}
  1399  
  1400  			pageInput = getPageInput(5, 0)
  1401  
  1402  			images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, metaDB, log)
  1403  			So(err, ShouldBeNil)
  1404  
  1405  			expectedImages = []string{
  1406  				"repo1:1.0.0", "repo1:1.0.1", "repo1:1.1.0", "repo1:latest",
  1407  				"repo2:2.0.0",
  1408  			}
  1409  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1410  
  1411  			for _, image := range images.Results {
  1412  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1413  			}
  1414  
  1415  			pageInput = getPageInput(5, 5)
  1416  
  1417  			images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, metaDB, log)
  1418  			So(err, ShouldBeNil)
  1419  
  1420  			expectedImages = []string{
  1421  				"repo2:2.0.1", "repo2:2.1.0", "repo2:latest",
  1422  				"repo3:3.0.1", "repo3:3.1.0",
  1423  			}
  1424  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1425  
  1426  			for _, image := range images.Results {
  1427  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1428  			}
  1429  
  1430  			pageInput = getPageInput(5, 10)
  1431  
  1432  			images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, metaDB, log)
  1433  			So(err, ShouldBeNil)
  1434  
  1435  			expectedImages = []string{
  1436  				"repo3:latest",
  1437  			}
  1438  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1439  
  1440  			for _, image := range images.Results {
  1441  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1442  			}
  1443  
  1444  			amdFilter := &gql_generated.Filter{Arch: []*string{&AMD}}
  1445  			pageInput = getPageInput(5, 0)
  1446  
  1447  			images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, amdFilter, pageInput, metaDB, log)
  1448  			So(err, ShouldBeNil)
  1449  
  1450  			expectedImages = []string{
  1451  				"repo1:1.0.0", "repo1:1.0.1",
  1452  				"repo2:2.0.0", "repo2:2.0.1",
  1453  				"repo3:3.0.1",
  1454  			}
  1455  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1456  
  1457  			for _, image := range images.Results {
  1458  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1459  			}
  1460  
  1461  			pageInput = getPageInput(2, 2)
  1462  
  1463  			images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, amdFilter, pageInput, metaDB, log)
  1464  			So(err, ShouldBeNil)
  1465  
  1466  			expectedImages = []string{
  1467  				"repo2:2.0.0", "repo2:2.0.1",
  1468  			}
  1469  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1470  
  1471  			for _, image := range images.Results {
  1472  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1473  			}
  1474  		})
  1475  	})
  1476  
  1477  	Convey("Get a list of images where a particular CVE is fixed", t, func() {
  1478  		Convey("Unpaginated request", func() {
  1479  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1480  				graphql.DefaultRecover)
  1481  
  1482  			images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, nil, metaDB, log)
  1483  			So(err, ShouldBeNil)
  1484  
  1485  			expectedImages := []string{
  1486  				"repo1:1.0.1", "repo1:1.1.0", "repo1:latest",
  1487  			}
  1488  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1489  
  1490  			for _, image := range images.Results {
  1491  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1492  			}
  1493  
  1494  			images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, nil, metaDB, log)
  1495  			So(err, ShouldBeNil)
  1496  
  1497  			expectedImages = []string{
  1498  				"repo1:1.1.0", "repo1:latest",
  1499  			}
  1500  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1501  
  1502  			for _, image := range images.Results {
  1503  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1504  			}
  1505  
  1506  			images, err = getImageListWithCVEFixed(responseContext, "CVE3", "repo1", cveInfo, nil, nil, metaDB, log)
  1507  			So(err, ShouldBeNil)
  1508  			So(len(images.Results), ShouldEqual, 0)
  1509  		})
  1510  
  1511  		Convey("paginated fail", func() {
  1512  			pageInput := &gql_generated.PageInput{
  1513  				Limit: ref(-1),
  1514  			}
  1515  
  1516  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1517  				graphql.DefaultRecover)
  1518  
  1519  			_, err = getImageListWithCVEFixed(responseContext, "cve", "repo1:1.1.0", cveInfo, &gql_generated.Filter{},
  1520  				pageInput, mocks.MetaDBMock{
  1521  					GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1522  						return mTypes.RepoMeta{
  1523  							Tags: map[string]mTypes.Descriptor{
  1524  								"1.1.0": {
  1525  									Digest:    godigest.FromString("str").String(),
  1526  									MediaType: ispec.MediaTypeImageManifest,
  1527  								},
  1528  							},
  1529  						}, nil
  1530  					},
  1531  				}, log)
  1532  			So(err, ShouldNotBeNil)
  1533  		})
  1534  
  1535  		Convey("context done", func() {
  1536  			ctx, cancel := context.WithCancel(ctx)
  1537  			cancel()
  1538  
  1539  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1540  				graphql.DefaultRecover)
  1541  
  1542  			_, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, nil, metaDB, log)
  1543  			So(err, ShouldNotBeNil)
  1544  		})
  1545  
  1546  		Convey("Paginated requests", func() {
  1547  			responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  1548  				graphql.DefaultRecover,
  1549  			)
  1550  
  1551  			pageInput := getPageInput(1, 0)
  1552  
  1553  			images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1554  			So(err, ShouldBeNil)
  1555  
  1556  			expectedImages := []string{
  1557  				"repo1:1.0.1",
  1558  			}
  1559  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1560  
  1561  			for _, image := range images.Results {
  1562  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1563  			}
  1564  
  1565  			pageInput = getPageInput(1, 1)
  1566  
  1567  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1568  			So(err, ShouldBeNil)
  1569  
  1570  			expectedImages = []string{
  1571  				"repo1:1.1.0",
  1572  			}
  1573  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1574  
  1575  			for _, image := range images.Results {
  1576  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1577  			}
  1578  
  1579  			pageInput = getPageInput(1, 2)
  1580  
  1581  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1582  			So(err, ShouldBeNil)
  1583  
  1584  			expectedImages = []string{
  1585  				"repo1:latest",
  1586  			}
  1587  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1588  
  1589  			for _, image := range images.Results {
  1590  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1591  			}
  1592  
  1593  			pageInput = getPageInput(1, 3)
  1594  
  1595  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1596  			So(err, ShouldBeNil)
  1597  			So(len(images.Results), ShouldEqual, 0)
  1598  
  1599  			pageInput = getPageInput(1, 10)
  1600  
  1601  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1602  			So(err, ShouldBeNil)
  1603  			So(len(images.Results), ShouldEqual, 0)
  1604  
  1605  			pageInput = getPageInput(2, 0)
  1606  
  1607  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1608  			So(err, ShouldBeNil)
  1609  
  1610  			expectedImages = []string{
  1611  				"repo1:1.0.1", "repo1:1.1.0",
  1612  			}
  1613  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1614  
  1615  			for _, image := range images.Results {
  1616  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1617  			}
  1618  
  1619  			pageInput = getPageInput(2, 1)
  1620  
  1621  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1622  			So(err, ShouldBeNil)
  1623  
  1624  			expectedImages = []string{
  1625  				"repo1:1.1.0", "repo1:latest",
  1626  			}
  1627  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1628  
  1629  			for _, image := range images.Results {
  1630  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1631  			}
  1632  
  1633  			pageInput = getPageInput(2, 2)
  1634  
  1635  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1636  			So(err, ShouldBeNil)
  1637  
  1638  			expectedImages = []string{
  1639  				"repo1:latest",
  1640  			}
  1641  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1642  
  1643  			for _, image := range images.Results {
  1644  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1645  			}
  1646  
  1647  			pageInput = getPageInput(5, 0)
  1648  
  1649  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1650  			So(err, ShouldBeNil)
  1651  
  1652  			expectedImages = []string{
  1653  				"repo1:1.0.1", "repo1:1.1.0", "repo1:latest",
  1654  			}
  1655  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1656  
  1657  			for _, image := range images.Results {
  1658  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1659  			}
  1660  
  1661  			pageInput = getPageInput(5, 0)
  1662  
  1663  			images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1664  			So(err, ShouldBeNil)
  1665  
  1666  			expectedImages = []string{
  1667  				"repo1:1.1.0", "repo1:latest",
  1668  			}
  1669  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1670  
  1671  			for _, image := range images.Results {
  1672  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1673  			}
  1674  
  1675  			pageInput = getPageInput(5, 2)
  1676  
  1677  			images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, pageInput, metaDB, log)
  1678  			So(err, ShouldBeNil)
  1679  			So(len(images.Results), ShouldEqual, 0)
  1680  
  1681  			amdFilter := &gql_generated.Filter{Arch: []*string{&AMD}}
  1682  			armFilter := &gql_generated.Filter{Arch: []*string{&ARM}}
  1683  
  1684  			pageInput = getPageInput(3, 0)
  1685  
  1686  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, amdFilter, pageInput, metaDB, log)
  1687  			So(err, ShouldBeNil)
  1688  
  1689  			expectedImages = []string{"repo1:1.0.1"}
  1690  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1691  
  1692  			for _, image := range images.Results {
  1693  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1694  			}
  1695  
  1696  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, armFilter, pageInput, metaDB, log)
  1697  			So(err, ShouldBeNil)
  1698  
  1699  			expectedImages = []string{"repo1:1.1.0", "repo1:latest"}
  1700  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1701  
  1702  			for _, image := range images.Results {
  1703  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1704  			}
  1705  
  1706  			pageInput = getPageInput(1, 1)
  1707  
  1708  			images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, armFilter, pageInput, metaDB, log)
  1709  			So(err, ShouldBeNil)
  1710  
  1711  			expectedImages = []string{"repo1:latest"}
  1712  			So(len(images.Results), ShouldEqual, len(expectedImages))
  1713  
  1714  			for _, image := range images.Results {
  1715  				So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
  1716  			}
  1717  		})
  1718  	})
  1719  
  1720  	Convey("Errors for cve resolvers", t, func() {
  1721  		_, err := getImageListForCVE(
  1722  			ctx,
  1723  			"id",
  1724  			mocks.CveInfoMock{
  1725  				GetImageListForCVEFn: func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error) {
  1726  					return []cvemodel.TagInfo{}, ErrTestError
  1727  				},
  1728  			},
  1729  			nil,
  1730  			nil,
  1731  			mocks.MetaDBMock{
  1732  				GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
  1733  				) ([]mTypes.RepoMeta, error) {
  1734  					return []mTypes.RepoMeta{{}}, nil
  1735  				},
  1736  			},
  1737  			log,
  1738  		)
  1739  		So(err, ShouldNotBeNil)
  1740  	})
  1741  }
  1742  
  1743  func TestMockedDerivedImageList(t *testing.T) {
  1744  	Convey("MetaDB FilterTags error", t, func() {
  1745  		log := log.NewLogger("debug", "/dev/null")
  1746  
  1747  		image := CreateRandomImage()
  1748  		mockMetaDB := mocks.MetaDBMock{
  1749  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  1750  			) ([]mTypes.FullImageMeta, error) {
  1751  				return []mTypes.FullImageMeta{}, ErrTestError
  1752  			},
  1753  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1754  				return mTypes.RepoMeta{}, ErrTestError
  1755  			},
  1756  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  1757  				return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
  1758  			},
  1759  		}
  1760  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  1761  			graphql.DefaultRecover)
  1762  
  1763  		mockCve := mocks.CveInfoMock{}
  1764  		images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &gql_generated.PageInput{},
  1765  			mockCve, log)
  1766  		So(err, ShouldNotBeNil)
  1767  		So(images.Results, ShouldBeEmpty)
  1768  	})
  1769  
  1770  	Convey("paginated fail", t, func() {
  1771  		log := log.NewLogger("debug", "/dev/null")
  1772  		image := CreateRandomImage()
  1773  		pageInput := &gql_generated.PageInput{
  1774  			Limit: ref(-1),
  1775  		}
  1776  
  1777  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  1778  			graphql.DefaultRecover)
  1779  
  1780  		_, err := derivedImageList(responseContext, "repo1:1.0.1", nil,
  1781  			mocks.MetaDBMock{
  1782  				GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1783  					return mTypes.RepoMeta{
  1784  						Tags: map[string]mTypes.Descriptor{
  1785  							"1.0.1": {
  1786  								Digest:    image.DigestStr(),
  1787  								MediaType: ispec.MediaTypeImageManifest,
  1788  							},
  1789  						},
  1790  					}, nil
  1791  				},
  1792  				FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  1793  					return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
  1794  				},
  1795  			},
  1796  			pageInput,
  1797  			mocks.CveInfoMock{}, log)
  1798  		So(err, ShouldNotBeNil)
  1799  	})
  1800  
  1801  	//nolint: dupl
  1802  	Convey("MetaDB FilterTags no repo available", t, func() {
  1803  		log := log.NewLogger("debug", "/dev/null")
  1804  		image := CreateDefaultImage()
  1805  
  1806  		mockMetaDB := mocks.MetaDBMock{
  1807  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  1808  			) ([]mTypes.FullImageMeta, error) {
  1809  				return []mTypes.FullImageMeta{}, nil
  1810  			},
  1811  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1812  				return mTypes.RepoMeta{
  1813  					Name: "repo1",
  1814  					Tags: map[string]mTypes.Descriptor{
  1815  						"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  1816  					},
  1817  				}, nil
  1818  			},
  1819  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  1820  				return map[string]mTypes.ImageMeta{
  1821  					digests[0]: image.AsImageMeta(),
  1822  				}, nil
  1823  			},
  1824  			GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
  1825  				return image.AsImageMeta(), nil
  1826  			},
  1827  		}
  1828  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  1829  			graphql.DefaultRecover)
  1830  
  1831  		mockCve := mocks.CveInfoMock{}
  1832  		images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &gql_generated.PageInput{},
  1833  			mockCve, log)
  1834  		So(err, ShouldBeNil)
  1835  		So(images.Results, ShouldBeEmpty)
  1836  	})
  1837  
  1838  	//nolint: dupl
  1839  	Convey("derived image list working", t, func() {
  1840  		log := log.NewLogger("debug", "/dev/null")
  1841  		layer1 := []byte{10, 11, 10, 11}
  1842  		layer2 := []byte{11, 11, 11, 11}
  1843  		layer3 := []byte{10, 10, 10, 11}
  1844  		layer4 := []byte{13, 14, 15, 11}
  1845  
  1846  		image := CreateImageWith().
  1847  			LayerBlobs([][]byte{
  1848  				layer1,
  1849  				layer2,
  1850  				layer3,
  1851  			}).DefaultConfig().Build()
  1852  
  1853  		derivedImage := CreateImageWith().
  1854  			LayerBlobs([][]byte{
  1855  				layer1,
  1856  				layer2,
  1857  				layer3,
  1858  				layer4,
  1859  			}).DefaultConfig().Build()
  1860  
  1861  		imageMetaMap := map[string]mTypes.ImageMeta{
  1862  			image.DigestStr():        image.AsImageMeta(),
  1863  			derivedImage.DigestStr(): derivedImage.AsImageMeta(),
  1864  		}
  1865  
  1866  		mockMetaDB := mocks.MetaDBMock{
  1867  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1868  				return mTypes.RepoMeta{
  1869  					Name: "repo1",
  1870  					Tags: map[string]mTypes.Descriptor{
  1871  						"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  1872  					},
  1873  				}, nil
  1874  			},
  1875  			GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
  1876  				return imageMetaMap[digest.String()], nil
  1877  			},
  1878  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  1879  				result := map[string]mTypes.ImageMeta{}
  1880  
  1881  				for _, digest := range digests {
  1882  					result[digest] = imageMetaMap[digest]
  1883  				}
  1884  
  1885  				return result, nil
  1886  			},
  1887  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  1888  			) ([]mTypes.FullImageMeta, error) {
  1889  				fullImageMetaList := []mTypes.FullImageMeta{}
  1890  				repos := []mTypes.RepoMeta{{
  1891  					Name: "repo1",
  1892  					Tags: map[string]mTypes.Descriptor{
  1893  						"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  1894  						"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  1895  						"1.0.3": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  1896  					},
  1897  				}}
  1898  
  1899  				for _, repo := range repos {
  1900  					for tag, descriptor := range repo.Tags {
  1901  						if filterFunc(repo, imageMetaMap[descriptor.Digest]) {
  1902  							fullImageMetaList = append(fullImageMetaList,
  1903  								convert.GetFullImageMeta(tag, repo, imageMetaMap[descriptor.Digest]))
  1904  						}
  1905  					}
  1906  				}
  1907  
  1908  				return fullImageMetaList, nil
  1909  			},
  1910  		}
  1911  
  1912  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  1913  			graphql.DefaultRecover)
  1914  
  1915  		mockCve := mocks.CveInfoMock{}
  1916  
  1917  		Convey("valid derivedImageList, results not affected by pageInput", func() {
  1918  			images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &gql_generated.PageInput{},
  1919  				mockCve, log)
  1920  			So(err, ShouldBeNil)
  1921  			So(images.Results, ShouldNotBeEmpty)
  1922  			So(len(images.Results), ShouldEqual, 2)
  1923  		})
  1924  
  1925  		Convey("valid derivedImageList, results affected by pageInput", func() {
  1926  			pageInput := gql_generated.PageInput{
  1927  				Limit:  ref(1),
  1928  				Offset: ref(0),
  1929  				SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
  1930  			}
  1931  
  1932  			images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &pageInput,
  1933  				mockCve, log)
  1934  			So(err, ShouldBeNil)
  1935  			So(images.Results, ShouldNotBeEmpty)
  1936  			So(len(images.Results), ShouldEqual, 1)
  1937  		})
  1938  	})
  1939  }
  1940  
  1941  func TestMockedBaseImageList(t *testing.T) {
  1942  	Convey("MetaDB FilterTags error", t, func() {
  1943  		mockMetaDB := mocks.MetaDBMock{
  1944  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  1945  			) ([]mTypes.FullImageMeta, error) {
  1946  				return []mTypes.FullImageMeta{}, ErrTestError
  1947  			},
  1948  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1949  				return mTypes.RepoMeta{}, ErrTestError
  1950  			},
  1951  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  1952  				return map[string]mTypes.ImageMeta{}, ErrTestError
  1953  			},
  1954  		}
  1955  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  1956  			graphql.DefaultRecover)
  1957  
  1958  		mockCve := mocks.CveInfoMock{}
  1959  		images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB, &gql_generated.PageInput{},
  1960  			mockCve, log.NewLogger("debug", ""))
  1961  		So(err, ShouldNotBeNil)
  1962  		So(images.Results, ShouldBeEmpty)
  1963  	})
  1964  
  1965  	Convey("paginated fail", t, func() {
  1966  		image := CreateDefaultImage()
  1967  		pageInput := &gql_generated.PageInput{Limit: ref(-1)}
  1968  
  1969  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  1970  			graphql.DefaultRecover)
  1971  		_, err := baseImageList(responseContext, "repo1:1.0.2", nil,
  1972  			mocks.MetaDBMock{
  1973  				GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  1974  					return mTypes.RepoMeta{
  1975  						Tags: map[string]mTypes.Descriptor{
  1976  							"1.0.2": {
  1977  								Digest:    image.DigestStr(),
  1978  								MediaType: ispec.MediaTypeImageManifest,
  1979  							},
  1980  						},
  1981  					}, nil
  1982  				},
  1983  				FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  1984  					return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
  1985  				},
  1986  			},
  1987  			pageInput, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
  1988  		So(err, ShouldNotBeNil)
  1989  	})
  1990  
  1991  	//nolint: dupl
  1992  	Convey("MetaDB FilterTags no repo available", t, func() {
  1993  		image := CreateDefaultImage()
  1994  
  1995  		mockMetaDB := mocks.MetaDBMock{
  1996  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  1997  			) ([]mTypes.FullImageMeta, error) {
  1998  				return []mTypes.FullImageMeta{}, nil
  1999  			},
  2000  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  2001  				return mTypes.RepoMeta{
  2002  					Name: "repo1",
  2003  					Tags: map[string]mTypes.Descriptor{
  2004  						"1.0.2": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2005  					},
  2006  				}, nil
  2007  			},
  2008  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  2009  				return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
  2010  			},
  2011  		}
  2012  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  2013  			graphql.DefaultRecover)
  2014  
  2015  		mockCve := mocks.CveInfoMock{}
  2016  		images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB, &gql_generated.PageInput{},
  2017  			mockCve, log.NewLogger("debug", ""))
  2018  		So(err, ShouldBeNil)
  2019  		So(images.Results, ShouldBeEmpty)
  2020  	})
  2021  
  2022  	//nolint: dupl
  2023  	Convey("base image list working", t, func() {
  2024  		layer1 := []byte{10, 11, 10, 11}
  2025  		layer2 := []byte{11, 11, 11, 11}
  2026  		layer3 := []byte{10, 10, 10, 11}
  2027  		layer4 := []byte{13, 14, 15, 11}
  2028  
  2029  		image := CreateImageWith().
  2030  			LayerBlobs([][]byte{
  2031  				layer1,
  2032  				layer2,
  2033  				layer3,
  2034  			}).DefaultConfig().Build()
  2035  
  2036  		derivedImage := CreateImageWith().
  2037  			LayerBlobs([][]byte{
  2038  				layer1,
  2039  				layer2,
  2040  				layer3,
  2041  				layer4,
  2042  			}).DefaultConfig().Build()
  2043  
  2044  		imageMetaMap := map[string]mTypes.ImageMeta{
  2045  			image.DigestStr():        image.AsImageMeta(),
  2046  			derivedImage.DigestStr(): derivedImage.AsImageMeta(),
  2047  		}
  2048  
  2049  		mockMetaDB := mocks.MetaDBMock{
  2050  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  2051  				return mTypes.RepoMeta{
  2052  					Name: "repo1",
  2053  					Tags: map[string]mTypes.Descriptor{
  2054  						"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2055  					},
  2056  				}, nil
  2057  			},
  2058  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  2059  				return imageMetaMap, nil
  2060  			},
  2061  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  2062  			) ([]mTypes.FullImageMeta, error) {
  2063  				fullImageMetaList := []mTypes.FullImageMeta{}
  2064  				repos := []mTypes.RepoMeta{{
  2065  					Name: "repo1",
  2066  					Tags: map[string]mTypes.Descriptor{
  2067  						"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2068  						"1.0.3": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2069  						"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2070  					},
  2071  				}}
  2072  
  2073  				for _, repo := range repos {
  2074  					for tag, descriptor := range repo.Tags {
  2075  						if filterFunc(repo, imageMetaMap[descriptor.Digest]) {
  2076  							fullImageMetaList = append(fullImageMetaList,
  2077  								convert.GetFullImageMeta(tag, repo, imageMetaMap[descriptor.Digest]))
  2078  						}
  2079  					}
  2080  				}
  2081  
  2082  				return fullImageMetaList, nil
  2083  			},
  2084  		}
  2085  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  2086  			graphql.DefaultRecover)
  2087  
  2088  		mockCve := mocks.CveInfoMock{}
  2089  
  2090  		Convey("valid baseImageList, results not affected by pageInput", func() {
  2091  			images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB,
  2092  				&gql_generated.PageInput{}, mockCve, log.NewLogger("debug", ""))
  2093  			So(err, ShouldBeNil)
  2094  			So(images.Results, ShouldNotBeEmpty)
  2095  			So(len(images.Results), ShouldEqual, 2)
  2096  			expectedTags := []string{"1.0.1", "1.0.3"}
  2097  			So(expectedTags, ShouldContain, *images.Results[0].Tag)
  2098  			So(expectedTags, ShouldContain, *images.Results[1].Tag)
  2099  		})
  2100  
  2101  		Convey("valid baseImageList, results affected by pageInput", func() {
  2102  			limit := 1
  2103  			offset := 0
  2104  			sortCriteria := gql_generated.SortCriteriaAlphabeticAsc
  2105  			pageInput := gql_generated.PageInput{
  2106  				Limit:  &limit,
  2107  				Offset: &offset,
  2108  				SortBy: &sortCriteria,
  2109  			}
  2110  
  2111  			images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB,
  2112  				&pageInput, mockCve, log.NewLogger("debug", ""))
  2113  			So(err, ShouldBeNil)
  2114  			So(images.Results, ShouldNotBeEmpty)
  2115  			So(len(images.Results), ShouldEqual, limit)
  2116  			So(*images.Results[0].Tag, ShouldEqual, "1.0.1")
  2117  		})
  2118  	})
  2119  
  2120  	//nolint: dupl
  2121  	Convey("filterTags working, no base image list found", t, func() {
  2122  		layer1 := []byte{10, 11, 10, 11}
  2123  		layer2 := []byte{11, 11, 11, 11}
  2124  		layer3 := []byte{10, 10, 10, 11}
  2125  		layer4 := []byte{13, 14, 15, 11}
  2126  
  2127  		image := CreateImageWith().
  2128  			LayerBlobs([][]byte{
  2129  				layer1,
  2130  				layer2,
  2131  				layer3,
  2132  			}).DefaultConfig().Build()
  2133  
  2134  		derivedImage := CreateImageWith().
  2135  			LayerBlobs([][]byte{
  2136  				layer4,
  2137  			}).DefaultConfig().Build()
  2138  
  2139  		imageMetaMap := map[string]mTypes.ImageMeta{
  2140  			image.DigestStr():        image.AsImageMeta(),
  2141  			derivedImage.DigestStr(): derivedImage.AsImageMeta(),
  2142  		}
  2143  
  2144  		mockMetaDB := mocks.MetaDBMock{
  2145  			GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
  2146  				return mTypes.RepoMeta{
  2147  					Name: "repo1",
  2148  					Tags: map[string]mTypes.Descriptor{
  2149  						"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2150  					},
  2151  				}, nil
  2152  			},
  2153  			FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
  2154  				return imageMetaMap, nil
  2155  			},
  2156  			FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
  2157  			) ([]mTypes.FullImageMeta, error) {
  2158  				fullImageMetaList := []mTypes.FullImageMeta{}
  2159  				repos := []mTypes.RepoMeta{{
  2160  					Name: "repo1",
  2161  					Tags: map[string]mTypes.Descriptor{
  2162  						"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2163  						"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
  2164  					},
  2165  				}}
  2166  
  2167  				for _, repo := range repos {
  2168  					for tag, descriptor := range repo.Tags {
  2169  						if filterFunc(repo, imageMetaMap[descriptor.Digest]) {
  2170  							fullImageMetaList = append(fullImageMetaList,
  2171  								convert.GetFullImageMeta(tag, repo, imageMetaMap[descriptor.Digest]))
  2172  						}
  2173  					}
  2174  				}
  2175  
  2176  				return fullImageMetaList, nil
  2177  			},
  2178  		}
  2179  		responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
  2180  			graphql.DefaultRecover)
  2181  
  2182  		mockCve := mocks.CveInfoMock{}
  2183  		images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB, &gql_generated.PageInput{},
  2184  			mockCve, log.NewLogger("debug", ""))
  2185  		So(err, ShouldBeNil)
  2186  		So(images.Results, ShouldBeEmpty)
  2187  	})
  2188  }
  2189  
  2190  func TestExpandedRepoInfoErrors(t *testing.T) {
  2191  	log := log.NewLogger("debug", "")
  2192  
  2193  	Convey("Access error", t, func() {
  2194  		userAc := reqCtx.NewUserAccessControl()
  2195  		userAc.SetUsername("user")
  2196  		userAc.SetGlobPatterns("read", map[string]bool{
  2197  			"repo": false,
  2198  		})
  2199  
  2200  		ctx := userAc.DeriveContext(context.Background())
  2201  
  2202  		responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
  2203  			graphql.DefaultRecover)
  2204  
  2205  		_, err := expandedRepoInfo(responseContext, "repo", mocks.MetaDBMock{}, mocks.CveInfoMock{}, log)
  2206  		So(err, ShouldBeNil)
  2207  	})
  2208  }
  2209  
  2210  func ref[T any](input T) *T {
  2211  	ref := input
  2212  
  2213  	return &ref
  2214  }
  2215  
  2216  func getPageInput(limit int, offset int) *gql_generated.PageInput {
  2217  	sortCriteria := gql_generated.SortCriteriaAlphabeticAsc
  2218  
  2219  	return &gql_generated.PageInput{
  2220  		Limit:  &limit,
  2221  		Offset: &offset,
  2222  		SortBy: &sortCriteria,
  2223  	}
  2224  }