| package registry // import "github.com/docker/docker/registry" |
| |
| import ( |
| "context" |
| "encoding/json" |
| "net/http" |
| "net/http/httptest" |
| "testing" |
| |
| "github.com/docker/docker/api/types/filters" |
| "github.com/docker/docker/api/types/registry" |
| "github.com/docker/docker/errdefs" |
| "gotest.tools/v3/assert" |
| ) |
| |
| func TestSearchErrors(t *testing.T) { |
| errorCases := []struct { |
| filtersArgs filters.Args |
| shouldReturnError bool |
| expectedError string |
| }{ |
| { |
| expectedError: "Unexpected status code 500", |
| shouldReturnError: true, |
| }, |
| { |
| filtersArgs: filters.NewArgs(filters.Arg("type", "custom")), |
| expectedError: "invalid filter 'type'", |
| }, |
| { |
| filtersArgs: filters.NewArgs(filters.Arg("is-automated", "invalid")), |
| expectedError: "invalid filter 'is-automated=[invalid]'", |
| }, |
| { |
| filtersArgs: filters.NewArgs( |
| filters.Arg("is-automated", "true"), |
| filters.Arg("is-automated", "false"), |
| ), |
| expectedError: "invalid filter 'is-automated", |
| }, |
| { |
| filtersArgs: filters.NewArgs(filters.Arg("is-official", "invalid")), |
| expectedError: "invalid filter 'is-official=[invalid]'", |
| }, |
| { |
| filtersArgs: filters.NewArgs( |
| filters.Arg("is-official", "true"), |
| filters.Arg("is-official", "false"), |
| ), |
| expectedError: "invalid filter 'is-official", |
| }, |
| { |
| filtersArgs: filters.NewArgs(filters.Arg("stars", "invalid")), |
| expectedError: "invalid filter 'stars=invalid'", |
| }, |
| { |
| filtersArgs: filters.NewArgs( |
| filters.Arg("stars", "1"), |
| filters.Arg("stars", "invalid"), |
| ), |
| expectedError: "invalid filter 'stars=invalid'", |
| }, |
| } |
| for _, tc := range errorCases { |
| tc := tc |
| t.Run(tc.expectedError, func(t *testing.T) { |
| srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| if !tc.shouldReturnError { |
| t.Errorf("unexpected HTTP request") |
| } |
| http.Error(w, "no search for you", http.StatusInternalServerError) |
| })) |
| defer srv.Close() |
| |
| // Construct the search term by cutting the 'http://' prefix off srv.URL. |
| term := srv.URL[7:] + "/term" |
| |
| reg, err := NewService(ServiceOptions{}) |
| assert.NilError(t, err) |
| _, err = reg.Search(context.Background(), tc.filtersArgs, term, 0, nil, map[string][]string{}) |
| assert.ErrorContains(t, err, tc.expectedError) |
| if tc.shouldReturnError { |
| assert.Check(t, errdefs.IsUnknown(err), "got: %T: %v", err, err) |
| return |
| } |
| assert.Check(t, errdefs.IsInvalidParameter(err), "got: %T: %v", err, err) |
| }) |
| } |
| } |
| |
| func TestSearch(t *testing.T) { |
| const term = "term" |
| successCases := []struct { |
| name string |
| filtersArgs filters.Args |
| registryResults []registry.SearchResult |
| expectedResults []registry.SearchResult |
| }{ |
| { |
| name: "empty results", |
| registryResults: []registry.SearchResult{}, |
| expectedResults: []registry.SearchResult{}, |
| }, |
| { |
| name: "no filter", |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| }, |
| }, |
| }, |
| { |
| name: "is-automated=true, no results", |
| filtersArgs: filters.NewArgs(filters.Arg("is-automated", "true")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| }, |
| }, |
| expectedResults: []registry.SearchResult{}, |
| }, |
| { |
| name: "is-automated=true", |
| filtersArgs: filters.NewArgs(filters.Arg("is-automated", "true")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsAutomated: true, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsAutomated: true, |
| }, |
| }, |
| }, |
| { |
| name: "is-automated=false, no results", |
| filtersArgs: filters.NewArgs(filters.Arg("is-automated", "false")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsAutomated: true, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{}, |
| }, |
| { |
| name: "is-automated=false", |
| filtersArgs: filters.NewArgs(filters.Arg("is-automated", "false")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsAutomated: false, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsAutomated: false, |
| }, |
| }, |
| }, |
| { |
| name: "is-official=true, no results", |
| filtersArgs: filters.NewArgs(filters.Arg("is-official", "true")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| }, |
| }, |
| expectedResults: []registry.SearchResult{}, |
| }, |
| { |
| name: "is-official=true", |
| filtersArgs: filters.NewArgs(filters.Arg("is-official", "true")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsOfficial: true, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsOfficial: true, |
| }, |
| }, |
| }, |
| { |
| name: "is-official=false, no results", |
| filtersArgs: filters.NewArgs(filters.Arg("is-official", "false")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsOfficial: true, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{}, |
| }, |
| { |
| name: "is-official=false", |
| filtersArgs: filters.NewArgs(filters.Arg("is-official", "false")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsOfficial: false, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| IsOfficial: false, |
| }, |
| }, |
| }, |
| { |
| name: "stars=0", |
| filtersArgs: filters.NewArgs(filters.Arg("stars", "0")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| StarCount: 0, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| StarCount: 0, |
| }, |
| }, |
| }, |
| { |
| name: "stars=0, no results", |
| filtersArgs: filters.NewArgs(filters.Arg("stars", "1")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name", |
| Description: "description", |
| StarCount: 0, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{}, |
| }, |
| { |
| name: "stars=1", |
| filtersArgs: filters.NewArgs(filters.Arg("stars", "1")), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name0", |
| Description: "description0", |
| StarCount: 0, |
| }, |
| { |
| Name: "name1", |
| Description: "description1", |
| StarCount: 1, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name1", |
| Description: "description1", |
| StarCount: 1, |
| }, |
| }, |
| }, |
| { |
| name: "stars=1, is-official=true, is-automated=true", |
| filtersArgs: filters.NewArgs( |
| filters.Arg("stars", "1"), |
| filters.Arg("is-official", "true"), |
| filters.Arg("is-automated", "true"), |
| ), |
| registryResults: []registry.SearchResult{ |
| { |
| Name: "name0", |
| Description: "description0", |
| StarCount: 0, |
| IsOfficial: true, |
| IsAutomated: true, |
| }, |
| { |
| Name: "name1", |
| Description: "description1", |
| StarCount: 1, |
| IsOfficial: false, |
| IsAutomated: true, |
| }, |
| { |
| Name: "name2", |
| Description: "description2", |
| StarCount: 1, |
| IsOfficial: true, |
| IsAutomated: false, |
| }, |
| { |
| Name: "name3", |
| Description: "description3", |
| StarCount: 2, |
| IsOfficial: true, |
| IsAutomated: true, |
| }, |
| }, |
| expectedResults: []registry.SearchResult{ |
| { |
| Name: "name3", |
| Description: "description3", |
| StarCount: 2, |
| IsOfficial: true, |
| IsAutomated: true, |
| }, |
| }, |
| }, |
| } |
| for _, tc := range successCases { |
| tc := tc |
| t.Run(tc.name, func(t *testing.T) { |
| srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-type", "application/json") |
| json.NewEncoder(w).Encode(registry.SearchResults{ |
| Query: term, |
| NumResults: len(tc.registryResults), |
| Results: tc.registryResults, |
| }) |
| })) |
| defer srv.Close() |
| |
| // Construct the search term by cutting the 'http://' prefix off srv.URL. |
| searchTerm := srv.URL[7:] + "/" + term |
| |
| reg, err := NewService(ServiceOptions{}) |
| assert.NilError(t, err) |
| results, err := reg.Search(context.Background(), tc.filtersArgs, searchTerm, 0, nil, map[string][]string{}) |
| assert.NilError(t, err) |
| assert.DeepEqual(t, results, tc.expectedResults) |
| }) |
| } |
| } |