hub_test.go RawHistoryFolder
1// SPDX-License-Identifier: MIT
2
3package registryproxy
4
5import (
6 "bytes"
7 "encoding/json"
8 "net/http/httptest"
9 "os"
10 "os/exec"
11 "strings"
12 "testing"
13
14 "github.com/bnema/zerowrap"
15)
16
17// An opt-in manual check using curl against a real local HTTP listener, with
18// Docker Hub as upstream. No daemon is required.
19func TestDockerHubCurl(t *testing.T) {
20 if os.Getenv("ERMITE_RUNNER_IT_REGISTRY") != "1" {
21 t.Skip("set ERMITE_RUNNER_IT_REGISTRY=1 for live Docker Hub curl check")
22 }
23 var logs bytes.Buffer
24 p, err := New(Config{Dir: t.TempDir(), Max: 64 << 20, Log: zerowrap.New(zerowrap.Config{Level: "debug", Format: "json", Output: &logs})})
25 if err != nil {
26 t.Fatal(err)
27 }
28 srv := httptest.NewServer(p)
29 defer srv.Close()
30 curl := func(path string) []byte {
31 t.Helper()
32 out, err := exec.Command("curl", "--max-time", "20", "-fsS", "-H", "Accept: application/vnd.oci.image.index.v1+json, application/vnd.oci.image.manifest.v1+json", srv.URL+path).CombinedOutput()
33 if err != nil {
34 t.Fatalf("curl %s: %v: %s", path, err, out)
35 }
36 t.Logf("curl %s: HTTP 200, %d bytes", path, len(out))
37 return out
38 }
39 var idx struct {
40 Manifests []struct {
41 Digest string
42 Platform struct{ Architecture, OS string }
43 }
44 }
45 b := curl("/v2/library/alpine/manifests/3")
46 if err := json.Unmarshal(b, &idx); err != nil {
47 t.Fatal(err)
48 }
49 if !bytes.Equal(b, curl("/v2/library/alpine/manifests/3")) {
50 t.Fatal("tag changed during check")
51 }
52 var digest string
53 for _, m := range idx.Manifests {
54 if m.Platform.Architecture == "amd64" && m.Platform.OS == "linux" {
55 digest = m.Digest
56 break
57 }
58 }
59 if !validDigest(digest) {
60 t.Fatal("no linux/amd64 manifest")
61 }
62 var manifest struct{ Config struct{ Digest string } }
63 if err := json.Unmarshal(curl("/v2/library/alpine/manifests/"+digest), &manifest); err != nil {
64 t.Fatal(err)
65 }
66 if !validDigest(manifest.Config.Digest) {
67 t.Fatal("no config blob")
68 }
69 path := "/v2/library/alpine/blobs/" + manifest.Config.Digest
70 first := curl(path)
71 second := curl(path)
72 if !bytes.Equal(first, second) {
73 t.Fatal("blob differs")
74 }
75 all := logs.String()
76 t.Log(all)
77 if !strings.Contains(all, `"hit":true`) || strings.Count(all, `"message":"registry blob"`) != 1 {
78 t.Fatal("missing cache hits or repeated blob miss")
79 }
80}