zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/extensions/sync/httpclient/client_internal_test.go (about) 1 package client 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "testing" 7 "time" 8 9 . "github.com/smartystreets/goconvey/convey" 10 11 "zotregistry.dev/zot/pkg/log" 12 ) 13 14 func TestTokenCache(t *testing.T) { 15 Convey("Get/Set tokens", t, func() { 16 tokenCache := NewTokenCache() 17 token := &bearerToken{ 18 Token: "tokenA", 19 ExpiresIn: 3, 20 IssuedAt: time.Now(), 21 } 22 23 token.expirationTime = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second).Add(tokenBuffer) 24 25 tokenCache.Set("repo", token) 26 cachedToken := tokenCache.Get("repo") 27 So(cachedToken.Token, ShouldEqual, token.Token) 28 29 // add token which expires soon 30 token2 := &bearerToken{ 31 Token: "tokenB", 32 ExpiresIn: 1, 33 IssuedAt: time.Now(), 34 } 35 36 token2.expirationTime = token2.IssuedAt.Add(time.Duration(token2.ExpiresIn) * time.Second).Add(tokenBuffer) 37 38 tokenCache.Set("repo2", token2) 39 cachedToken = tokenCache.Get("repo2") 40 So(cachedToken.Token, ShouldEqual, token2.Token) 41 42 time.Sleep(1 * time.Second) 43 44 // token3 should be expired when adding a new one 45 token3 := &bearerToken{ 46 Token: "tokenC", 47 ExpiresIn: 3, 48 IssuedAt: time.Now(), 49 } 50 51 token3.expirationTime = token3.IssuedAt.Add(time.Duration(token3.ExpiresIn) * time.Second).Add(tokenBuffer) 52 53 tokenCache.Set("repo3", token3) 54 cachedToken = tokenCache.Get("repo3") 55 So(cachedToken.Token, ShouldEqual, token3.Token) 56 57 // token2 should be expired 58 token = tokenCache.Get("repo2") 59 So(token, ShouldBeNil) 60 61 time.Sleep(2 * time.Second) 62 63 // the rest of them should also be expired 64 tokenCache.Set("repo4", &bearerToken{ 65 Token: "tokenD", 66 }) 67 68 // token1 should be expired 69 token = tokenCache.Get("repo1") 70 So(token, ShouldBeNil) 71 }) 72 73 Convey("Error paths", t, func() { 74 tokenCache := NewTokenCache() 75 token := tokenCache.Get("repo") 76 So(token, ShouldBeNil) 77 78 tokenCache = nil 79 token = tokenCache.Get("repo") 80 So(token, ShouldBeNil) 81 82 tokenCache = NewTokenCache() 83 tokenCache.Set("repo", nil) 84 token = tokenCache.Get("repo") 85 So(token, ShouldBeNil) 86 }) 87 } 88 89 func TestNeedsRetryOnInsuficientScope(t *testing.T) { 90 resp := http.Response{ 91 Status: "401 Unauthorized", 92 StatusCode: http.StatusUnauthorized, 93 Proto: "HTTP/1.1", 94 ProtoMajor: 1, 95 ProtoMinor: 1, 96 Header: map[string][]string{ 97 "Content-Length": {"145"}, 98 "Content-Type": {"application/json"}, 99 "Date": {"Fri, 26 Aug 2022 08:03:13 GMT"}, 100 "X-Content-Type-Options": {"nosniff"}, 101 }, 102 Request: nil, 103 } 104 105 Convey("Test client retries on insufficient scope", t, func() { 106 resp.Header["Www-Authenticate"] = []string{ 107 `Bearer realm="https://registry.suse.com/auth",service="SUSE Linux Docker Registry"` + 108 `,scope="registry:catalog:*",error="insufficient_scope"`, 109 } 110 111 expectedScope := "registry:catalog:*" 112 expectedRealm := "https://registry.suse.com/auth" 113 expectedService := "SUSE Linux Docker Registry" 114 115 needsRetry, params := needsRetryWithUpdatedScope(nil, &resp) 116 117 So(needsRetry, ShouldBeTrue) 118 So(params.scope, ShouldEqual, expectedScope) 119 So(params.realm, ShouldEqual, expectedRealm) 120 So(params.service, ShouldEqual, expectedService) 121 }) 122 123 Convey("Test client fails on insufficient scope", t, func() { 124 resp.Header["Www-Authenticate"] = []string{ 125 `Bearer realm="https://registry.suse.com/auth=error"`, 126 } 127 128 needsRetry, _ := needsRetryWithUpdatedScope(nil, &resp) 129 So(needsRetry, ShouldBeFalse) 130 }) 131 } 132 133 func TestClient(t *testing.T) { 134 Convey("Test client", t, func() { 135 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 136 w.WriteHeader(http.StatusInternalServerError) 137 })) 138 defer server.Close() 139 140 client, err := New(Config{ 141 URL: server.URL, 142 TLSVerify: false, 143 }, log.NewLogger("", "")) 144 So(err, ShouldBeNil) 145 146 Convey("Test Ping() fails", func() { 147 ok := client.Ping() 148 So(ok, ShouldBeFalse) 149 }) 150 151 Convey("Test makeAndDoRequest() fails", func() { 152 client.authType = tokenAuth 153 //nolint: bodyclose 154 _, _, err := client.makeAndDoRequest(http.MethodGet, "application/json", "catalog", server.URL) 155 So(err, ShouldNotBeNil) 156 }) 157 158 Convey("Test setupAuth() fails", func() { 159 request, err := http.NewRequest(http.MethodGet, server.URL, nil) //nolint: noctx 160 So(err, ShouldBeNil) 161 162 client.authType = tokenAuth 163 err = client.setupAuth(request, "catalog") 164 So(err, ShouldNotBeNil) 165 }) 166 }) 167 }