zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/cli/client/search_functions_internal_test.go (about) 1 //go:build search 2 // +build search 3 4 // 5 //nolint:dupl 6 package client 7 8 import ( 9 "bytes" 10 "context" 11 "io" 12 "os" 13 "regexp" 14 "strings" 15 "sync" 16 "testing" 17 "time" 18 19 godigest "github.com/opencontainers/go-digest" 20 ispec "github.com/opencontainers/image-spec/specs-go/v1" 21 . "github.com/smartystreets/goconvey/convey" 22 "github.com/spf13/cobra" 23 24 zerr "zotregistry.dev/zot/errors" 25 "zotregistry.dev/zot/pkg/common" 26 ) 27 28 func TestSearchAllImages(t *testing.T) { 29 Convey("SearchAllImages", t, func() { 30 buff := bytes.NewBufferString("") 31 searchConfig := getMockSearchConfig(buff, mockService{ 32 getAllImagesFn: func(ctx context.Context, config SearchConfig, username, password string, 33 channel chan stringResult, wtgrp *sync.WaitGroup, 34 ) { 35 str, err := getMockImageStruct().stringPlainText(10, 10, 10, false) 36 37 channel <- stringResult{StrValue: str, Err: err} 38 }, 39 }) 40 41 err := SearchAllImages(searchConfig) 42 So(err, ShouldBeNil) 43 space := regexp.MustCompile(`\s+`) 44 str := space.ReplaceAllString(buff.String(), " ") 45 actual := strings.TrimSpace(str) 46 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 47 }) 48 } 49 50 func TestSearchAllImagesGQL(t *testing.T) { 51 Convey("SearchAllImagesGQL", t, func() { 52 buff := bytes.NewBufferString("") 53 searchConfig := getMockSearchConfig(buff, mockService{ 54 getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string, 55 ) (*common.ImageListResponse, error) { 56 return &common.ImageListResponse{ImageList: common.ImageList{ 57 PaginatedImagesResult: common.PaginatedImagesResult{ 58 Results: []common.ImageSummary{getMockImageSummary()}, 59 }, 60 }}, nil 61 }, 62 }) 63 64 err := SearchAllImagesGQL(searchConfig) 65 So(err, ShouldBeNil) 66 space := regexp.MustCompile(`\s+`) 67 str := space.ReplaceAllString(buff.String(), " ") 68 actual := strings.TrimSpace(str) 69 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 70 }) 71 72 Convey("SearchAllImagesGQL error", t, func() { 73 buff := bytes.NewBufferString("") 74 searchConfig := getMockSearchConfig(buff, mockService{ 75 getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string, 76 ) (*common.ImageListResponse, error) { 77 return &common.ImageListResponse{ImageList: common.ImageList{ 78 PaginatedImagesResult: common.PaginatedImagesResult{ 79 Results: []common.ImageSummary{getMockImageSummary()}, 80 }, 81 }}, zerr.ErrInjected 82 }, 83 }) 84 85 err := SearchAllImagesGQL(searchConfig) 86 So(err, ShouldNotBeNil) 87 }) 88 } 89 90 func TestSearchImageByName(t *testing.T) { 91 Convey("SearchImageByName", t, func() { 92 buff := bytes.NewBufferString("") 93 searchConfig := getMockSearchConfig(buff, mockService{ 94 getImageByNameFn: func(ctx context.Context, config SearchConfig, username string, password string, imageName string, 95 channel chan stringResult, wtgrp *sync.WaitGroup, 96 ) { 97 str, err := getMockImageStruct().stringPlainText(10, 10, 10, false) 98 99 channel <- stringResult{StrValue: str, Err: err} 100 }, 101 }) 102 103 err := SearchImageByName(searchConfig, "repo") 104 So(err, ShouldBeNil) 105 space := regexp.MustCompile(`\s+`) 106 str := space.ReplaceAllString(buff.String(), " ") 107 actual := strings.TrimSpace(str) 108 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 109 }) 110 111 Convey("SearchImageByName error", t, func() { 112 buff := bytes.NewBufferString("") 113 searchConfig := getMockSearchConfig(buff, mockService{ 114 getImageByNameFn: func(ctx context.Context, config SearchConfig, username string, password string, imageName string, 115 channel chan stringResult, wtgrp *sync.WaitGroup, 116 ) { 117 channel <- stringResult{StrValue: "", Err: zerr.ErrInjected} 118 }, 119 }) 120 121 err := SearchImageByName(searchConfig, "repo") 122 So(err, ShouldNotBeNil) 123 }) 124 } 125 126 func TestSearchImageByNameGQL(t *testing.T) { 127 Convey("SearchImageByNameGQL", t, func() { 128 buff := bytes.NewBufferString("") 129 searchConfig := getMockSearchConfig(buff, mockService{ 130 getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string, 131 ) (*common.ImageListResponse, error) { 132 return &common.ImageListResponse{ImageList: common.ImageList{ 133 PaginatedImagesResult: common.PaginatedImagesResult{ 134 Results: []common.ImageSummary{getMockImageSummary()}, 135 }, 136 }}, nil 137 }, 138 }) 139 140 err := SearchImageByNameGQL(searchConfig, "repo") 141 So(err, ShouldBeNil) 142 space := regexp.MustCompile(`\s+`) 143 str := space.ReplaceAllString(buff.String(), " ") 144 actual := strings.TrimSpace(str) 145 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 146 }) 147 148 Convey("SearchImageByNameGQL error", t, func() { 149 buff := bytes.NewBufferString("") 150 searchConfig := getMockSearchConfig(buff, mockService{ 151 getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string, 152 ) (*common.ImageListResponse, error) { 153 return &common.ImageListResponse{ImageList: common.ImageList{ 154 PaginatedImagesResult: common.PaginatedImagesResult{ 155 Results: []common.ImageSummary{getMockImageSummary()}, 156 }, 157 }}, zerr.ErrInjected 158 }, 159 }) 160 161 err := SearchImageByNameGQL(searchConfig, "repo") 162 So(err, ShouldNotBeNil) 163 }) 164 } 165 166 func TestSearchImagesByDigest(t *testing.T) { 167 Convey("SearchImagesByDigest", t, func() { 168 buff := bytes.NewBufferString("") 169 searchConfig := getMockSearchConfig(buff, mockService{ 170 getImagesByDigestFn: func(ctx context.Context, config SearchConfig, username string, password string, digest string, 171 rch chan stringResult, wtgrp *sync.WaitGroup, 172 ) { 173 str, err := getMockImageStruct().stringPlainText(10, 10, 10, false) 174 175 rch <- stringResult{StrValue: str, Err: err} 176 }, 177 }) 178 179 err := SearchImagesByDigest(searchConfig, godigest.FromString("str").String()) 180 So(err, ShouldBeNil) 181 space := regexp.MustCompile(`\s+`) 182 str := space.ReplaceAllString(buff.String(), " ") 183 actual := strings.TrimSpace(str) 184 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 185 }) 186 187 Convey("SearchImagesByDigest error", t, func() { 188 buff := bytes.NewBufferString("") 189 searchConfig := getMockSearchConfig(buff, mockService{ 190 getImagesByDigestFn: func(ctx context.Context, config SearchConfig, username string, password string, digest string, 191 rch chan stringResult, wtgrp *sync.WaitGroup, 192 ) { 193 rch <- stringResult{StrValue: "", Err: zerr.ErrInjected} 194 }, 195 }) 196 197 err := SearchImagesByDigest(searchConfig, godigest.FromString("str").String()) 198 So(err, ShouldNotBeNil) 199 }) 200 } 201 202 func TestSearchDerivedImageListGQL(t *testing.T) { 203 Convey("SearchDerivedImageListGQL", t, func() { 204 buff := bytes.NewBufferString("") 205 searchConfig := getMockSearchConfig(buff, mockService{ 206 getDerivedImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 207 derivedImage string) (*common.DerivedImageListResponse, error, 208 ) { 209 return &common.DerivedImageListResponse{DerivedImageList: common.DerivedImageList{ 210 PaginatedImagesResult: common.PaginatedImagesResult{ 211 Results: []common.ImageSummary{ 212 getMockImageSummary(), 213 }, 214 }, 215 }}, nil 216 }, 217 }) 218 219 err := SearchDerivedImageListGQL(searchConfig, "repo:tag") 220 So(err, ShouldBeNil) 221 space := regexp.MustCompile(`\s+`) 222 str := space.ReplaceAllString(buff.String(), " ") 223 actual := strings.TrimSpace(str) 224 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 225 }) 226 227 Convey("SearchDerivedImageListGQL error", t, func() { 228 buff := bytes.NewBufferString("") 229 searchConfig := getMockSearchConfig(buff, mockService{ 230 getDerivedImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 231 derivedImage string) (*common.DerivedImageListResponse, error, 232 ) { 233 return &common.DerivedImageListResponse{DerivedImageList: common.DerivedImageList{ 234 PaginatedImagesResult: common.PaginatedImagesResult{Results: []common.ImageSummary{}}, 235 }}, zerr.ErrInjected 236 }, 237 }) 238 239 err := SearchDerivedImageListGQL(searchConfig, "repo:tag") 240 So(err, ShouldNotBeNil) 241 }) 242 } 243 244 func TestSearchBaseImageListGQL(t *testing.T) { 245 Convey("SearchBaseImageListGQL", t, func() { 246 buff := bytes.NewBufferString("") 247 searchConfig := getMockSearchConfig(buff, mockService{ 248 getBaseImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 249 derivedImage string) (*common.BaseImageListResponse, error, 250 ) { 251 return &common.BaseImageListResponse{BaseImageList: common.BaseImageList{ 252 PaginatedImagesResult: common.PaginatedImagesResult{Results: []common.ImageSummary{ 253 getMockImageSummary(), 254 }}, 255 }}, nil 256 }, 257 }) 258 259 err := SearchBaseImageListGQL(searchConfig, "repo:tag") 260 So(err, ShouldBeNil) 261 space := regexp.MustCompile(`\s+`) 262 str := space.ReplaceAllString(buff.String(), " ") 263 actual := strings.TrimSpace(str) 264 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 265 }) 266 267 Convey("SearchBaseImageListGQL error", t, func() { 268 buff := bytes.NewBufferString("") 269 searchConfig := getMockSearchConfig(buff, mockService{ 270 getBaseImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 271 derivedImage string) (*common.BaseImageListResponse, error, 272 ) { 273 return &common.BaseImageListResponse{BaseImageList: common.BaseImageList{ 274 PaginatedImagesResult: common.PaginatedImagesResult{Results: []common.ImageSummary{}}, 275 }}, zerr.ErrInjected 276 }, 277 }) 278 279 err := SearchBaseImageListGQL(searchConfig, "repo:tag") 280 So(err, ShouldNotBeNil) 281 }) 282 } 283 284 func TestSearchImagesForDigestGQL(t *testing.T) { 285 Convey("SearchImagesForDigestGQL", t, func() { 286 buff := bytes.NewBufferString("") 287 searchConfig := getMockSearchConfig(buff, mockService{ 288 getImagesForDigestGQLFn: func(ctx context.Context, config SearchConfig, username string, 289 password string, digest string) (*common.ImagesForDigest, error, 290 ) { 291 return &common.ImagesForDigest{ImagesForDigestList: common.ImagesForDigestList{ 292 PaginatedImagesResult: common.PaginatedImagesResult{ 293 Results: []common.ImageSummary{getMockImageSummary()}, 294 }, 295 }}, nil 296 }, 297 }) 298 299 err := SearchImagesForDigestGQL(searchConfig, "digest") 300 So(err, ShouldBeNil) 301 space := regexp.MustCompile(`\s+`) 302 str := space.ReplaceAllString(buff.String(), " ") 303 actual := strings.TrimSpace(str) 304 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 305 }) 306 307 Convey("SearchImagesForDigestGQL error", t, func() { 308 buff := bytes.NewBufferString("") 309 searchConfig := getMockSearchConfig(buff, mockService{ 310 getImagesForDigestGQLFn: func(ctx context.Context, config SearchConfig, username string, 311 password string, digest string) (*common.ImagesForDigest, error, 312 ) { 313 return &common.ImagesForDigest{ImagesForDigestList: common.ImagesForDigestList{ 314 PaginatedImagesResult: common.PaginatedImagesResult{}, 315 }}, zerr.ErrInjected 316 }, 317 }) 318 319 err := SearchImagesForDigestGQL(searchConfig, "digest") 320 So(err, ShouldNotBeNil) 321 }) 322 } 323 324 func TestSearchCVEForImageGQL(t *testing.T) { 325 Convey("SearchCVEForImageGQL normal mode", t, func() { 326 buff := bytes.NewBufferString("") 327 searchConfig := getMockSearchConfig(buff, mockService{ 328 getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 329 imageName string, searchedCVE string) (*cveResult, error, 330 ) { 331 return &cveResult{ 332 Data: cveData{ 333 CVEListForImage: cveListForImage{ 334 CVEList: []cve{ 335 { 336 ID: "dummyCVEID", 337 Description: "Description of the CVE", 338 Title: "Title of that CVE", 339 Severity: "HIGH", 340 PackageList: []packageList{ 341 { 342 Name: "packagename", 343 FixedVersion: "fixedver", 344 InstalledVersion: "installedver", 345 }, 346 }, 347 }, 348 { 349 ID: "test-cve-id2", 350 Description: "Test CVE ID 2", 351 Title: "Test CVE 2", 352 Severity: "HIGH", 353 PackageList: []packageList{ 354 { 355 Name: "packagename", 356 PackagePath: "/usr/bin/dummy.jar", 357 FixedVersion: "fixedver", 358 InstalledVersion: "installedver", 359 }, 360 { 361 Name: "packagename", 362 PackagePath: "/usr/bin/dummy.gem", 363 FixedVersion: "fixedver", 364 InstalledVersion: "installedver", 365 }, 366 }, 367 }, 368 }, 369 Summary: common.ImageVulnerabilitySummary{ 370 Count: 2, 371 UnknownCount: 0, 372 LowCount: 0, 373 MediumCount: 0, 374 HighCount: 2, 375 CriticalCount: 0, 376 MaxSeverity: "HIGH", 377 }, 378 }, 379 }, 380 }, nil 381 }, 382 }) 383 384 err := SearchCVEForImageGQL(searchConfig, "repo-test", "dummyCVEID") 385 So(err, ShouldBeNil) 386 bufferContent := buff.String() 387 bufferLines := strings.Split(bufferContent, "\n") 388 389 // Expected result - each row indicates a row of the table with reduced spaces 390 expected := []string{ 391 "CRITICAL 0, HIGH 2, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 2", 392 "", 393 "ID SEVERITY TITLE", 394 "dummyCVEID HIGH Title of that CVE", 395 "test-cve-id2 HIGH Test CVE 2", 396 } 397 398 space := regexp.MustCompile(`\s+`) 399 400 for lineIndex := 0; lineIndex < len(expected); lineIndex++ { 401 line := space.ReplaceAllString(bufferLines[lineIndex], " ") 402 So(line, ShouldEqualTrimSpace, expected[lineIndex]) 403 } 404 }) 405 406 Convey("SearchCVEForImageGQL verbose mode", t, func() { 407 buff := bytes.NewBufferString("") 408 searchConfig := getMockSearchConfig(buff, mockService{ 409 getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 410 imageName string, searchedCVE string) (*cveResult, error, 411 ) { 412 return &cveResult{ 413 Data: cveData{ 414 CVEListForImage: cveListForImage{ 415 CVEList: []cve{ 416 { 417 ID: "CVE-100", 418 Description: "", 419 Title: "CVE-100 Title", 420 Severity: "HIGH", 421 PackageList: []packageList{}, 422 }, 423 { 424 ID: "CVE-101", 425 Description: "Desc 101\n", 426 Title: "CVE-101 Title", 427 Severity: "HIGH", 428 PackageList: []packageList{ 429 { 430 Name: "Pkg1", 431 FixedVersion: "2.0.0", 432 InstalledVersion: "1.0.0", 433 }, 434 }, 435 }, 436 { 437 ID: "CVE-102", 438 Description: "Desc 102", 439 Title: "CVE-102 Title", 440 Severity: "HIGH", 441 PackageList: []packageList{ 442 { 443 Name: "dummy-java", 444 PackagePath: "/usr/bin/dummy.jar", 445 FixedVersion: "4.0.0", 446 InstalledVersion: "3.0.0", 447 }, 448 { 449 Name: "dummy-ruby", 450 PackagePath: "/usr/bin/dummy.gem", 451 FixedVersion: "5.0.0", 452 InstalledVersion: "1.0.0", 453 }, 454 }, 455 }, 456 }, 457 Summary: common.ImageVulnerabilitySummary{ 458 Count: 3, 459 UnknownCount: 0, 460 LowCount: 0, 461 MediumCount: 0, 462 HighCount: 3, 463 CriticalCount: 0, 464 MaxSeverity: "HIGH", 465 }, 466 }, 467 }, 468 }, nil 469 }, 470 }) 471 472 searchConfig.Verbose = true 473 err := SearchCVEForImageGQL(searchConfig, "repo-test", "dummyCVEID") 474 So(err, ShouldBeNil) 475 bufferContent := buff.String() 476 bufferLines := strings.Split(bufferContent, "\n") 477 478 // Expected result - each row indicates a line in the output 479 expected := []string{ 480 "CRITICAL 0, HIGH 3, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 3", 481 "", 482 "CVE-100", 483 "Severity: HIGH", 484 "Title: CVE-100 Title", 485 "Description:", 486 "Not Specified", 487 "", 488 "Vulnerable Packages:", 489 "No Vulnerable Packages", 490 "", 491 "", 492 "CVE-101", 493 "Severity: HIGH", 494 "Title: CVE-101 Title", 495 "Description:", 496 "Desc 101", 497 "", 498 "Vulnerable Packages:", 499 " Package Name: Pkg1", 500 " Package Path: ", 501 " Installed Version: 1.0.0", 502 " Fixed Version: 2.0.0", 503 "", 504 "", 505 "CVE-102", 506 "Severity: HIGH", 507 "Title: CVE-102 Title", 508 "Description:", 509 "Desc 102", 510 "", 511 "Vulnerable Packages:", 512 " Package Name: dummy-java", 513 " Package Path: /usr/bin/dummy.jar", 514 " Installed Version: 3.0.0", 515 " Fixed Version: 4.0.0", 516 "", 517 " Package Name: dummy-ruby", 518 " Package Path: /usr/bin/dummy.gem", 519 " Installed Version: 1.0.0", 520 " Fixed Version: 5.0.0", 521 "", 522 "", 523 } 524 525 for index, expectedLine := range expected { 526 So(bufferLines[index], ShouldEqual, expectedLine) 527 } 528 }) 529 530 Convey("SearchCVEForImageGQL with injected error", t, func() { 531 buff := bytes.NewBufferString("") 532 searchConfig := getMockSearchConfig(buff, mockService{ 533 getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username string, password string, 534 imageName string, searchedCVE string) (*cveResult, error, 535 ) { 536 return &cveResult{}, zerr.ErrInjected 537 }, 538 }) 539 540 err := SearchCVEForImageGQL(searchConfig, "repo-test", "dummyCVEID") 541 So(err, ShouldNotBeNil) 542 }) 543 } 544 545 func TestSearchImagesByCVEIDGQL(t *testing.T) { 546 Convey("SearchImagesByCVEIDGQL", t, func() { 547 buff := bytes.NewBufferString("") 548 searchConfig := getMockSearchConfig(buff, mockService{ 549 getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password, 550 imageName, cveID string) (*common.ImagesForCve, error, 551 ) { 552 return &common.ImagesForCve{ 553 ImagesForCVEList: common.ImagesForCVEList{ 554 PaginatedImagesResult: common.PaginatedImagesResult{ 555 Results: []common.ImageSummary{ 556 getMockImageSummary(), 557 }, 558 }, 559 }, 560 }, nil 561 }, 562 }) 563 564 err := SearchImagesByCVEIDGQL(searchConfig, "repo", "CVE-12345") 565 So(err, ShouldBeNil) 566 space := regexp.MustCompile(`\s+`) 567 str := space.ReplaceAllString(buff.String(), " ") 568 actual := strings.TrimSpace(str) 569 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 570 }) 571 572 Convey("SearchImagesByCVEIDGQL error", t, func() { 573 buff := bytes.NewBufferString("") 574 searchConfig := getMockSearchConfig(buff, mockService{ 575 getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password, 576 imageName, cveID string) (*common.ImagesForCve, error, 577 ) { 578 return &common.ImagesForCve{ 579 ImagesForCVEList: common.ImagesForCVEList{ 580 PaginatedImagesResult: common.PaginatedImagesResult{}, 581 }, 582 }, zerr.ErrInjected 583 }, 584 }) 585 586 err := SearchImagesByCVEIDGQL(searchConfig, "repo", "CVE-12345") 587 So(err, ShouldNotBeNil) 588 }) 589 } 590 591 func TestSearchFixedTagsGQL(t *testing.T) { 592 Convey("SearchFixedTagsGQL", t, func() { 593 buff := bytes.NewBufferString("") 594 searchConfig := getMockSearchConfig(buff, mockService{ 595 getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password, 596 imageName, cveID string) (*common.ImageListWithCVEFixedResponse, error, 597 ) { 598 return &common.ImageListWithCVEFixedResponse{ 599 ImageListWithCVEFixed: common.ImageListWithCVEFixed{ 600 PaginatedImagesResult: common.PaginatedImagesResult{ 601 Results: []common.ImageSummary{getMockImageSummary()}, 602 }, 603 }, 604 }, nil 605 }, 606 }) 607 608 err := SearchFixedTagsGQL(searchConfig, "repo", "CVE-12345") 609 So(err, ShouldBeNil) 610 space := regexp.MustCompile(`\s+`) 611 str := space.ReplaceAllString(buff.String(), " ") 612 actual := strings.TrimSpace(str) 613 So(actual, ShouldContainSubstring, "repo tag os/arch 8c25cb36 false 100B") 614 }) 615 616 Convey("SearchFixedTagsGQL error", t, func() { 617 buff := bytes.NewBufferString("") 618 searchConfig := getMockSearchConfig(buff, mockService{ 619 getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password, 620 imageName, cveID string) (*common.ImageListWithCVEFixedResponse, error, 621 ) { 622 return &common.ImageListWithCVEFixedResponse{ 623 ImageListWithCVEFixed: common.ImageListWithCVEFixed{ 624 PaginatedImagesResult: common.PaginatedImagesResult{}, 625 }, 626 }, zerr.ErrInjected 627 }, 628 }) 629 630 err := SearchFixedTagsGQL(searchConfig, "repo", "CVE-12345") 631 So(err, ShouldNotBeNil) 632 }) 633 } 634 635 func TestSearchReferrersGQL(t *testing.T) { 636 Convey("SearchReferrersGQL", t, func() { 637 buff := bytes.NewBufferString("") 638 searchConfig := getMockSearchConfig(buff, mockService{ 639 getReferrersGQLFn: func(ctx context.Context, config SearchConfig, username, password, 640 repo, digest string) (*common.ReferrersResp, error, 641 ) { 642 return &common.ReferrersResp{ 643 ReferrersResult: common.ReferrersResult{ 644 Referrers: []common.Referrer{{ 645 MediaType: ispec.MediaTypeImageManifest, 646 Size: 100, 647 ArtifactType: "art.type", 648 Digest: godigest.FromString("123").String(), 649 }}, 650 }, 651 }, nil 652 }, 653 }) 654 655 err := SearchReferrersGQL(searchConfig, "repo@"+godigest.FromString("str").String()) 656 So(err, ShouldBeNil) 657 space := regexp.MustCompile(`\s+`) 658 str := space.ReplaceAllString(buff.String(), " ") 659 actual := strings.TrimSpace(str) 660 So(actual, ShouldContainSubstring, 661 "art.type 100 B sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3") 662 }) 663 664 Convey("SearchReferrersGQL error", t, func() { 665 buff := bytes.NewBufferString("") 666 searchConfig := getMockSearchConfig(buff, mockService{ 667 getReferrersGQLFn: func(ctx context.Context, config SearchConfig, username, password, 668 repo, digest string) (*common.ReferrersResp, error, 669 ) { 670 return &common.ReferrersResp{}, zerr.ErrInjected 671 }, 672 }) 673 674 err := SearchReferrersGQL(searchConfig, "repo@"+godigest.FromString("str").String()) 675 So(err, ShouldNotBeNil) 676 }) 677 } 678 679 func TestGlobalSearchGQL(t *testing.T) { 680 Convey("GlobalSearchGQL", t, func() { 681 buff := bytes.NewBufferString("") 682 searchConfig := getMockSearchConfig(buff, mockService{ 683 globalSearchGQLFn: func(ctx context.Context, config SearchConfig, username, password, 684 query string) (*common.GlobalSearch, error, 685 ) { 686 return &common.GlobalSearch{ 687 Repos: []common.RepoSummary{{ 688 Name: "repo", 689 Size: "100", 690 LastUpdated: time.Date(2010, 1, 1, 1, 1, 1, 0, time.UTC), 691 }}, 692 }, nil 693 }, 694 }) 695 696 err := GlobalSearchGQL(searchConfig, "repo") 697 So(err, ShouldBeNil) 698 space := regexp.MustCompile(`\s+`) 699 str := space.ReplaceAllString(buff.String(), " ") 700 actual := strings.TrimSpace(str) 701 So(actual, ShouldContainSubstring, 702 "repo ") 703 }) 704 705 Convey("GlobalSearchGQL error", t, func() { 706 buff := bytes.NewBufferString("") 707 searchConfig := getMockSearchConfig(buff, mockService{ 708 globalSearchGQLFn: func(ctx context.Context, config SearchConfig, username, password, 709 query string) (*common.GlobalSearch, error, 710 ) { 711 return &common.GlobalSearch{}, zerr.ErrInjected 712 }, 713 }) 714 715 err := GlobalSearchGQL(searchConfig, "repo") 716 So(err, ShouldNotBeNil) 717 }) 718 } 719 720 func TestSearchReferrers(t *testing.T) { 721 Convey("SearchReferrers", t, func() { 722 buff := bytes.NewBufferString("") 723 searchConfig := getMockSearchConfig(buff, mockService{ 724 getReferrersFn: func(ctx context.Context, config SearchConfig, username string, password string, 725 repo string, digest string) (referrersResult, error, 726 ) { 727 return referrersResult([]common.Referrer{ 728 { 729 MediaType: ispec.MediaTypeImageManifest, 730 Size: 100, 731 ArtifactType: "art.type", 732 Digest: godigest.FromString("123").String(), 733 }, 734 }), nil 735 }, 736 }) 737 738 err := SearchReferrers(searchConfig, "repo@"+godigest.FromString("str").String()) 739 So(err, ShouldBeNil) 740 space := regexp.MustCompile(`\s+`) 741 str := space.ReplaceAllString(buff.String(), " ") 742 actual := strings.TrimSpace(str) 743 So(actual, ShouldContainSubstring, 744 "art.type 100 B sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3") 745 }) 746 747 Convey("SearchReferrers error", t, func() { 748 buff := bytes.NewBufferString("") 749 searchConfig := getMockSearchConfig(buff, mockService{ 750 getReferrersFn: func(ctx context.Context, config SearchConfig, username string, password string, 751 repo string, digest string) (referrersResult, error, 752 ) { 753 return referrersResult{}, zerr.ErrInjected 754 }, 755 }) 756 757 err := SearchReferrers(searchConfig, "repo@"+godigest.FromString("str").String()) 758 So(err, ShouldNotBeNil) 759 }) 760 } 761 762 func TestSearchRepos(t *testing.T) { 763 Convey("SearchRepos", t, func() { 764 buff := bytes.NewBufferString("") 765 searchConfig := getMockSearchConfig(buff, mockService{}) 766 767 err := SearchRepos(searchConfig) 768 So(err, ShouldBeNil) 769 space := regexp.MustCompile(`\s+`) 770 str := space.ReplaceAllString(buff.String(), " ") 771 actual := strings.TrimSpace(str) 772 So(actual, ShouldContainSubstring, "repo1") 773 So(actual, ShouldContainSubstring, "repo2") 774 }) 775 } 776 777 func getMockSearchConfig(buff *bytes.Buffer, mockService mockService) SearchConfig { 778 return SearchConfig{ 779 ResultWriter: buff, 780 User: "", 781 SearchService: mockService, 782 ServURL: "http://127.0.0.1:8000", 783 OutputFormat: "", 784 VerifyTLS: false, 785 FixedFlag: false, 786 Verbose: false, 787 Debug: false, 788 } 789 } 790 791 func getMockImageStruct() imageStruct { 792 return imageStruct(common.ImageSummary{ 793 RepoName: "repo", Tag: "tag", 794 MediaType: ispec.MediaTypeImageManifest, 795 Digest: godigest.FromString("str").String(), 796 Size: "100", 797 Manifests: []common.ManifestSummary{{ 798 Size: "100", 799 Platform: common.Platform{Os: "os", Arch: "arch"}, 800 Digest: godigest.FromString("str").String(), 801 ConfigDigest: godigest.FromString("str").String(), 802 }}, 803 }) 804 } 805 806 func getMockImageSummary() common.ImageSummary { 807 return common.ImageSummary{ 808 RepoName: "repo", Tag: "tag", 809 MediaType: ispec.MediaTypeImageManifest, 810 Digest: godigest.FromString("str").String(), 811 Size: "100", 812 Manifests: []common.ManifestSummary{{ 813 Size: "100", 814 Platform: common.Platform{Os: "os", Arch: "arch"}, 815 Digest: godigest.FromString("str").String(), 816 ConfigDigest: godigest.FromString("str").String(), 817 }}, 818 } 819 } 820 821 func TestUtils(t *testing.T) { 822 Convey("Utils", t, func() { 823 ok := haveSameArgs(field{"query", []struct { 824 Name string `json:"name"` 825 }{ 826 {Name: "arg1"}, {Name: "arg2"}, 827 }}, GQLQuery{ 828 Name: "query", Args: []string{"arg1"}, 829 }) 830 So(ok, ShouldBeFalse) 831 832 ok = haveSameArgs(field{"query", []struct { 833 Name string `json:"name"` 834 }{ 835 {Name: "arg1"}, {Name: "arg2"}, 836 }}, GQLQuery{ 837 Name: "query", Args: []string{"arg1", "arg3"}, 838 }) 839 So(ok, ShouldBeFalse) 840 841 err := containsGQLQueryWithParams( 842 []field{ 843 {Name: "query"}, 844 }, 845 []typeInfo{}, 846 GQLQuery{Name: "other-name"}, 847 ) 848 So(err, ShouldNotBeNil) 849 }) 850 851 Convey("GetConfigOptions", t, func() { 852 // no flags 853 cmd := &cobra.Command{} 854 isSpinner, verifyTLS, err := GetCliConfigOptions(cmd) 855 So(err, ShouldNotBeNil) 856 So(isSpinner, ShouldBeFalse) 857 So(verifyTLS, ShouldBeFalse) 858 859 // bad showspinner 860 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":"bad", "verify-tls": false}]}`) 861 cmd = &cobra.Command{} 862 cmd.Flags().String(ConfigFlag, "imagetest", "") 863 isSpinner, verifyTLS, err = GetCliConfigOptions(cmd) 864 So(err, ShouldNotBeNil) 865 So(isSpinner, ShouldBeFalse) 866 So(verifyTLS, ShouldBeFalse) 867 os.Remove(configPath) 868 869 // bad verify-tls 870 configPath = makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false, "verify-tls": "bad"}]}`) 871 cmd = &cobra.Command{} 872 cmd.Flags().String(ConfigFlag, "imagetest", "") 873 isSpinner, verifyTLS, err = GetCliConfigOptions(cmd) 874 So(err, ShouldNotBeNil) 875 So(isSpinner, ShouldBeFalse) 876 So(verifyTLS, ShouldBeFalse) 877 os.Remove(configPath) 878 }) 879 880 Convey("GetServerURLFromFlags", t, func() { 881 cmd := &cobra.Command{} 882 cmd.Flags().String(URLFlag, "url", "") 883 url, err := GetServerURLFromFlags(cmd) 884 So(url, ShouldResemble, "url") 885 So(err, ShouldBeNil) 886 887 // err no config or url 888 cmd = &cobra.Command{} 889 url, err = GetServerURLFromFlags(cmd) 890 So(url, ShouldResemble, "") 891 So(err, ShouldNotBeNil) 892 893 // err ulr from config is empty 894 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest"}]}`) 895 cmd = &cobra.Command{} 896 cmd.Flags().String(ConfigFlag, "imagetest", "") 897 url, err = GetServerURLFromFlags(cmd) 898 So(url, ShouldResemble, "") 899 So(err, ShouldNotBeNil) 900 os.Remove(configPath) 901 902 // err reading the server url from config 903 configPath = makeConfigFile("{}") 904 cmd = &cobra.Command{} 905 cmd.Flags().String(ConfigFlag, "imagetest", "") 906 url, err = GetServerURLFromFlags(cmd) 907 So(url, ShouldResemble, "") 908 So(err, ShouldNotBeNil) 909 os.Remove(configPath) 910 }) 911 912 Convey("CheckExtEndPointQuery", t, func() { 913 // invalid url 914 err := CheckExtEndPointQuery(SearchConfig{ 915 User: "", 916 ServURL: "bad-url", 917 }) 918 So(err, ShouldNotBeNil) 919 920 // good url but no connection 921 err = CheckExtEndPointQuery(SearchConfig{ 922 User: "", 923 ServURL: "http://127.0.0.1:5000", 924 VerifyTLS: false, 925 Debug: false, 926 ResultWriter: io.Discard, 927 }) 928 So(err, ShouldNotBeNil) 929 }) 930 }