github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/registry/remote/registry_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package remote
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"net/http"
    23  	"net/http/httptest"
    24  	"net/url"
    25  	"reflect"
    26  	"strconv"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/opcr-io/oras-go/v2/errdef"
    31  	"github.com/opcr-io/oras-go/v2/registry"
    32  )
    33  
    34  func TestRegistryInterface(t *testing.T) {
    35  	var reg interface{} = &Registry{}
    36  	if _, ok := reg.(registry.Registry); !ok {
    37  		t.Error("&Registry{} does not conform registry.Registry")
    38  	}
    39  }
    40  
    41  func TestRegistry_TLS(t *testing.T) {
    42  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    43  		if r.Method != http.MethodGet || r.URL.Path != "/v2/" {
    44  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    45  			w.WriteHeader(http.StatusNotFound)
    46  			return
    47  		}
    48  	}))
    49  	defer ts.Close()
    50  	uri, err := url.Parse(ts.URL)
    51  	if err != nil {
    52  		t.Fatalf("invalid test http server: %v", err)
    53  	}
    54  
    55  	reg, err := NewRegistry(uri.Host)
    56  	if err != nil {
    57  		t.Fatalf("NewRegistry() error = %v", err)
    58  	}
    59  	reg.Client = ts.Client()
    60  
    61  	ctx := context.Background()
    62  	if err := reg.Ping(ctx); err != nil {
    63  		t.Errorf("Registry.Ping() error = %v", err)
    64  	}
    65  }
    66  
    67  func TestRegistry_Ping(t *testing.T) {
    68  	v2Implemented := true
    69  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    70  		if r.Method != http.MethodGet || r.URL.Path != "/v2/" {
    71  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    72  			w.WriteHeader(http.StatusNotFound)
    73  			return
    74  		}
    75  
    76  		if v2Implemented {
    77  			w.WriteHeader(http.StatusOK)
    78  		} else {
    79  			w.WriteHeader(http.StatusNotFound)
    80  		}
    81  	}))
    82  	defer ts.Close()
    83  	uri, err := url.Parse(ts.URL)
    84  	if err != nil {
    85  		t.Fatalf("invalid test http server: %v", err)
    86  	}
    87  
    88  	reg, err := NewRegistry(uri.Host)
    89  	if err != nil {
    90  		t.Fatalf("NewRegistry() error = %v", err)
    91  	}
    92  	reg.PlainHTTP = true
    93  
    94  	ctx := context.Background()
    95  	if err := reg.Ping(ctx); err != nil {
    96  		t.Errorf("Registry.Ping() error = %v", err)
    97  	}
    98  
    99  	v2Implemented = false
   100  	if err := reg.Ping(ctx); err == nil {
   101  		t.Errorf("Registry.Ping() error = %v, wantErr %v", err, errdef.ErrNotFound)
   102  	}
   103  }
   104  
   105  func TestRegistry_Repositories(t *testing.T) {
   106  	repoSet := [][]string{
   107  		{"the", "quick", "brown", "fox"},
   108  		{"jumps", "over", "the", "lazy"},
   109  		{"dog"},
   110  	}
   111  	var ts *httptest.Server
   112  	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   113  		if r.Method != http.MethodGet || r.URL.Path != "/v2/_catalog" {
   114  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   115  			w.WriteHeader(http.StatusNotFound)
   116  			return
   117  		}
   118  		q := r.URL.Query()
   119  		n, err := strconv.Atoi(q.Get("n"))
   120  		if err != nil || n != 4 {
   121  			t.Errorf("bad page size: %s", q.Get("n"))
   122  			w.WriteHeader(http.StatusBadRequest)
   123  			return
   124  		}
   125  		var repos []string
   126  		switch q.Get("test") {
   127  		case "foo":
   128  			repos = repoSet[1]
   129  			w.Header().Set("Link", fmt.Sprintf(`<%s/v2/_catalog?n=4&test=bar>; rel="next"`, ts.URL))
   130  		case "bar":
   131  			repos = repoSet[2]
   132  		default:
   133  			repos = repoSet[0]
   134  			w.Header().Set("Link", `</v2/_catalog?n=4&test=foo>; rel="next"`)
   135  		}
   136  		result := struct {
   137  			Repositories []string `json:"repositories"`
   138  		}{
   139  			Repositories: repos,
   140  		}
   141  		if err := json.NewEncoder(w).Encode(result); err != nil {
   142  			t.Errorf("failed to write response: %v", err)
   143  		}
   144  	}))
   145  	defer ts.Close()
   146  	uri, err := url.Parse(ts.URL)
   147  	if err != nil {
   148  		t.Fatalf("invalid test http server: %v", err)
   149  	}
   150  
   151  	reg, err := NewRegistry(uri.Host)
   152  	if err != nil {
   153  		t.Fatalf("NewRegistry() error = %v", err)
   154  	}
   155  	reg.PlainHTTP = true
   156  	reg.RepositoryListPageSize = 4
   157  
   158  	ctx := context.Background()
   159  	index := 0
   160  	if err := reg.Repositories(ctx, "", func(got []string) error {
   161  		if index > 2 {
   162  			t.Fatalf("out of index bound: %d", index)
   163  		}
   164  		repos := repoSet[index]
   165  		index++
   166  		if !reflect.DeepEqual(got, repos) {
   167  			t.Errorf("Registry.Repositories() = %v, want %v", got, repos)
   168  		}
   169  		return nil
   170  	}); err != nil {
   171  		t.Fatalf("Registry.Repositories() error = %v", err)
   172  	}
   173  }
   174  
   175  func TestRegistry_Repository(t *testing.T) {
   176  	reg, err := NewRegistry("localhost:5000")
   177  	if err != nil {
   178  		t.Fatalf("NewRegistry() error = %v", err)
   179  	}
   180  	reg.PlainHTTP = true
   181  	reg.RepositoryListPageSize = 50
   182  	reg.TagListPageSize = 100
   183  	reg.ReferrerListPageSize = 10
   184  	reg.MaxMetadataBytes = 8 * 1024 * 1024
   185  
   186  	ctx := context.Background()
   187  	got, err := reg.Repository(ctx, "hello-world")
   188  	if err != nil {
   189  		t.Fatalf("Registry.Repository() error = %v", err)
   190  	}
   191  	reg.Reference.Repository = "hello-world"
   192  	want := (*Repository)(&reg.RepositoryOptions)
   193  	if !reflect.DeepEqual(got, want) {
   194  		t.Errorf("Registry.Repository() = %v, want %v", got, want)
   195  	}
   196  }
   197  
   198  // Testing `last` parameter for Repositories list
   199  func TestRegistry_Repositories_WithLastParam(t *testing.T) {
   200  	repoSet := strings.Split("abcdefghijklmnopqrstuvwxyz", "")
   201  	var offset int
   202  	var ts *httptest.Server
   203  	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   204  		if r.Method != http.MethodGet || r.URL.Path != "/v2/_catalog" {
   205  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   206  			w.WriteHeader(http.StatusNotFound)
   207  			return
   208  		}
   209  		q := r.URL.Query()
   210  		n, err := strconv.Atoi(q.Get("n"))
   211  		if err != nil || n != 4 {
   212  			t.Errorf("bad page size: %s", q.Get("n"))
   213  			w.WriteHeader(http.StatusBadRequest)
   214  			return
   215  		}
   216  		last := q.Get("last")
   217  		if last != "" {
   218  			offset = indexOf(last, repoSet) + 1
   219  		}
   220  		var repos []string
   221  		switch q.Get("test") {
   222  		case "foo":
   223  			repos = repoSet[offset : offset+n]
   224  			w.Header().Set("Link", fmt.Sprintf(`<%s/v2/_catalog?n=4&last=v&test=bar>; rel="next"`, ts.URL))
   225  		case "bar":
   226  			repos = repoSet[offset : offset+n]
   227  		default:
   228  			repos = repoSet[offset : offset+n]
   229  			w.Header().Set("Link", fmt.Sprintf(`<%s/v2/_catalog?n=4&last=r&test=foo>; rel="next"`, ts.URL))
   230  		}
   231  		result := struct {
   232  			Repositories []string `json:"repositories"`
   233  		}{
   234  			Repositories: repos,
   235  		}
   236  		if err := json.NewEncoder(w).Encode(result); err != nil {
   237  			t.Errorf("failed to write response: %v", err)
   238  		}
   239  	}))
   240  	defer ts.Close()
   241  	uri, err := url.Parse(ts.URL)
   242  	if err != nil {
   243  		t.Fatalf("invalid test http server: %v", err)
   244  	}
   245  
   246  	reg, err := NewRegistry(uri.Host)
   247  	if err != nil {
   248  		t.Fatalf("NewRegistry() error = %v", err)
   249  	}
   250  	reg.PlainHTTP = true
   251  	reg.RepositoryListPageSize = 4
   252  	last := "n"
   253  	startInd := indexOf(last, repoSet) + 1
   254  
   255  	ctx := context.Background()
   256  	if err := reg.Repositories(ctx, last, func(got []string) error {
   257  		want := repoSet[startInd : startInd+reg.RepositoryListPageSize]
   258  		startInd += reg.RepositoryListPageSize
   259  		if !reflect.DeepEqual(got, want) {
   260  			t.Errorf("Registry.Repositories() = %v, want %v", got, want)
   261  		}
   262  		return nil
   263  	}); err != nil {
   264  		t.Fatalf("Registry.Repositories() error = %v", err)
   265  	}
   266  }
   267  
   268  // indexOf returns the index of an element within a slice
   269  func indexOf(element string, data []string) int {
   270  	for ind, val := range data {
   271  		if element == val {
   272  			return ind
   273  		}
   274  	}
   275  	return -1 //not found.
   276  }