summaryrefslogtreecommitdiffstats
path: root/pkg/v1/google/list_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/v1/google/list_test.go')
-rw-r--r--pkg/v1/google/list_test.go339
1 files changed, 339 insertions, 0 deletions
diff --git a/pkg/v1/google/list_test.go b/pkg/v1/google/list_test.go
new file mode 100644
index 0000000..5718526
--- /dev/null
+++ b/pkg/v1/google/list_test.go
@@ -0,0 +1,339 @@
+// Copyright 2018 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/go-containerregistry/pkg/authn"
+ "github.com/google/go-containerregistry/pkg/logs"
+ "github.com/google/go-containerregistry/pkg/name"
+)
+
+func mustParseDuration(t *testing.T, d string) time.Duration {
+ dur, err := time.ParseDuration(d)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return dur
+}
+
+func TestRoundtrip(t *testing.T) {
+ raw := rawManifestInfo{
+ Size: "100",
+ MediaType: "hi",
+ Created: "12345678",
+ Uploaded: "23456789",
+ Tags: []string{"latest"},
+ }
+
+ og, err := json.Marshal(raw)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ parsed := ManifestInfo{}
+ if err := json.Unmarshal(og, &parsed); err != nil {
+ t.Fatal(err)
+ }
+
+ roundtripped, err := json.Marshal(parsed)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if diff := cmp.Diff(og, roundtripped); diff != "" {
+ t.Errorf("ManifestInfo can't roundtrip: (-want +got) = %s", diff)
+ }
+}
+
+func TestList(t *testing.T) {
+ cases := []struct {
+ name string
+ responseBody []byte
+ wantErr bool
+ wantTags *Tags
+ }{{
+ name: "success",
+ responseBody: []byte(`{"tags":["foo","bar"]}`),
+ wantErr: false,
+ wantTags: &Tags{Tags: []string{"foo", "bar"}},
+ }, {
+ name: "gcr success",
+ responseBody: []byte(`{"child":["hello", "world"],"manifest":{"digest1":{"imageSizeBytes":"1","mediaType":"mainstream","timeCreatedms":"1","timeUploadedMs":"2","tag":["foo"]},"digest2":{"imageSizeBytes":"2","mediaType":"indie","timeCreatedMs":"3","timeUploadedMs":"4","tag":["bar","baz"]}},"tags":["foo","bar","baz"]}`),
+ wantErr: false,
+ wantTags: &Tags{
+ Children: []string{"hello", "world"},
+ Manifests: map[string]ManifestInfo{
+ "digest1": {
+ Size: 1,
+ MediaType: "mainstream",
+ Created: time.Unix(0, 0).Add(mustParseDuration(t, "1ms")),
+ Uploaded: time.Unix(0, 0).Add(mustParseDuration(t, "2ms")),
+ Tags: []string{"foo"},
+ },
+ "digest2": {
+ Size: 2,
+ MediaType: "indie",
+ Created: time.Unix(0, 0).Add(mustParseDuration(t, "3ms")),
+ Uploaded: time.Unix(0, 0).Add(mustParseDuration(t, "4ms")),
+ Tags: []string{"bar", "baz"},
+ },
+ },
+ Tags: []string{"foo", "bar", "baz"},
+ },
+ }, {
+ name: "just children",
+ responseBody: []byte(`{"child":["hello", "world"]}`),
+ wantErr: false,
+ wantTags: &Tags{
+ Children: []string{"hello", "world"},
+ },
+ }, {
+ name: "not json",
+ responseBody: []byte("notjson"),
+ wantErr: true,
+ }}
+
+ repoName := "ubuntu"
+ // To test WithUserAgent
+ uaSentinel := "this-is-the-user-agent"
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ tagsPath := fmt.Sprintf("/v2/%s/tags/list", repoName)
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if got, want := r.Header.Get("User-Agent"), uaSentinel; !strings.Contains(got, want) {
+ t.Errorf("request did not container useragent, got %q want Contains(%q)", got, want)
+ }
+ switch r.URL.Path {
+ case "/v2/":
+ w.WriteHeader(http.StatusOK)
+ case tagsPath:
+ if r.Method != http.MethodGet {
+ t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
+ }
+
+ w.Write(tc.responseBody)
+ default:
+ t.Fatalf("Unexpected path: %v", r.URL.Path)
+ }
+ }))
+ defer server.Close()
+ u, err := url.Parse(server.URL)
+ if err != nil {
+ t.Fatalf("url.Parse(%v) = %v", server.URL, err)
+ }
+
+ repo, err := name.NewRepository(fmt.Sprintf("%s/%s", u.Host, repoName), name.WeakValidation)
+ if err != nil {
+ t.Fatalf("name.NewRepository(%v) = %v", repoName, err)
+ }
+
+ tags, err := List(repo, WithAuthFromKeychain(authn.DefaultKeychain), WithTransport(http.DefaultTransport), WithUserAgent(uaSentinel), WithContext(context.Background()))
+ if (err != nil) != tc.wantErr {
+ t.Errorf("List() wrong error: %v, want %v: %v\n", (err != nil), tc.wantErr, err)
+ }
+
+ if diff := cmp.Diff(tc.wantTags, tags); diff != "" {
+ t.Errorf("List() wrong tags (-want +got) = %s", diff)
+ }
+ })
+ }
+}
+
+type recorder struct {
+ Tags []*Tags
+ Errs []error
+}
+
+func (r *recorder) walk(repo name.Repository, tags *Tags, err error) error {
+ r.Tags = append(r.Tags, tags)
+ r.Errs = append(r.Errs, err)
+
+ return nil
+}
+
+func TestWalk(t *testing.T) {
+ // Stupid coverage to make sure it doesn't panic.
+ var b bytes.Buffer
+ logs.Debug.SetOutput(&b)
+
+ cases := []struct {
+ name string
+ responseBody []byte
+ wantResult recorder
+ }{{
+ name: "gcr success",
+ responseBody: []byte(`{"child":["hello", "world"],"manifest":{"digest1":{"imageSizeBytes":"1","mediaType":"mainstream","timeCreatedms":"1","timeUploadedMs":"2","tag":["foo"]},"digest2":{"imageSizeBytes":"2","mediaType":"indie","timeCreatedMs":"3","timeUploadedMs":"4","tag":["bar","baz"]}},"tags":["foo","bar","baz"]}`),
+ wantResult: recorder{
+ Tags: []*Tags{{
+ Children: []string{"hello", "world"},
+ Manifests: map[string]ManifestInfo{
+ "digest1": {
+ Size: 1,
+ MediaType: "mainstream",
+ Created: time.Unix(0, 0).Add(mustParseDuration(t, "1ms")),
+ Uploaded: time.Unix(0, 0).Add(mustParseDuration(t, "2ms")),
+ Tags: []string{"foo"},
+ },
+ "digest2": {
+ Size: 2,
+ MediaType: "indie",
+ Created: time.Unix(0, 0).Add(mustParseDuration(t, "3ms")),
+ Uploaded: time.Unix(0, 0).Add(mustParseDuration(t, "4ms")),
+ Tags: []string{"bar", "baz"},
+ },
+ },
+ Tags: []string{"foo", "bar", "baz"},
+ }, {
+ Tags: []string{"hello"},
+ }, {
+ Tags: []string{"world"},
+ }},
+ Errs: []error{nil, nil, nil},
+ },
+ }}
+
+ repoName := "ubuntu"
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ rootPath := fmt.Sprintf("/v2/%s/tags/list", repoName)
+ helloPath := fmt.Sprintf("/v2/%s/hello/tags/list", repoName)
+ worldPath := fmt.Sprintf("/v2/%s/world/tags/list", repoName)
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/v2/":
+ w.WriteHeader(http.StatusOK)
+ case rootPath:
+ if r.Method != http.MethodGet {
+ t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
+ }
+
+ w.Write(tc.responseBody)
+ case helloPath:
+ w.Write([]byte(`{"tags":["hello"]}`))
+ case worldPath:
+ w.Write([]byte(`{"tags":["world"]}`))
+ default:
+ t.Fatalf("Unexpected path: %v", r.URL.Path)
+ }
+ }))
+ defer server.Close()
+ u, err := url.Parse(server.URL)
+ if err != nil {
+ t.Fatalf("url.Parse(%v) = %v", server.URL, err)
+ }
+
+ repo, err := name.NewRepository(fmt.Sprintf("%s/%s", u.Host, repoName), name.WeakValidation)
+ if err != nil {
+ t.Fatalf("name.NewRepository(%v) = %v", repoName, err)
+ }
+
+ r := recorder{}
+ if err := Walk(repo, r.walk, WithAuth(authn.Anonymous)); err != nil {
+ t.Errorf("unexpected err: %v", err)
+ }
+
+ if diff := cmp.Diff(tc.wantResult, r); diff != "" {
+ t.Errorf("Walk() wrong tags (-want +got) = %s", diff)
+ }
+ })
+ }
+}
+
+// Copied shamelessly from remote.
+func TestCancelledList(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+
+ repoName := "doesnotmatter"
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/v2/":
+ w.WriteHeader(http.StatusOK)
+ default:
+ t.Fatalf("Unexpected path: %v", r.URL.Path)
+ }
+ }))
+ defer server.Close()
+ u, err := url.Parse(server.URL)
+ if err != nil {
+ t.Fatalf("url.Parse(%v) = %v", server.URL, err)
+ }
+
+ repo, err := name.NewRepository(fmt.Sprintf("%s/%s", u.Host, repoName), name.WeakValidation)
+ if err != nil {
+ t.Fatalf("name.NewRepository(%v) = %v", repoName, err)
+ }
+
+ _, err = List(repo, WithContext(ctx))
+ if !strings.Contains(err.Error(), context.Canceled.Error()) {
+ t.Errorf("wanted %q to contain %q", err.Error(), context.Canceled.Error())
+ }
+}
+
+func makeResp(hdr string) *http.Response {
+ return &http.Response{
+ Header: http.Header{
+ "Link": []string{hdr},
+ },
+ }
+}
+
+func TestGetNextPageURL(t *testing.T) {
+ for _, hdr := range []string{
+ "",
+ "<",
+ "><",
+ "<>",
+ fmt.Sprintf("<%c>", 0x7f), // makes url.Parse fail
+ } {
+ u, err := getNextPageURL(makeResp(hdr))
+ if err == nil && u != nil {
+ t.Errorf("Expected err, got %+v", u)
+ }
+ }
+
+ good := &http.Response{
+ Header: http.Header{
+ "Link": []string{"<example.com>"},
+ },
+ Request: &http.Request{
+ URL: &url.URL{
+ Scheme: "https",
+ },
+ },
+ }
+ u, err := getNextPageURL(good)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if u.Scheme != "https" {
+ t.Errorf("expected scheme to match request, got %s", u.Scheme)
+ }
+}