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)(®.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 }