go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/internal/rpcquota/rpcquota_test.go (about) 1 // Copyright 2022 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 rpcquota 16 17 import ( 18 "context" 19 "testing" 20 21 "github.com/alicebob/miniredis/v2" 22 "github.com/gomodule/redigo/redis" 23 "google.golang.org/grpc/codes" 24 25 "go.chromium.org/luci/resultdb/internal/testutil" 26 "go.chromium.org/luci/server/auth" 27 "go.chromium.org/luci/server/auth/authtest" 28 quota "go.chromium.org/luci/server/quotabeta" 29 quotapb "go.chromium.org/luci/server/quotabeta/proto" 30 "go.chromium.org/luci/server/quotabeta/quotaconfig" 31 "go.chromium.org/luci/server/redisconn" 32 33 . "github.com/smartystreets/goconvey/convey" 34 . "go.chromium.org/luci/common/testing/assertions" 35 ) 36 37 // testQuotaContext returns a context with the given policies. 38 func testQuotaContext(ctx context.Context, t *testing.T, policies []*quotapb.Policy) context.Context { 39 // Create a miniredis instance for the luci-quota library to use. 40 // Arguably it would be better to stub out the quota implementation 41 // entirely, but that's not cleanly supported by luci-quota. 42 s, err := miniredis.Run() 43 So(err, ShouldBeNil) 44 t.Cleanup(s.Close) 45 ctx = redisconn.UsePool(ctx, &redis.Pool{ 46 Dial: func() (redis.Conn, error) { 47 return redis.Dial("tcp", s.Addr()) 48 }, 49 }) 50 // Use a simple quota config that grants a generous 1k RPCs (per 10 51 // minutes) to every 'user', which should be plenty for unit tests. 52 quotacfg, err := quotaconfig.NewMemory(ctx, policies) 53 So(err, ShouldBeNil) 54 return quota.Use(ctx, quotacfg) 55 } 56 57 func TestUpdateUserQuota(t *testing.T) { 58 Convey(`SpecificUser`, t, func() { 59 ctx := testQuotaContext(testutil.TestingContext(), t, []*quotapb.Policy{ 60 { 61 Name: "someresource/user/aaa/example/com", 62 Resources: 1000, 63 Replenishment: 20, 64 }, 65 }) 66 ctx = auth.WithState(ctx, &authtest.FakeState{Identity: "user:aaa@example.com"}) 67 Convey(`Enough quota`, func() { 68 err := UpdateUserQuota(ctx, "someresource/", 1, "svc", "meth") 69 So(err, ShouldBeNil) 70 }) 71 Convey(`Insufficient quota`, func() { 72 err := UpdateUserQuota(ctx, "someresource/", 9999, "svc", "meth") 73 So(err, ShouldHaveAppStatus, codes.ResourceExhausted) 74 }) 75 Convey(`No matching policy`, func() { 76 ctx := auth.WithState(ctx, &authtest.FakeState{Identity: "user:bbb@example.com"}) 77 err := UpdateUserQuota(ctx, "someresource/", 9999, "svc", "meth") 78 So(err, ShouldHaveAppStatus, codes.ResourceExhausted) 79 }) 80 Convey(`Track only`, func() { 81 ctx := context.WithValue(ctx, "aTrackOnlyKey, true) 82 err := UpdateUserQuota(ctx, "someresource/", 9999, "svc", "meth") 83 So(err, ShouldBeNil) 84 }) 85 }) 86 Convey(`WildcardFallback`, t, func() { 87 ctx := testQuotaContext(testutil.TestingContext(), t, []*quotapb.Policy{ 88 { 89 Name: "someresource/${user}", 90 Resources: 1000, 91 Replenishment: 20, 92 }, 93 { 94 Name: "someresource/user/aaa/example/com", 95 Resources: 1000, 96 Replenishment: 20, 97 }, 98 }) 99 ctx = auth.WithState(ctx, &authtest.FakeState{Identity: "user:bbb@example.com"}) 100 Convey(`Enough quota`, func() { 101 err := UpdateUserQuota(ctx, "someresource/", 1, "svc", "meth") 102 So(err, ShouldBeNil) 103 }) 104 Convey(`Insufficient quota`, func() { 105 err := UpdateUserQuota(ctx, "someresource/", 9999, "svc", "meth") 106 So(err, ShouldHaveAppStatus, codes.ResourceExhausted) 107 }) 108 }) 109 110 }