go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/quota/integration_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 quota_test 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "github.com/alicebob/miniredis/v2" 23 "github.com/gomodule/redigo/redis" 24 "google.golang.org/protobuf/types/known/durationpb" 25 "google.golang.org/protobuf/types/known/timestamppb" 26 27 "go.chromium.org/luci/common/clock" 28 "go.chromium.org/luci/common/clock/testclock" 29 30 "go.chromium.org/luci/server/auth" 31 "go.chromium.org/luci/server/quota" 32 "go.chromium.org/luci/server/quota/internal/quotakeys" 33 "go.chromium.org/luci/server/quota/quotapb" 34 "go.chromium.org/luci/server/redisconn" 35 36 _ "go.chromium.org/luci/server/quota/quotatestmonkeypatch" 37 38 . "github.com/smartystreets/goconvey/convey" 39 . "go.chromium.org/luci/common/testing/assertions" 40 ) 41 42 var integrationTestApp = quota.Register("ita", "a.ApplicationOptions{ 43 ResourceTypes: []string{ 44 "qps", 45 "thingBytes", 46 }, 47 }) 48 49 func TestFullFlow(t *testing.T) { 50 Convey(`FullFlow`, t, func() { 51 ctx := context.Background() 52 53 s, err := miniredis.Run() 54 So(err, ShouldBeNil) 55 defer s.Close() 56 57 tc := testclock.New(testclock.TestRecentTimeUTC.Round(time.Microsecond)) 58 ctx = clock.Set(ctx, tc) 59 60 s.SetTime(clock.Now(ctx)) 61 62 ctx = redisconn.UsePool(ctx, &redis.Pool{ 63 Dial: func() (redis.Conn, error) { 64 return redis.Dial("tcp", s.Addr()) 65 }, 66 }) 67 68 policy := "apb.PolicyConfig{ 69 Policies: []*quotapb.PolicyConfig_Entry{ 70 { 71 Key: "apb.PolicyKey{Namespace: "ns1", Name: "cool people", ResourceType: "qps"}, 72 Policy: "apb.Policy{ 73 Default: 123, 74 Limit: 5156, 75 Refill: "apb.Policy_Refill{ 76 Units: 1, 77 Interval: 3, 78 }, 79 }, 80 }, 81 }, 82 } 83 Convey(`policy`, func() { 84 Convey(`valid`, func() { 85 policyConfigID, err := integrationTestApp.LoadPoliciesAuto(ctx, "@internal:integrationTestApp", policy) 86 So(err, ShouldBeNil) 87 So(policyConfigID, ShouldResembleProto, "apb.PolicyConfigID{ 88 AppId: "ita", 89 Realm: "@internal:integrationTestApp", 90 VersionScheme: 1, 91 Version: "W`-@.nn^o,_kP)j_BSM_.:jhMfP]d`mj]J/kKTpa", 92 }) 93 94 cfgKey := quotakeys.PolicyConfigID(policyConfigID) 95 So(s.Keys(), ShouldResemble, []string{cfgKey}) 96 hkeys, err := s.HKeys(cfgKey) 97 So(err, ShouldBeNil) 98 polKey := "apb.PolicyKey{ 99 Namespace: "ns1", 100 Name: "cool people", 101 ResourceType: "qps", 102 } 103 polKeyStr := quotakeys.PolicyKey(polKey) 104 So(hkeys, ShouldResemble, []string{polKeyStr, "~loaded_time"}) 105 So(s.HGet(cfgKey, polKeyStr), ShouldEqual, string([]byte{ 106 147, 123, 205, 20, 36, 146, 1, 3, 107 })) 108 109 Convey(`account`, func() { 110 requestId := "somereq" 111 rsp, err := quota.ApplyOps(ctx, requestId, durationpb.New(time.Hour), []*quotapb.Op{ 112 { 113 AccountId: integrationTestApp.AccountID("project:realm", "cvgroup1", "username", "qps"), 114 PolicyId: "apb.PolicyID{Config: policyConfigID, Key: polKey}, 115 RelativeTo: quotapb.Op_CURRENT_BALANCE, Delta: 5, 116 }, 117 }) 118 So(err, ShouldBeNil) 119 So(rsp, ShouldResembleProto, "apb.ApplyOpsResponse{ 120 Results: []*quotapb.OpResult{ 121 {NewBalance: 128, AccountStatus: quotapb.OpResult_CREATED}, 122 }, 123 OriginallySet: timestamppb.New(clock.Now(ctx)), 124 }) 125 So(s.TTL(quotakeys.RequestDedupKey("apb.RequestDedupKey{ 126 Ident: string(auth.CurrentIdentity(ctx)), 127 RequestId: requestId, 128 })), ShouldEqual, time.Hour) 129 }) 130 }) 131 132 Convey(`bad resource`, func() { 133 policy.Policies[0].Key.ResourceType = "fakey fake" 134 _, err := integrationTestApp.LoadPoliciesAuto(ctx, "@internal:integrationTestApp", policy) 135 So(err, ShouldErrLike, "unknown resource type") 136 }) 137 }) 138 139 Convey(`GetAccounts`, func() { 140 existingAccountID := "apb.AccountID{ 141 AppId: "foo", 142 Realm: "bar", 143 Namespace: "baz", 144 Name: "qux", 145 ResourceType: "quux", 146 } 147 148 nonExistingAccountID := "apb.AccountID{ 149 AppId: "foo1", 150 Realm: "bar1", 151 Namespace: "baz1", 152 Name: "qux1", 153 ResourceType: "quux1", 154 } 155 156 // Add test value to retrieve. 157 _, err := quota.ApplyOps(ctx, "", nil, []*quotapb.Op{ 158 { 159 AccountId: existingAccountID, 160 RelativeTo: quotapb.Op_ZERO, 161 Delta: 1, 162 Options: uint32(quotapb.Op_IGNORE_POLICY_BOUNDS), 163 }, 164 }) 165 So(err, ShouldBeNil) 166 167 res, err := quota.GetAccounts(ctx, []*quotapb.AccountID{existingAccountID, nonExistingAccountID}) 168 So(err, ShouldBeNil) 169 So(res, ShouldResembleProto, "apb.GetAccountsResponse{ 170 Accounts: []*quotapb.GetAccountsResponse_AccountState{ 171 { 172 Id: existingAccountID, 173 Account: "apb.Account{ 174 Balance: 1, 175 UpdatedTs: timestamppb.New(clock.Now(ctx)), 176 }, 177 }, 178 { 179 Id: nonExistingAccountID, 180 }, 181 }, 182 }) 183 }) 184 185 }) 186 }