zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/cli/client/image_cmd_internal_test.go (about)

     1  //go:build search
     2  // +build search
     3  
     4  package client
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"log"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strings"
    17  	"sync"
    18  	"testing"
    19  	"time"
    20  
    21  	godigest "github.com/opencontainers/go-digest"
    22  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    23  	. "github.com/smartystreets/goconvey/convey"
    24  
    25  	zerr "zotregistry.io/zot/errors"
    26  	"zotregistry.io/zot/pkg/api"
    27  	"zotregistry.io/zot/pkg/api/config"
    28  	"zotregistry.io/zot/pkg/common"
    29  	extconf "zotregistry.io/zot/pkg/extensions/config"
    30  	stypes "zotregistry.io/zot/pkg/storage/types"
    31  	test "zotregistry.io/zot/pkg/test/common"
    32  	. "zotregistry.io/zot/pkg/test/image-utils"
    33  )
    34  
    35  func TestSearchImageCmd(t *testing.T) {
    36  	Convey("Test image help", t, func() {
    37  		args := []string{"--help"}
    38  		configPath := makeConfigFile("")
    39  		defer os.Remove(configPath)
    40  		cmd := NewImageCommand(new(mockService))
    41  		buff := bytes.NewBufferString("")
    42  		cmd.SetOut(buff)
    43  		cmd.SetErr(buff)
    44  		cmd.SetArgs(args)
    45  		err := cmd.Execute()
    46  		So(buff.String(), ShouldContainSubstring, "Usage")
    47  		So(err, ShouldBeNil)
    48  		Convey("with the shorthand", func() {
    49  			args[0] = "-h"
    50  			configPath := makeConfigFile("")
    51  			defer os.Remove(configPath)
    52  			cmd := NewImageCommand(new(mockService))
    53  			buff := bytes.NewBufferString("")
    54  			cmd.SetOut(buff)
    55  			cmd.SetErr(buff)
    56  			cmd.SetArgs(args)
    57  			err := cmd.Execute()
    58  			So(buff.String(), ShouldContainSubstring, "Usage")
    59  			So(err, ShouldBeNil)
    60  		})
    61  	})
    62  
    63  	Convey("Test image no url", t, func() {
    64  		args := []string{"name", "dummyIdRandom", "--config", "imagetest"}
    65  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
    66  		defer os.Remove(configPath)
    67  		cmd := NewImageCommand(new(mockService))
    68  		buff := bytes.NewBufferString("")
    69  		cmd.SetOut(buff)
    70  		cmd.SetErr(buff)
    71  		cmd.SetArgs(args)
    72  		err := cmd.Execute()
    73  		So(err, ShouldNotBeNil)
    74  		So(errors.Is(err, zerr.ErrNoURLProvided), ShouldBeTrue)
    75  	})
    76  
    77  	Convey("Test image invalid home directory", t, func() {
    78  		args := []string{"name", "dummyImageName", "--config", "imagetest"}
    79  
    80  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`)
    81  		defer os.Remove(configPath)
    82  
    83  		err := os.Setenv("HOME", "nonExistentDirectory")
    84  		if err != nil {
    85  			panic(err)
    86  		}
    87  
    88  		cmd := NewImageCommand(new(mockService))
    89  		buff := bytes.NewBufferString("")
    90  		cmd.SetOut(buff)
    91  		cmd.SetErr(buff)
    92  		cmd.SetArgs(args)
    93  		err = cmd.Execute()
    94  		So(err, ShouldNotBeNil)
    95  
    96  		home, err := os.UserHomeDir()
    97  		if err != nil {
    98  			panic(err)
    99  		}
   100  		err = os.Setenv("HOME", home)
   101  		if err != nil {
   102  			log.Fatal(err)
   103  		}
   104  	})
   105  
   106  	Convey("Test image no params", t, func() {
   107  		args := []string{"--url", "someUrl"}
   108  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
   109  		defer os.Remove(configPath)
   110  		cmd := NewImageCommand(new(mockService))
   111  		buff := bytes.NewBufferString("")
   112  		cmd.SetOut(buff)
   113  		cmd.SetErr(buff)
   114  		cmd.SetArgs(args)
   115  		err := cmd.Execute()
   116  		So(err, ShouldBeNil)
   117  	})
   118  
   119  	Convey("Test image invalid url", t, func() {
   120  		args := []string{"name", "dummyImageName", "--url", "invalidUrl"}
   121  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
   122  		defer os.Remove(configPath)
   123  		cmd := NewImageCommand(new(searchService))
   124  		buff := bytes.NewBufferString("")
   125  		cmd.SetOut(buff)
   126  		cmd.SetErr(buff)
   127  		cmd.SetArgs(args)
   128  		err := cmd.Execute()
   129  		So(err, ShouldNotBeNil)
   130  		So(strings.Contains(err.Error(), zerr.ErrInvalidURL.Error()), ShouldBeTrue)
   131  		So(buff.String(), ShouldContainSubstring, "invalid URL format")
   132  	})
   133  
   134  	Convey("Test image invalid url port", t, func() {
   135  		args := []string{"name", "dummyImageName", "--url", "http://localhost:99999"}
   136  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
   137  		defer os.Remove(configPath)
   138  		cmd := NewImageCommand(new(searchService))
   139  		buff := bytes.NewBufferString("")
   140  		cmd.SetOut(buff)
   141  		cmd.SetErr(buff)
   142  		cmd.SetArgs(args)
   143  		err := cmd.Execute()
   144  		So(err, ShouldNotBeNil)
   145  		So(buff.String(), ShouldContainSubstring, "invalid port")
   146  
   147  		Convey("without flags", func() {
   148  			args := []string{"list", "--url", "http://localhost:99999"}
   149  			configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
   150  			defer os.Remove(configPath)
   151  			cmd := NewImageCommand(new(searchService))
   152  			buff := bytes.NewBufferString("")
   153  			cmd.SetOut(buff)
   154  			cmd.SetErr(buff)
   155  			cmd.SetArgs(args)
   156  			err = cmd.Execute()
   157  			So(err, ShouldNotBeNil)
   158  			So(buff.String(), ShouldContainSubstring, "invalid port")
   159  		})
   160  	})
   161  
   162  	Convey("Test image unreachable", t, func() {
   163  		args := []string{"name", "dummyImageName", "--url", "http://localhost:9999"}
   164  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
   165  		defer os.Remove(configPath)
   166  		cmd := NewImageCommand(new(searchService))
   167  		buff := bytes.NewBufferString("")
   168  		cmd.SetOut(buff)
   169  		cmd.SetErr(buff)
   170  		cmd.SetArgs(args)
   171  		err := cmd.Execute()
   172  		So(err, ShouldNotBeNil)
   173  	})
   174  
   175  	Convey("Test image url from config", t, func() {
   176  		args := []string{"name", "dummyImageName", "--config", "imagetest"}
   177  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`)
   178  		defer os.Remove(configPath)
   179  		cmd := NewImageCommand(new(mockService))
   180  		buff := bytes.NewBufferString("")
   181  		cmd.SetOut(buff)
   182  		cmd.SetErr(buff)
   183  		cmd.SetArgs(args)
   184  		err := cmd.Execute()
   185  		space := regexp.MustCompile(`\s+`)
   186  		str := space.ReplaceAllString(buff.String(), " ")
   187  		So(strings.TrimSpace(str), ShouldEqual,
   188  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
   189  		So(err, ShouldBeNil)
   190  	})
   191  
   192  	Convey("Test image by name", t, func() {
   193  		args := []string{"name", "dummyImageName", "--url", "http://127.0.0.1:8080"}
   194  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`)
   195  		defer os.Remove(configPath)
   196  		imageCmd := NewImageCommand(new(mockService))
   197  		buff := &bytes.Buffer{}
   198  		imageCmd.SetOut(buff)
   199  		imageCmd.SetErr(buff)
   200  		imageCmd.SetArgs(args)
   201  		err := imageCmd.Execute()
   202  		space := regexp.MustCompile(`\s+`)
   203  		str := space.ReplaceAllString(buff.String(), " ")
   204  		So(strings.TrimSpace(str), ShouldEqual,
   205  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
   206  		So(err, ShouldBeNil)
   207  	})
   208  
   209  	Convey("Test image by digest", t, func() {
   210  		searchConfig := getTestSearchConfig("http://127.0.0.1:8080", new(mockService))
   211  		buff := &bytes.Buffer{}
   212  		searchConfig.ResultWriter = buff
   213  		err := SearchImagesByDigest(searchConfig, "6e2f80bf")
   214  		So(err, ShouldBeNil)
   215  
   216  		space := regexp.MustCompile(`\s+`)
   217  		str := space.ReplaceAllString(buff.String(), " ")
   218  		So(strings.TrimSpace(str), ShouldEqual,
   219  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB")
   220  		So(err, ShouldBeNil)
   221  	})
   222  }
   223  
   224  func TestListRepos(t *testing.T) {
   225  	searchConfig := getTestSearchConfig("https://test-url.com", new(mockService))
   226  
   227  	Convey("Test listing repositories", t, func() {
   228  		buff := &bytes.Buffer{}
   229  		searchConfig.ResultWriter = buff
   230  		err := SearchRepos(searchConfig)
   231  		So(err, ShouldBeNil)
   232  	})
   233  
   234  	Convey("Test listing repositories with debug flag", t, func() {
   235  		args := []string{"list", "--config", "config-test", "--debug"}
   236  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`)
   237  		defer os.Remove(configPath)
   238  		cmd := NewRepoCommand(new(searchService))
   239  
   240  		buff := bytes.NewBufferString("")
   241  		cmd.SetOut(buff)
   242  		cmd.SetErr(buff)
   243  		cmd.SetArgs(args)
   244  		err := cmd.Execute()
   245  		So(err, ShouldNotBeNil)
   246  		space := regexp.MustCompile(`\s+`)
   247  		str := space.ReplaceAllString(buff.String(), " ")
   248  		actual := strings.TrimSpace(str)
   249  		So(actual, ShouldContainSubstring, "GET")
   250  	})
   251  
   252  	Convey("Test error on home directory", t, func() {
   253  		args := []string{"list", "--config", "config-test"}
   254  
   255  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`)
   256  		defer os.Remove(configPath)
   257  
   258  		err := os.Setenv("HOME", "nonExistentDirectory")
   259  		if err != nil {
   260  			panic(err)
   261  		}
   262  
   263  		cmd := NewRepoCommand(new(mockService))
   264  		buff := bytes.NewBufferString("")
   265  		cmd.SetOut(buff)
   266  		cmd.SetErr(buff)
   267  		cmd.SetArgs(args)
   268  		err = cmd.Execute()
   269  		So(err, ShouldNotBeNil)
   270  
   271  		home, err := os.UserHomeDir()
   272  		if err != nil {
   273  			panic(err)
   274  		}
   275  		err = os.Setenv("HOME", home)
   276  		if err != nil {
   277  			log.Fatal(err)
   278  		}
   279  	})
   280  
   281  	Convey("Test listing repositories error", t, func() {
   282  		args := []string{"list", "--config", "config-test"}
   283  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test",
   284          	"url":"https://invalid.invalid","showspinner":false}]}`)
   285  		defer os.Remove(configPath)
   286  		cmd := NewRepoCommand(new(searchService))
   287  		buff := bytes.NewBufferString("")
   288  		cmd.SetOut(buff)
   289  		cmd.SetErr(buff)
   290  		cmd.SetArgs(args)
   291  		err := cmd.Execute()
   292  		So(err, ShouldNotBeNil)
   293  	})
   294  
   295  	Convey("Test unable to get config value", t, func() {
   296  		args := []string{"list", "--config", "config-test-nonexistent"}
   297  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`)
   298  		defer os.Remove(configPath)
   299  		cmd := NewRepoCommand(new(mockService))
   300  		buff := bytes.NewBufferString("")
   301  		cmd.SetOut(buff)
   302  		cmd.SetErr(buff)
   303  		cmd.SetArgs(args)
   304  		err := cmd.Execute()
   305  		So(err, ShouldNotBeNil)
   306  	})
   307  
   308  	Convey("Test error - no url provided", t, func() {
   309  		args := []string{"list", "--config", "config-test"}
   310  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"","showspinner":false}]}`)
   311  		defer os.Remove(configPath)
   312  		cmd := NewRepoCommand(new(mockService))
   313  		buff := bytes.NewBufferString("")
   314  		cmd.SetOut(buff)
   315  		cmd.SetErr(buff)
   316  		cmd.SetArgs(args)
   317  		err := cmd.Execute()
   318  		So(err, ShouldNotBeNil)
   319  	})
   320  
   321  	Convey("Test error - spinner config invalid", t, func() {
   322  		args := []string{"list", "--config", "config-test"}
   323  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test",
   324         		"url":"https://test-url.com","showspinner":invalid}]}`)
   325  		defer os.Remove(configPath)
   326  		cmd := NewRepoCommand(new(mockService))
   327  		buff := bytes.NewBufferString("")
   328  		cmd.SetOut(buff)
   329  		cmd.SetErr(buff)
   330  		cmd.SetArgs(args)
   331  		err := cmd.Execute()
   332  		So(err, ShouldNotBeNil)
   333  	})
   334  
   335  	Convey("Test error - verifyTLSConfig fails", t, func() {
   336  		args := []string{"list", "--config", "config-test"}
   337  		configPath := makeConfigFile(`{"configs":[{"_name":"config-test",
   338          	"verify-tls":"invalid", "url":"https://test-url.com","showspinner":false}]}`)
   339  		defer os.Remove(configPath)
   340  		cmd := NewRepoCommand(new(mockService))
   341  		buff := bytes.NewBufferString("")
   342  		cmd.SetOut(buff)
   343  		cmd.SetErr(buff)
   344  		cmd.SetArgs(args)
   345  		err := cmd.Execute()
   346  		So(err, ShouldNotBeNil)
   347  	})
   348  }
   349  
   350  func TestOutputFormat(t *testing.T) {
   351  	Convey("Test text", t, func() {
   352  		args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "text"}
   353  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`)
   354  		defer os.Remove(configPath)
   355  		cmd := NewImageCommand(new(mockService))
   356  		buff := bytes.NewBufferString("")
   357  		cmd.SetOut(buff)
   358  		cmd.SetErr(buff)
   359  		cmd.SetArgs(args)
   360  		err := cmd.Execute()
   361  		space := regexp.MustCompile(`\s+`)
   362  		str := space.ReplaceAllString(buff.String(), " ")
   363  		So(strings.TrimSpace(str), ShouldEqual,
   364  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
   365  		So(err, ShouldBeNil)
   366  	})
   367  
   368  	Convey("Test json", t, func() {
   369  		args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "json"}
   370  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`)
   371  		defer os.Remove(configPath)
   372  		cmd := NewImageCommand(new(mockService))
   373  		buff := bytes.NewBufferString("")
   374  		cmd.SetOut(buff)
   375  		cmd.SetErr(buff)
   376  		cmd.SetArgs(args)
   377  		err := cmd.Execute()
   378  		// Output is supposed to be in json lines format, keep all spaces as is for verification
   379  		So(buff.String(), ShouldEqual, `{"repoName":"dummyImageName","tag":"tag",`+
   380  			`"digest":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",`+
   381  			`"mediaType":"application/vnd.oci.image.manifest.v1+json",`+
   382  			`"manifests":[{"digest":"sha256:6e2f80bf9cfaabad474fbaf8ad68fdb652f776ea80b63492ecca404e5f6446a6",`+
   383  			`"configDigest":"sha256:4c10985c40365538426f2ba8cf0c21384a7769be502a550dcc0601b3736625e0",`+
   384  			`"lastUpdated":"0001-01-01T00:00:00Z","size":"123445","platform":{"os":"os","arch":"arch",`+
   385  			`"variant":""},"isSigned":false,"downloadCount":0,`+
   386  			`"layers":[{"size":"","digest":"sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6",`+
   387  			`"score":0}],"history":null,"vulnerabilities":{"maxSeverity":"","count":0},`+
   388  			`"referrers":null,"artifactType":"","signatureInfo":null}],"size":"123445",`+
   389  			`"downloadCount":0,"lastUpdated":"0001-01-01T00:00:00Z","description":"","isSigned":false,"licenses":"",`+
   390  			`"labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",`+
   391  			`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}`+"\n")
   392  		So(err, ShouldBeNil)
   393  	})
   394  
   395  	Convey("Test yaml", t, func() {
   396  		args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "yaml"}
   397  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`)
   398  		defer os.Remove(configPath)
   399  		cmd := NewImageCommand(new(mockService))
   400  		buff := bytes.NewBufferString("")
   401  		cmd.SetOut(buff)
   402  		cmd.SetErr(buff)
   403  		cmd.SetArgs(args)
   404  		err := cmd.Execute()
   405  		space := regexp.MustCompile(`\s+`)
   406  		str := space.ReplaceAllString(buff.String(), " ")
   407  		So(
   408  			strings.TrimSpace(str),
   409  			ShouldEqual,
   410  			`--- reponame: dummyImageName tag: tag `+
   411  				`digest: sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 `+
   412  				`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - `+
   413  				`digest: sha256:6e2f80bf9cfaabad474fbaf8ad68fdb652f776ea80b63492ecca404e5f6446a6 `+
   414  				`configdigest: sha256:4c10985c40365538426f2ba8cf0c21384a7769be502a550dcc0601b3736625e0 `+
   415  				`lastupdated: 0001-01-01T00:00:00Z size: "123445" platform: os: os arch: arch variant: "" `+
   416  				`issigned: false downloadcount: 0 layers: - size: "" `+
   417  				`digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 score: 0 `+
   418  				`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" `+
   419  				`signatureinfo: [] size: "123445" downloadcount: 0 `+
   420  				`lastupdated: 0001-01-01T00:00:00Z description: "" issigned: false licenses: "" labels: "" `+
   421  				`title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: "" `+
   422  				`count: 0 referrers: [] signatureinfo: []`,
   423  		)
   424  		So(err, ShouldBeNil)
   425  
   426  		Convey("Test yml", func() {
   427  			args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "yml"}
   428  			configPath := makeConfigFile(
   429  				`{"configs":[{"_name":"imagetest",` +
   430  					`"url":"https://test-url.com","showspinner":false}]}`,
   431  			)
   432  			defer os.Remove(configPath)
   433  			cmd := NewImageCommand(new(mockService))
   434  			buff := bytes.NewBufferString("")
   435  			cmd.SetOut(buff)
   436  			cmd.SetErr(buff)
   437  			cmd.SetArgs(args)
   438  			err := cmd.Execute()
   439  			space := regexp.MustCompile(`\s+`)
   440  			str := space.ReplaceAllString(buff.String(), " ")
   441  			So(
   442  				strings.TrimSpace(str),
   443  				ShouldEqual,
   444  				`--- reponame: dummyImageName tag: tag `+
   445  					`digest: sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 `+
   446  					`mediatype: application/vnd.oci.image.manifest.v1+json `+
   447  					`manifests: - digest: sha256:6e2f80bf9cfaabad474fbaf8ad68fdb652f776ea80b63492ecca404e5f6446a6 `+
   448  					`configdigest: sha256:4c10985c40365538426f2ba8cf0c21384a7769be502a550dcc0601b3736625e0 `+
   449  					`lastupdated: 0001-01-01T00:00:00Z size: "123445" platform: os: os arch: arch variant: "" `+
   450  					`issigned: false downloadcount: 0 layers: - size: "" `+
   451  					`digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 score: 0 `+
   452  					`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" `+
   453  					`signatureinfo: [] size: "123445" downloadcount: 0 `+
   454  					`lastupdated: 0001-01-01T00:00:00Z description: "" issigned: false licenses: "" labels: "" `+
   455  					`title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: `+
   456  					`"" count: 0 referrers: [] signatureinfo: []`,
   457  			)
   458  			So(err, ShouldBeNil)
   459  		})
   460  	})
   461  
   462  	Convey("Test invalid", t, func() {
   463  		args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "random"}
   464  		configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`)
   465  		defer os.Remove(configPath)
   466  		cmd := NewImageCommand(new(mockService))
   467  		buff := bytes.NewBufferString("")
   468  		cmd.SetOut(buff)
   469  		cmd.SetErr(buff)
   470  		cmd.SetArgs(args)
   471  		err := cmd.Execute()
   472  		So(err, ShouldNotBeNil)
   473  		So(buff.String(), ShouldContainSubstring, "invalid output format")
   474  	})
   475  }
   476  
   477  func TestImagesCommandGQL(t *testing.T) {
   478  	port := test.GetFreePort()
   479  	baseURL := test.GetBaseURL(port)
   480  	conf := config.New()
   481  	conf.HTTP.Port = port
   482  
   483  	defaultVal := true
   484  	conf.Extensions = &extconf.ExtensionConfig{
   485  		Search: &extconf.SearchConfig{
   486  			BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
   487  		},
   488  	}
   489  	ctlr := api.NewController(conf)
   490  	ctlr.Config.Storage.RootDirectory = t.TempDir()
   491  	cm := test.NewControllerManager(ctlr)
   492  
   493  	cm.StartAndWait(conf.HTTP.Port)
   494  	defer cm.StopServer()
   495  
   496  	Convey("commands with gql", t, func() {
   497  		err := removeLocalStorageContents(ctlr.StoreController.DefaultStore)
   498  		So(err, ShouldBeNil)
   499  
   500  		Convey("base and derived command", func() {
   501  			baseImage := CreateImageWith().LayerBlobs(
   502  				[][]byte{{1, 2, 3}, {11, 22, 33}},
   503  			).DefaultConfig().Build()
   504  
   505  			derivedImage := CreateImageWith().LayerBlobs(
   506  				[][]byte{{1, 2, 3}, {11, 22, 33}, {44, 55, 66}},
   507  			).DefaultConfig().Build()
   508  
   509  			err := UploadImage(baseImage, baseURL, "repo", "base")
   510  			So(err, ShouldBeNil)
   511  
   512  			err = UploadImage(derivedImage, baseURL, "repo", "derived")
   513  			So(err, ShouldBeNil)
   514  
   515  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   516  				baseURL))
   517  			defer os.Remove(configPath)
   518  			args := []string{"base", "repo:derived", "--config", "imagetest"}
   519  
   520  			cmd := NewImageCommand(NewSearchService())
   521  			buff := bytes.NewBufferString("")
   522  			cmd.SetOut(buff)
   523  			cmd.SetErr(buff)
   524  			cmd.SetArgs(args)
   525  			err = cmd.Execute()
   526  			So(err, ShouldBeNil)
   527  			space := regexp.MustCompile(`\s+`)
   528  			str := space.ReplaceAllString(buff.String(), " ")
   529  			actual := strings.TrimSpace(str)
   530  			So(actual, ShouldContainSubstring, "repo base linux/amd64 df554ddd false 699B")
   531  			args = []string{"derived", "repo:base", "--config", "imagetest"}
   532  
   533  			cmd = NewImageCommand(NewSearchService())
   534  			buff = bytes.NewBufferString("")
   535  			cmd.SetOut(buff)
   536  			cmd.SetErr(buff)
   537  			cmd.SetArgs(args)
   538  			err = cmd.Execute()
   539  			So(err, ShouldBeNil)
   540  			str = space.ReplaceAllString(buff.String(), " ")
   541  			actual = strings.TrimSpace(str)
   542  			So(actual, ShouldContainSubstring, "repo derived linux/amd64 79f4b82e false 854B")
   543  		})
   544  
   545  		Convey("base and derived command errors", func() {
   546  			// too many parameters
   547  			buff := bytes.NewBufferString("")
   548  			args := []string{"too", "many", "args", "--config", "imagetest"}
   549  			cmd := NewImageBaseCommand(NewSearchService())
   550  			cmd.SetOut(buff)
   551  			cmd.SetErr(buff)
   552  			cmd.SetArgs(args)
   553  			err := cmd.Execute()
   554  			So(err, ShouldNotBeNil)
   555  
   556  			cmd = NewImageDerivedCommand(NewSearchService())
   557  			cmd.SetOut(buff)
   558  			cmd.SetErr(buff)
   559  			cmd.SetArgs(args)
   560  			err = cmd.Execute()
   561  			So(err, ShouldNotBeNil)
   562  
   563  			// bad input
   564  			buff = bytes.NewBufferString("")
   565  			args = []string{"only-repo"}
   566  			cmd = NewImageBaseCommand(NewSearchService())
   567  			cmd.SetOut(buff)
   568  			cmd.SetErr(buff)
   569  			cmd.SetArgs(args)
   570  			err = cmd.Execute()
   571  			So(err, ShouldNotBeNil)
   572  
   573  			cmd = NewImageDerivedCommand(NewSearchService())
   574  			cmd.SetOut(buff)
   575  			cmd.SetErr(buff)
   576  			cmd.SetArgs(args)
   577  			err = cmd.Execute()
   578  			So(err, ShouldNotBeNil)
   579  
   580  			// no url
   581  			buff = bytes.NewBufferString("")
   582  			args = []string{"repo:tag"}
   583  			cmd = NewImageBaseCommand(NewSearchService())
   584  			cmd.SetOut(buff)
   585  			cmd.SetErr(buff)
   586  			cmd.SetArgs(args)
   587  			err = cmd.Execute()
   588  			So(err, ShouldNotBeNil)
   589  
   590  			cmd = NewImageDerivedCommand(NewSearchService())
   591  			cmd.SetOut(buff)
   592  			cmd.SetErr(buff)
   593  			cmd.SetArgs(args)
   594  			err = cmd.Execute()
   595  			So(err, ShouldNotBeNil)
   596  		})
   597  
   598  		Convey("digest command", func() {
   599  			image := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
   600  
   601  			err := UploadImage(image, baseURL, "repo", "img")
   602  			So(err, ShouldBeNil)
   603  
   604  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   605  				baseURL))
   606  			defer os.Remove(configPath)
   607  			args := []string{"digest", image.DigestStr(), "--config", "imagetest"}
   608  
   609  			cmd := NewImageCommand(NewSearchService())
   610  			buff := bytes.NewBufferString("")
   611  			cmd.SetOut(buff)
   612  			cmd.SetErr(buff)
   613  			cmd.SetArgs(args)
   614  			err = cmd.Execute()
   615  			So(err, ShouldBeNil)
   616  			space := regexp.MustCompile(`\s+`)
   617  			str := space.ReplaceAllString(buff.String(), " ")
   618  			actual := strings.TrimSpace(str)
   619  			So(actual, ShouldContainSubstring, fmt.Sprintf("repo img linux/amd64 %s false 552B",
   620  				image.DigestStr()[7:7+8]))
   621  		})
   622  
   623  		Convey("digest command errors", func() {
   624  			// too many parameters
   625  			buff := bytes.NewBufferString("")
   626  			args := []string{"too", "many", "args", "--config", "imagetest"}
   627  			cmd := NewImageDigestCommand(NewSearchService())
   628  			cmd.SetOut(buff)
   629  			cmd.SetErr(buff)
   630  			cmd.SetArgs(args)
   631  			err := cmd.Execute()
   632  			So(err, ShouldNotBeNil)
   633  
   634  			// bad input
   635  			buff = bytes.NewBufferString("")
   636  			args = []string{"bad-digest"}
   637  			cmd = NewImageDigestCommand(NewSearchService())
   638  			cmd.SetOut(buff)
   639  			cmd.SetErr(buff)
   640  			cmd.SetArgs(args)
   641  			err = cmd.Execute()
   642  			So(err, ShouldNotBeNil)
   643  
   644  			// no url
   645  			buff = bytes.NewBufferString("")
   646  			args = []string{godigest.FromString("str").String()}
   647  			cmd = NewImageDigestCommand(NewSearchService())
   648  			cmd.SetOut(buff)
   649  			cmd.SetErr(buff)
   650  			cmd.SetArgs(args)
   651  			err = cmd.Execute()
   652  			So(err, ShouldNotBeNil)
   653  		})
   654  
   655  		Convey("list command", func() {
   656  			image := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
   657  
   658  			err := UploadImage(image, baseURL, "repo", "img")
   659  			So(err, ShouldBeNil)
   660  
   661  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   662  				baseURL))
   663  			defer os.Remove(configPath)
   664  			args := []string{"list", "--config", "imagetest"}
   665  
   666  			cmd := NewImageCommand(NewSearchService())
   667  			buff := bytes.NewBufferString("")
   668  			cmd.SetOut(buff)
   669  			cmd.SetErr(buff)
   670  			cmd.SetArgs(args)
   671  			err = cmd.Execute()
   672  			So(err, ShouldBeNil)
   673  			space := regexp.MustCompile(`\s+`)
   674  			str := space.ReplaceAllString(buff.String(), " ")
   675  			actual := strings.TrimSpace(str)
   676  			fmt.Println(actual)
   677  			So(actual, ShouldContainSubstring, fmt.Sprintf("repo img linux/amd64 %s false 552B",
   678  				image.DigestStr()[7:7+8]))
   679  			fmt.Println(actual)
   680  		})
   681  
   682  		Convey("list command errors", func() {
   683  			// too many parameters
   684  			buff := bytes.NewBufferString("")
   685  			args := []string{"repo:img", "arg", "--config", "imagetest"}
   686  			cmd := NewImageListCommand(NewSearchService())
   687  			cmd.SetOut(buff)
   688  			cmd.SetErr(buff)
   689  			cmd.SetArgs(args)
   690  			err := cmd.Execute()
   691  			So(err, ShouldNotBeNil)
   692  
   693  			// no url
   694  			buff = bytes.NewBufferString("")
   695  			args = []string{}
   696  			cmd = NewImageListCommand(NewSearchService())
   697  			cmd.SetOut(buff)
   698  			cmd.SetErr(buff)
   699  			cmd.SetArgs(args)
   700  			err = cmd.Execute()
   701  			So(err, ShouldNotBeNil)
   702  		})
   703  
   704  		Convey("name command", func() {
   705  			image := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
   706  
   707  			err := UploadImage(image, baseURL, "repo", "img")
   708  			So(err, ShouldBeNil)
   709  
   710  			err = UploadImage(CreateRandomImage(), baseURL, "repo", "img2")
   711  			So(err, ShouldBeNil)
   712  
   713  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   714  				baseURL))
   715  			defer os.Remove(configPath)
   716  			args := []string{"name", "repo:img", "--config", "imagetest"}
   717  
   718  			cmd := NewImageCommand(NewSearchService())
   719  			buff := bytes.NewBufferString("")
   720  			cmd.SetOut(buff)
   721  			cmd.SetErr(buff)
   722  			cmd.SetArgs(args)
   723  			err = cmd.Execute()
   724  			So(err, ShouldBeNil)
   725  			space := regexp.MustCompile(`\s+`)
   726  			str := space.ReplaceAllString(buff.String(), " ")
   727  			actual := strings.TrimSpace(str)
   728  			fmt.Println(actual)
   729  			So(actual, ShouldContainSubstring, fmt.Sprintf("repo img linux/amd64 %s false 552B",
   730  				image.DigestStr()[7:7+8]))
   731  			fmt.Println(actual)
   732  		})
   733  
   734  		Convey("name command errors", func() {
   735  			// too many parameters
   736  			buff := bytes.NewBufferString("")
   737  			args := []string{"repo:img", "arg", "--config", "imagetest"}
   738  			cmd := NewImageNameCommand(NewSearchService())
   739  			cmd.SetOut(buff)
   740  			cmd.SetErr(buff)
   741  			cmd.SetArgs(args)
   742  			err := cmd.Execute()
   743  			So(err, ShouldNotBeNil)
   744  
   745  			// bad input
   746  			buff = bytes.NewBufferString("")
   747  			args = []string{":tag"}
   748  			cmd = NewImageNameCommand(NewSearchService())
   749  			cmd.SetOut(buff)
   750  			cmd.SetErr(buff)
   751  			cmd.SetArgs(args)
   752  			err = cmd.Execute()
   753  			So(err, ShouldNotBeNil)
   754  
   755  			// no url
   756  			buff = bytes.NewBufferString("")
   757  			args = []string{"repo:tag"}
   758  			cmd = NewImageNameCommand(NewSearchService())
   759  			cmd.SetOut(buff)
   760  			cmd.SetErr(buff)
   761  			cmd.SetArgs(args)
   762  			err = cmd.Execute()
   763  			So(err, ShouldNotBeNil)
   764  		})
   765  
   766  		Convey("CVE", func() {
   767  			vulnImage := CreateDefaultVulnerableImage()
   768  			err := UploadImage(vulnImage, baseURL, "repo", "vuln")
   769  			So(err, ShouldBeNil)
   770  
   771  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   772  				baseURL))
   773  			defer os.Remove(configPath)
   774  
   775  			args := []string{"cve", "repo:vuln", "--config", "imagetest"}
   776  			cmd := NewImageCommand(mockService{})
   777  			buff := bytes.NewBufferString("")
   778  			cmd.SetOut(buff)
   779  			cmd.SetErr(buff)
   780  			cmd.SetArgs(args)
   781  			err = cmd.Execute()
   782  			So(err, ShouldBeNil)
   783  			space := regexp.MustCompile(`\s+`)
   784  			str := space.ReplaceAllString(buff.String(), " ")
   785  			actual := strings.TrimSpace(str)
   786  			So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE")
   787  		})
   788  
   789  		Convey("CVE errors", func() {
   790  			count := 0
   791  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   792  				baseURL))
   793  			defer os.Remove(configPath)
   794  			args := []string{"cve", "repo:vuln", "--config", "imagetest"}
   795  			cmd := NewImageCommand(mockService{
   796  				getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username, password,
   797  					imageName, searchedCVE string) (*cveResult, error,
   798  				) {
   799  					if count == 0 {
   800  						count++
   801  						fmt.Println("Count:", count)
   802  
   803  						return &cveResult{}, zerr.ErrCVEDBNotFound
   804  					}
   805  
   806  					return &cveResult{}, zerr.ErrInjected
   807  				},
   808  			})
   809  			buff := bytes.NewBufferString("")
   810  			cmd.SetOut(buff)
   811  			cmd.SetErr(buff)
   812  			cmd.SetArgs(args)
   813  			err = cmd.Execute()
   814  			So(err, ShouldNotBeNil)
   815  			space := regexp.MustCompile(`\s+`)
   816  			str := space.ReplaceAllString(buff.String(), " ")
   817  			actual := strings.TrimSpace(str)
   818  			So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready")
   819  		})
   820  	})
   821  
   822  	Convey("Config error", t, func() {
   823  		args := []string{"base", "repo:derived", "--config", "imagetest"}
   824  		cmd := NewImageCommand(NewSearchService())
   825  		buff := bytes.NewBufferString("")
   826  		cmd.SetOut(buff)
   827  		cmd.SetErr(buff)
   828  		cmd.SetArgs(args)
   829  		err := cmd.Execute()
   830  		So(err, ShouldNotBeNil)
   831  		So(err, ShouldNotBeNil)
   832  
   833  		args = []string{"derived", "repo:base"}
   834  		cmd = NewImageCommand(NewSearchService())
   835  		buff = bytes.NewBufferString("")
   836  		cmd.SetOut(buff)
   837  		cmd.SetErr(buff)
   838  		cmd.SetArgs(args)
   839  		err = cmd.Execute()
   840  		So(err, ShouldNotBeNil)
   841  
   842  		args = []string{"digest", ispec.DescriptorEmptyJSON.Digest.String()}
   843  		cmd = NewImageCommand(NewSearchService())
   844  		buff = bytes.NewBufferString("")
   845  		cmd.SetOut(buff)
   846  		cmd.SetErr(buff)
   847  		cmd.SetArgs(args)
   848  		err = cmd.Execute()
   849  		So(err, ShouldNotBeNil)
   850  
   851  		args = []string{"list"}
   852  		cmd = NewImageCommand(NewSearchService())
   853  		buff = bytes.NewBufferString("")
   854  		cmd.SetOut(buff)
   855  		cmd.SetErr(buff)
   856  		cmd.SetArgs(args)
   857  		err = cmd.Execute()
   858  		So(err, ShouldNotBeNil)
   859  
   860  		args = []string{"name", "repo:img"}
   861  		cmd = NewImageCommand(NewSearchService())
   862  		buff = bytes.NewBufferString("")
   863  		cmd.SetOut(buff)
   864  		cmd.SetErr(buff)
   865  		cmd.SetArgs(args)
   866  		err = cmd.Execute()
   867  		So(err, ShouldNotBeNil)
   868  
   869  		args = []string{"cve", "repo:vuln"}
   870  		cmd = NewImageCommand(mockService{})
   871  		buff = bytes.NewBufferString("")
   872  		cmd.SetOut(buff)
   873  		cmd.SetErr(buff)
   874  		cmd.SetArgs(args)
   875  		err = cmd.Execute()
   876  		So(err, ShouldNotBeNil)
   877  	})
   878  }
   879  
   880  func TestImageCommandREST(t *testing.T) {
   881  	port := test.GetFreePort()
   882  	baseURL := test.GetBaseURL(port)
   883  	conf := config.New()
   884  	conf.HTTP.Port = port
   885  
   886  	ctlr := api.NewController(conf)
   887  	ctlr.Config.Storage.RootDirectory = t.TempDir()
   888  	cm := test.NewControllerManager(ctlr)
   889  
   890  	cm.StartAndWait(conf.HTTP.Port)
   891  	defer cm.StopServer()
   892  
   893  	Convey("commands without gql", t, func() {
   894  		err := removeLocalStorageContents(ctlr.StoreController.DefaultStore)
   895  		So(err, ShouldBeNil)
   896  
   897  		Convey("base and derived command", func() {
   898  			baseImage := CreateImageWith().LayerBlobs(
   899  				[][]byte{{1, 2, 3}, {11, 22, 33}},
   900  			).DefaultConfig().Build()
   901  
   902  			derivedImage := CreateImageWith().LayerBlobs(
   903  				[][]byte{{1, 2, 3}, {11, 22, 33}, {44, 55, 66}},
   904  			).DefaultConfig().Build()
   905  
   906  			err := UploadImage(baseImage, baseURL, "repo", "base")
   907  			So(err, ShouldBeNil)
   908  
   909  			err = UploadImage(derivedImage, baseURL, "repo", "derived")
   910  			So(err, ShouldBeNil)
   911  
   912  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   913  				baseURL))
   914  			defer os.Remove(configPath)
   915  
   916  			args := []string{"base", "repo:derived", "--config", "imagetest"}
   917  			cmd := NewImageCommand(NewSearchService())
   918  			buff := bytes.NewBufferString("")
   919  			cmd.SetOut(buff)
   920  			cmd.SetErr(buff)
   921  			cmd.SetArgs(args)
   922  			err = cmd.Execute()
   923  			So(err, ShouldNotBeNil)
   924  
   925  			args = []string{"derived", "repo:base"}
   926  			cmd = NewImageCommand(NewSearchService())
   927  			buff = bytes.NewBufferString("")
   928  			cmd.SetOut(buff)
   929  			cmd.SetErr(buff)
   930  			cmd.SetArgs(args)
   931  			err = cmd.Execute()
   932  			So(err, ShouldNotBeNil)
   933  		})
   934  
   935  		Convey("digest command", func() {
   936  			image := CreateRandomImage()
   937  
   938  			err := UploadImage(image, baseURL, "repo", "img")
   939  			So(err, ShouldBeNil)
   940  
   941  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   942  				baseURL))
   943  			defer os.Remove(configPath)
   944  
   945  			args := []string{"digest", image.DigestStr(), "--config", "imagetest"}
   946  			cmd := NewImageCommand(NewSearchService())
   947  			buff := bytes.NewBufferString("")
   948  			cmd.SetOut(buff)
   949  			cmd.SetErr(buff)
   950  			cmd.SetArgs(args)
   951  			err = cmd.Execute()
   952  			So(err, ShouldNotBeNil)
   953  		})
   954  
   955  		Convey("list command", func() {
   956  			image := CreateRandomImage()
   957  
   958  			err := UploadImage(image, baseURL, "repo", "img")
   959  			So(err, ShouldBeNil)
   960  
   961  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   962  				baseURL))
   963  			defer os.Remove(configPath)
   964  
   965  			args := []string{"list", "--config", "imagetest"}
   966  			cmd := NewImageCommand(NewSearchService())
   967  			buff := bytes.NewBufferString("")
   968  			cmd.SetOut(buff)
   969  			cmd.SetErr(buff)
   970  			cmd.SetArgs(args)
   971  			err = cmd.Execute()
   972  			So(err, ShouldBeNil)
   973  			fmt.Println(buff.String())
   974  			fmt.Println()
   975  		})
   976  
   977  		Convey("name command", func() {
   978  			image := CreateRandomImage()
   979  
   980  			err := UploadImage(image, baseURL, "repo", "img")
   981  			So(err, ShouldBeNil)
   982  
   983  			err = UploadImage(CreateRandomImage(), baseURL, "repo", "img2")
   984  			So(err, ShouldBeNil)
   985  
   986  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
   987  				baseURL))
   988  			defer os.Remove(configPath)
   989  
   990  			args := []string{"name", "repo:img", "--config", "imagetest"}
   991  			cmd := NewImageCommand(NewSearchService())
   992  			buff := bytes.NewBufferString("")
   993  			cmd.SetOut(buff)
   994  			cmd.SetErr(buff)
   995  			cmd.SetArgs(args)
   996  			err = cmd.Execute()
   997  			So(err, ShouldBeNil)
   998  			fmt.Println(buff.String())
   999  			fmt.Println()
  1000  		})
  1001  
  1002  		Convey("CVE", func() {
  1003  			vulnImage := CreateDefaultVulnerableImage()
  1004  			err := UploadImage(vulnImage, baseURL, "repo", "vuln")
  1005  			So(err, ShouldBeNil)
  1006  
  1007  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
  1008  				baseURL))
  1009  			args := []string{"cve", "repo:vuln", "--config", "imagetest"}
  1010  			defer os.Remove(configPath)
  1011  			cmd := NewImageCommand(mockService{})
  1012  			buff := bytes.NewBufferString("")
  1013  			cmd.SetOut(buff)
  1014  			cmd.SetErr(buff)
  1015  			cmd.SetArgs(args)
  1016  			err = cmd.Execute()
  1017  			So(err, ShouldNotBeNil)
  1018  		})
  1019  	})
  1020  }
  1021  
  1022  type mockService struct {
  1023  	getAllImagesFn func(ctx context.Context, config SearchConfig, username, password string,
  1024  		channel chan stringResult, wtgrp *sync.WaitGroup)
  1025  
  1026  	getImagesGQLFn func(ctx context.Context, config SearchConfig, username, password string,
  1027  		imageName string) (*common.ImageListResponse, error)
  1028  
  1029  	getImageByNameFn func(ctx context.Context, config SearchConfig,
  1030  		username, password, imageName string, channel chan stringResult, wtgrp *sync.WaitGroup,
  1031  	)
  1032  
  1033  	getImagesByDigestFn func(ctx context.Context, config SearchConfig, username,
  1034  		password, digest string, rch chan stringResult, wtgrp *sync.WaitGroup,
  1035  	)
  1036  
  1037  	getReferrersFn func(ctx context.Context, config SearchConfig, username, password string,
  1038  		repo, digest string,
  1039  	) (referrersResult, error)
  1040  
  1041  	globalSearchGQLFn func(ctx context.Context, config SearchConfig, username, password string,
  1042  		query string,
  1043  	) (*common.GlobalSearch, error)
  1044  
  1045  	getReferrersGQLFn func(ctx context.Context, config SearchConfig, username, password string,
  1046  		repo, digest string,
  1047  	) (*common.ReferrersResp, error)
  1048  
  1049  	getDerivedImageListGQLFn func(ctx context.Context, config SearchConfig, username, password string,
  1050  		derivedImage string,
  1051  	) (*common.DerivedImageListResponse, error)
  1052  
  1053  	getBaseImageListGQLFn func(ctx context.Context, config SearchConfig, username, password string,
  1054  		derivedImage string,
  1055  	) (*common.BaseImageListResponse, error)
  1056  
  1057  	getImagesForDigestGQLFn func(ctx context.Context, config SearchConfig, username, password string,
  1058  		digest string,
  1059  	) (*common.ImagesForDigest, error)
  1060  
  1061  	getCveByImageGQLFn func(ctx context.Context, config SearchConfig, username, password,
  1062  		imageName, searchedCVE string,
  1063  	) (*cveResult, error)
  1064  
  1065  	getTagsForCVEGQLFn func(ctx context.Context, config SearchConfig, username, password,
  1066  		imageName, cveID string,
  1067  	) (*common.ImagesForCve, error)
  1068  
  1069  	getFixedTagsForCVEGQLFn func(ctx context.Context, config SearchConfig, username, password,
  1070  		imageName, cveID string,
  1071  	) (*common.ImageListWithCVEFixedResponse, error)
  1072  }
  1073  
  1074  func (service mockService) getRepos(ctx context.Context, config SearchConfig, username,
  1075  	password string, channel chan stringResult, wtgrp *sync.WaitGroup,
  1076  ) {
  1077  	defer wtgrp.Done()
  1078  	defer close(channel)
  1079  
  1080  	fmt.Fprintln(config.ResultWriter, "\n\nREPOSITORY NAME")
  1081  
  1082  	fmt.Fprintln(config.ResultWriter, "repo1")
  1083  	fmt.Fprintln(config.ResultWriter, "repo2")
  1084  }
  1085  
  1086  func (service mockService) getReferrers(ctx context.Context, config SearchConfig, username, password string,
  1087  	repo, digest string,
  1088  ) (referrersResult, error) {
  1089  	if service.getReferrersFn != nil {
  1090  		return service.getReferrersFn(ctx, config, username, password, repo, digest)
  1091  	}
  1092  
  1093  	return referrersResult{
  1094  		common.Referrer{
  1095  			ArtifactType: "art.type",
  1096  			Digest:       ispec.DescriptorEmptyJSON.Digest.String(),
  1097  			MediaType:    ispec.MediaTypeImageManifest,
  1098  			Size:         100,
  1099  		},
  1100  	}, nil
  1101  }
  1102  
  1103  func (service mockService) globalSearchGQL(ctx context.Context, config SearchConfig, username, password string,
  1104  	query string,
  1105  ) (*common.GlobalSearch, error) {
  1106  	if service.globalSearchGQLFn != nil {
  1107  		return service.globalSearchGQLFn(ctx, config, username, password, query)
  1108  	}
  1109  
  1110  	return &common.GlobalSearch{
  1111  		Images: []common.ImageSummary{
  1112  			{
  1113  				RepoName:  "repo",
  1114  				MediaType: ispec.MediaTypeImageManifest,
  1115  				Size:      "100",
  1116  				Manifests: []common.ManifestSummary{
  1117  					{
  1118  						Digest:       godigest.FromString("str").String(),
  1119  						Size:         "100",
  1120  						ConfigDigest: ispec.DescriptorEmptyJSON.Digest.String(),
  1121  					},
  1122  				},
  1123  			},
  1124  		},
  1125  		Repos: []common.RepoSummary{
  1126  			{
  1127  				Name:        "repo",
  1128  				Size:        "100",
  1129  				LastUpdated: time.Date(2010, 1, 1, 1, 1, 1, 0, time.UTC),
  1130  			},
  1131  		},
  1132  	}, nil
  1133  }
  1134  
  1135  func (service mockService) getReferrersGQL(ctx context.Context, config SearchConfig, username, password string,
  1136  	repo, digest string,
  1137  ) (*common.ReferrersResp, error) {
  1138  	if service.getReferrersGQLFn != nil {
  1139  		return service.getReferrersGQLFn(ctx, config, username, password, repo, digest)
  1140  	}
  1141  
  1142  	return &common.ReferrersResp{
  1143  		ReferrersResult: common.ReferrersResult{
  1144  			Referrers: []common.Referrer{
  1145  				{
  1146  					MediaType:    "MediaType",
  1147  					ArtifactType: "ArtifactType",
  1148  					Size:         100,
  1149  					Digest:       "Digest",
  1150  				},
  1151  			},
  1152  		},
  1153  	}, nil
  1154  }
  1155  
  1156  func (service mockService) getDerivedImageListGQL(ctx context.Context, config SearchConfig, username, password string,
  1157  	derivedImage string,
  1158  ) (*common.DerivedImageListResponse, error) {
  1159  	if service.getDerivedImageListGQLFn != nil {
  1160  		return service.getDerivedImageListGQLFn(ctx, config, username, password, derivedImage)
  1161  	}
  1162  
  1163  	imageListGQLResponse := &common.DerivedImageListResponse{}
  1164  	imageListGQLResponse.DerivedImageList.Results = []common.ImageSummary{
  1165  		{
  1166  			RepoName: "dummyImageName",
  1167  			Tag:      "tag",
  1168  			Manifests: []common.ManifestSummary{
  1169  				{
  1170  					Digest:       godigest.FromString("Digest").String(),
  1171  					ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1172  					Size:         "123445",
  1173  					Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1174  					Platform:     common.Platform{Os: "os", Arch: "arch"},
  1175  				},
  1176  			},
  1177  			Size: "123445",
  1178  		},
  1179  	}
  1180  
  1181  	return imageListGQLResponse, nil
  1182  }
  1183  
  1184  func (service mockService) getBaseImageListGQL(ctx context.Context, config SearchConfig, username, password string,
  1185  	baseImage string,
  1186  ) (*common.BaseImageListResponse, error) {
  1187  	if service.getBaseImageListGQLFn != nil {
  1188  		return service.getBaseImageListGQLFn(ctx, config, username, password, baseImage)
  1189  	}
  1190  
  1191  	imageListGQLResponse := &common.BaseImageListResponse{}
  1192  	imageListGQLResponse.BaseImageList.Results = []common.ImageSummary{
  1193  		{
  1194  			RepoName: "dummyImageName",
  1195  			Tag:      "tag",
  1196  			Manifests: []common.ManifestSummary{
  1197  				{
  1198  					Digest:       godigest.FromString("Digest").String(),
  1199  					ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1200  					Size:         "123445",
  1201  					Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1202  					Platform:     common.Platform{Os: "os", Arch: "arch"},
  1203  				},
  1204  			},
  1205  			Size: "123445",
  1206  		},
  1207  	}
  1208  
  1209  	return imageListGQLResponse, nil
  1210  }
  1211  
  1212  func (service mockService) getImagesGQL(ctx context.Context, config SearchConfig, username, password string,
  1213  	imageName string,
  1214  ) (*common.ImageListResponse, error) {
  1215  	if service.getImagesGQLFn != nil {
  1216  		return service.getImagesGQLFn(ctx, config, username, password, imageName)
  1217  	}
  1218  
  1219  	imageListGQLResponse := &common.ImageListResponse{}
  1220  	imageListGQLResponse.PaginatedImagesResult.Results = []common.ImageSummary{
  1221  		{
  1222  			RepoName:  "dummyImageName",
  1223  			Tag:       "tag",
  1224  			MediaType: ispec.MediaTypeImageManifest,
  1225  			Digest:    godigest.FromString("test").String(),
  1226  			Manifests: []common.ManifestSummary{
  1227  				{
  1228  					Digest:       godigest.FromString("Digest").String(),
  1229  					ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1230  					Size:         "123445",
  1231  					Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1232  					Platform:     common.Platform{Os: "os", Arch: "arch"},
  1233  				},
  1234  			},
  1235  			Size: "123445",
  1236  		},
  1237  	}
  1238  
  1239  	return imageListGQLResponse, nil
  1240  }
  1241  
  1242  func (service mockService) getImagesForDigestGQL(ctx context.Context, config SearchConfig, username, password string,
  1243  	digest string,
  1244  ) (*common.ImagesForDigest, error) {
  1245  	if service.getImagesForDigestGQLFn != nil {
  1246  		return service.getImagesForDigestGQLFn(ctx, config, username, password, digest)
  1247  	}
  1248  
  1249  	imageListGQLResponse := &common.ImagesForDigest{}
  1250  	imageListGQLResponse.Results = []common.ImageSummary{
  1251  		{
  1252  			RepoName:  "randomimageName",
  1253  			Tag:       "tag",
  1254  			MediaType: ispec.MediaTypeImageManifest,
  1255  			Digest:    godigest.FromString("test").String(),
  1256  			Manifests: []common.ManifestSummary{
  1257  				{
  1258  					Digest:       godigest.FromString("Digest").String(),
  1259  					ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1260  					Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1261  					Size:         "123445",
  1262  					Platform:     common.Platform{Os: "os", Arch: "arch"},
  1263  				},
  1264  			},
  1265  			Size: "123445",
  1266  		},
  1267  	}
  1268  
  1269  	return imageListGQLResponse, nil
  1270  }
  1271  
  1272  func (service mockService) getTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password,
  1273  	imageName, cveID string,
  1274  ) (*common.ImagesForCve, error) {
  1275  	if service.getTagsForCVEGQLFn != nil {
  1276  		return service.getTagsForCVEGQLFn(ctx, config, username, password, imageName, cveID)
  1277  	}
  1278  
  1279  	images := &common.ImagesForCve{
  1280  		Errors: nil,
  1281  		ImagesForCVEList: struct {
  1282  			common.PaginatedImagesResult `json:"ImageListForCVE"` //nolint:tagliatelle // graphQL schema
  1283  		}{},
  1284  	}
  1285  
  1286  	if imageName == "" {
  1287  		imageName = "image-name"
  1288  	}
  1289  
  1290  	images.Errors = nil
  1291  
  1292  	mockedImage := service.getMockedImageByName(imageName)
  1293  	images.Results = []common.ImageSummary{common.ImageSummary(mockedImage)}
  1294  
  1295  	return images, nil
  1296  }
  1297  
  1298  func (service mockService) getFixedTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password,
  1299  	imageName, cveID string,
  1300  ) (*common.ImageListWithCVEFixedResponse, error) {
  1301  	if service.getFixedTagsForCVEGQLFn != nil {
  1302  		return service.getFixedTagsForCVEGQLFn(ctx, config, username, password, imageName, cveID)
  1303  	}
  1304  
  1305  	fixedTags := &common.ImageListWithCVEFixedResponse{
  1306  		Errors: nil,
  1307  		ImageListWithCVEFixed: struct {
  1308  			common.PaginatedImagesResult `json:"ImageListWithCVEFixed"` //nolint:tagliatelle // graphQL schema
  1309  		}{},
  1310  	}
  1311  
  1312  	fixedTags.Errors = nil
  1313  
  1314  	mockedImage := service.getMockedImageByName(imageName)
  1315  	fixedTags.Results = []common.ImageSummary{common.ImageSummary(mockedImage)}
  1316  
  1317  	return fixedTags, nil
  1318  }
  1319  
  1320  func (service mockService) getCveByImageGQL(ctx context.Context, config SearchConfig, username, password,
  1321  	imageName, searchedCVE string,
  1322  ) (*cveResult, error) {
  1323  	if service.getCveByImageGQLFn != nil {
  1324  		return service.getCveByImageGQLFn(ctx, config, username, password, imageName, searchedCVE)
  1325  	}
  1326  	cveRes := &cveResult{}
  1327  	cveRes.Data = cveData{
  1328  		CVEListForImage: cveListForImage{
  1329  			Tag: imageName,
  1330  			CVEList: []cve{
  1331  				{
  1332  					ID:          "dummyCVEID",
  1333  					Description: "Description of the CVE",
  1334  					Title:       "Title of that CVE",
  1335  					Severity:    "HIGH",
  1336  					PackageList: []packageList{
  1337  						{
  1338  							Name:             "packagename",
  1339  							FixedVersion:     "fixedver",
  1340  							InstalledVersion: "installedver",
  1341  						},
  1342  					},
  1343  				},
  1344  			},
  1345  		},
  1346  	}
  1347  
  1348  	return cveRes, nil
  1349  }
  1350  
  1351  //nolint:goconst
  1352  func (service mockService) getMockedImageByName(imageName string) imageStruct {
  1353  	image := imageStruct{}
  1354  	image.RepoName = imageName
  1355  	image.Tag = "tag"
  1356  	image.MediaType = ispec.MediaTypeImageManifest
  1357  	image.Manifests = []common.ManifestSummary{
  1358  		{
  1359  			Digest:       godigest.FromString("Digest").String(),
  1360  			ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1361  			Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1362  			Size:         "123445",
  1363  			Platform:     common.Platform{Os: "os", Arch: "arch"},
  1364  		},
  1365  	}
  1366  	image.Size = "123445"
  1367  
  1368  	return image
  1369  }
  1370  
  1371  func (service mockService) getAllImages(ctx context.Context, config SearchConfig, username, password string,
  1372  	channel chan stringResult, wtgrp *sync.WaitGroup,
  1373  ) {
  1374  	defer wtgrp.Done()
  1375  	defer close(channel)
  1376  
  1377  	if service.getAllImagesFn != nil {
  1378  		service.getAllImagesFn(ctx, config, username, password, channel, wtgrp)
  1379  
  1380  		return
  1381  	}
  1382  
  1383  	image := &imageStruct{}
  1384  	image.RepoName = "randomimageName"
  1385  	image.Tag = "tag"
  1386  	image.Digest = godigest.FromString("test").String()
  1387  	image.MediaType = ispec.MediaTypeImageManifest
  1388  	image.Manifests = []common.ManifestSummary{
  1389  		{
  1390  			Digest:       godigest.FromString("Digest").String(),
  1391  			ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1392  			Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1393  			Size:         "123445",
  1394  			Platform:     common.Platform{Os: "os", Arch: "arch"},
  1395  		},
  1396  	}
  1397  	image.Size = "123445"
  1398  
  1399  	str, err := image.string(config.OutputFormat, len(image.RepoName), len(image.Tag), len("os/Arch"), config.Verbose)
  1400  	if err != nil {
  1401  		channel <- stringResult{"", err}
  1402  
  1403  		return
  1404  	}
  1405  
  1406  	channel <- stringResult{str, nil}
  1407  }
  1408  
  1409  func (service mockService) getImageByName(ctx context.Context, config SearchConfig,
  1410  	username, password, imageName string, channel chan stringResult, wtgrp *sync.WaitGroup,
  1411  ) {
  1412  	defer wtgrp.Done()
  1413  	defer close(channel)
  1414  
  1415  	if service.getImageByNameFn != nil {
  1416  		service.getImageByNameFn(ctx, config, username, password, imageName, channel, wtgrp)
  1417  
  1418  		return
  1419  	}
  1420  
  1421  	image := &imageStruct{}
  1422  	image.RepoName = imageName
  1423  	image.Tag = "tag"
  1424  	image.Digest = godigest.FromString("test").String()
  1425  	image.MediaType = ispec.MediaTypeImageManifest
  1426  	image.Manifests = []common.ManifestSummary{
  1427  		{
  1428  			Digest:       godigest.FromString("Digest").String(),
  1429  			ConfigDigest: godigest.FromString("ConfigDigest").String(),
  1430  			Layers:       []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}},
  1431  			Size:         "123445",
  1432  			Platform:     common.Platform{Os: "os", Arch: "arch"},
  1433  		},
  1434  	}
  1435  	image.Size = "123445"
  1436  
  1437  	str, err := image.string(config.OutputFormat, len(image.RepoName), len(image.Tag), len("os/Arch"), config.Verbose)
  1438  	if err != nil {
  1439  		channel <- stringResult{"", err}
  1440  
  1441  		return
  1442  	}
  1443  
  1444  	channel <- stringResult{str, nil}
  1445  }
  1446  
  1447  func (service mockService) getImagesByDigest(ctx context.Context, config SearchConfig, username,
  1448  	password, digest string, rch chan stringResult, wtgrp *sync.WaitGroup,
  1449  ) {
  1450  	if service.getImagesByDigestFn != nil {
  1451  		defer wtgrp.Done()
  1452  		defer close(rch)
  1453  
  1454  		service.getImagesByDigestFn(ctx, config, username, password, digest, rch, wtgrp)
  1455  
  1456  		return
  1457  	}
  1458  
  1459  	service.getImageByName(ctx, config, username, password, "anImage", rch, wtgrp)
  1460  }
  1461  
  1462  func makeConfigFile(content string) string {
  1463  	os.Setenv("HOME", os.TempDir())
  1464  
  1465  	home, err := os.UserHomeDir()
  1466  	if err != nil {
  1467  		panic(err)
  1468  	}
  1469  
  1470  	configPath := path.Join(home, "/.zot")
  1471  
  1472  	if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil {
  1473  		panic(err)
  1474  	}
  1475  
  1476  	return configPath
  1477  }
  1478  
  1479  func getTestSearchConfig(url string, searchService SearchService) SearchConfig {
  1480  	var (
  1481  		user         string
  1482  		outputFormat string
  1483  		verbose      bool
  1484  		debug        bool
  1485  		verifyTLS    bool
  1486  	)
  1487  
  1488  	return SearchConfig{
  1489  		SearchService: searchService,
  1490  		SortBy:        "alpha-asc",
  1491  		ServURL:       url,
  1492  		User:          user,
  1493  		OutputFormat:  outputFormat,
  1494  		Verbose:       verbose,
  1495  		Debug:         debug,
  1496  		VerifyTLS:     verifyTLS,
  1497  		ResultWriter:  nil,
  1498  	}
  1499  }
  1500  
  1501  func removeLocalStorageContents(imageStore stypes.ImageStore) error {
  1502  	repos, err := imageStore.GetRepositories()
  1503  	if err != nil {
  1504  		return err
  1505  	}
  1506  
  1507  	for _, repo := range repos {
  1508  		// take just the first path
  1509  		err = os.RemoveAll(filepath.Join(imageStore.RootDir(), filepath.SplitList(repo)[0]))
  1510  		if err != nil {
  1511  			return err
  1512  		}
  1513  	}
  1514  
  1515  	return nil
  1516  }