go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/gerrit/prod_test.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 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 package gerrit 16 17 import ( 18 "context" 19 "fmt" 20 "net/http" 21 "net/http/httptest" 22 "net/url" 23 "testing" 24 "time" 25 26 "go.chromium.org/luci/common/clock/testclock" 27 "go.chromium.org/luci/common/errors" 28 gerritpb "go.chromium.org/luci/common/proto/gerrit" 29 "go.chromium.org/luci/gae/service/datastore" 30 "go.chromium.org/luci/server/auth" 31 "go.chromium.org/luci/server/auth/authtest" 32 33 . "github.com/smartystreets/goconvey/convey" 34 . "go.chromium.org/luci/common/testing/assertions" 35 ) 36 37 func TestMakeClient(t *testing.T) { 38 t.Parallel() 39 40 Convey("With mocked token server", t, func() { 41 epoch := datastore.RoundTime(testclock.TestRecentTimeUTC) 42 ctx, tclock := testclock.UseTime(context.Background(), testclock.TestRecentTimeUTC) 43 ctx = authtest.MockAuthConfig(ctx) 44 45 const gHost = "first.example.com" 46 47 f, err := newProd(ctx) 48 So(err, ShouldBeNil) 49 50 Convey("factory.token", func() { 51 Convey("works", func() { 52 f.mockMintProjectToken = func(context.Context, auth.ProjectTokenParams) (*auth.Token, error) { 53 return &auth.Token{Token: "tok-1", Expiry: epoch.Add(2 * time.Minute)}, nil 54 } 55 t, err := f.token(ctx, gHost, "lProject") 56 So(err, ShouldBeNil) 57 So(t.AccessToken, ShouldEqual, "tok-1") 58 So(t.TokenType, ShouldEqual, "Bearer") 59 }) 60 Convey("return errEmptyProjectToken when minted token is nil", func() { 61 f.mockMintProjectToken = func(context.Context, auth.ProjectTokenParams) (*auth.Token, error) { 62 return nil, nil 63 } 64 _, err := f.token(ctx, gHost, "lProject-1") 65 So(err, ShouldErrLike, errEmptyProjectToken) 66 }) 67 Convey("error", func() { 68 f.mockMintProjectToken = func(context.Context, auth.ProjectTokenParams) (*auth.Token, error) { 69 return nil, errors.New("flake") 70 } 71 _, err := f.token(ctx, gHost, "lProject") 72 So(err, ShouldErrLike, "flake") 73 }) 74 }) 75 76 Convey("factory.makeClient", func() { 77 // This test calls ListChanges RPC because it is the easiest to mock empty 78 // response for. 79 var requests []*http.Request 80 srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 81 requests = append(requests, r) 82 w.Write([]byte(")]}'\n[]")) // no changes. 83 })) 84 defer srv.Close() 85 f.baseTransport = srv.Client().Transport 86 87 u, err := url.Parse(srv.URL) 88 So(err, ShouldBeNil) 89 90 limitedCtx, limitedCancel := context.WithTimeout(ctx, time.Minute) 91 defer limitedCancel() 92 93 Convey("project-scoped account", func() { 94 tokenCnt := 0 95 f.mockMintProjectToken = func(ctx context.Context, _ auth.ProjectTokenParams) (*auth.Token, error) { 96 So(ctx.Err(), ShouldBeNil) // must not be expired. 97 tokenCnt++ 98 return &auth.Token{ 99 Token: fmt.Sprintf("tok-%d", tokenCnt), 100 Expiry: epoch.Add(2 * time.Minute), 101 }, nil 102 } 103 104 c, err := f.MakeClient(limitedCtx, u.Host, "lProject") 105 So(err, ShouldBeNil) 106 _, err = c.ListChanges(limitedCtx, &gerritpb.ListChangesRequest{}) 107 So(err, ShouldBeNil) 108 So(requests, ShouldHaveLength, 1) 109 So(requests[0].Header["Authorization"], ShouldResemble, []string{"Bearer tok-1"}) 110 111 // Ensure client can be used even if context of its creation expires. 112 limitedCancel() 113 So(limitedCtx.Err(), ShouldNotBeNil) 114 // force token refresh 115 tclock.Add(3 * time.Minute) 116 _, err = c.ListChanges(ctx, &gerritpb.ListChangesRequest{}) 117 So(err, ShouldBeNil) 118 So(requests, ShouldHaveLength, 2) 119 So(requests[1].Header["Authorization"], ShouldResemble, []string{"Bearer tok-2"}) 120 }) 121 }) 122 }) 123 }