go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/appengine/gaeauth/server/internal/authdbimpl/authdb_test.go (about) 1 // Copyright 2015 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 authdbimpl 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 "time" 22 23 "go.chromium.org/luci/appengine/gaetesting" 24 "go.chromium.org/luci/common/retry/transient" 25 "go.chromium.org/luci/gae/service/datastore" 26 "go.chromium.org/luci/server/auth/service" 27 "go.chromium.org/luci/server/auth/service/protocol" 28 29 . "github.com/smartystreets/goconvey/convey" 30 . "go.chromium.org/luci/common/testing/assertions" 31 ) 32 33 func TestConfigureAuthService(t *testing.T) { 34 t.Parallel() 35 36 Convey("Initial config", t, func() { 37 srv := &fakeAuthService{LatestRev: 123} 38 ctx := setAuthService(gaetesting.TestingContext(), srv) 39 40 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service"), ShouldBeNil) 41 So(srv.Calls, ShouldResemble, []string{ 42 `EnsureSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service" ""`, 43 }) 44 45 info, err := GetLatestSnapshotInfo(ctx) 46 So(err, ShouldBeNil) 47 So(info, ShouldResemble, &SnapshotInfo{ 48 AuthServiceURL: "http://auth-service", 49 Rev: 123, 50 }) 51 52 // Coverage for GetAuthDBSnapshot. 53 _, err = GetAuthDBSnapshot(ctx, "missing") 54 So(err, ShouldEqual, datastore.ErrNoSuchEntity) 55 snap, err := GetAuthDBSnapshot(ctx, info.GetSnapshotID()) 56 So(err, ShouldBeNil) 57 So(snap, ShouldResembleProto, &protocol.AuthDB{ 58 OauthClientId: "client-id-for-rev-123", 59 OauthClientSecret: "secret", 60 }) 61 62 // Same config call again triggers resubsciption. 63 srv.Calls = nil 64 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service"), ShouldBeNil) 65 So(srv.Calls, ShouldResemble, []string{ 66 `EnsureSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service" ""`, 67 }) 68 }) 69 70 Convey("Switching cfg", t, func() { 71 srv := &fakeAuthService{LatestRev: 123} 72 ctx := setAuthService(gaetesting.TestingContext(), srv) 73 74 // Initial config. 75 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service-1"), ShouldBeNil) 76 // Change URL of the service. 77 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service-2"), ShouldBeNil) 78 79 info, err := GetLatestSnapshotInfo(ctx) 80 So(err, ShouldBeNil) 81 So(info, ShouldResemble, &SnapshotInfo{ 82 AuthServiceURL: "http://auth-service-2", 83 Rev: 123, 84 }) 85 86 So(srv.Calls, ShouldResemble, []string{ 87 `EnsureSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service-1" ""`, 88 `EnsureSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service-2" ""`, 89 `DeleteSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service-1"`, 90 }) 91 }) 92 93 Convey("Removing cfg", t, func() { 94 srv := &fakeAuthService{LatestRev: 123} 95 ctx := setAuthService(gaetesting.TestingContext(), srv) 96 97 // Initial config. 98 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service-1"), ShouldBeNil) 99 // Remove. 100 So(ConfigureAuthService(ctx, "http://base_url", ""), ShouldBeNil) 101 102 info, err := GetLatestSnapshotInfo(ctx) 103 So(err, ShouldBeNil) 104 So(info, ShouldBeNil) 105 106 So(srv.Calls, ShouldResemble, []string{ 107 `EnsureSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service-1" ""`, 108 `DeleteSubscription "projects/app/subscriptions/dev-app-server-v1+auth-service-1"`, 109 }) 110 }) 111 } 112 113 func TestSyncAuthDB(t *testing.T) { 114 t.Parallel() 115 116 Convey("No new changes", t, func() { 117 srv := &fakeAuthService{LatestRev: 123} 118 ctx := setAuthService(gaetesting.TestingContext(), srv) 119 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service"), ShouldBeNil) 120 121 info, err := syncAuthDB(ctx) 122 So(err, ShouldBeNil) 123 So(info, ShouldResemble, &SnapshotInfo{ 124 AuthServiceURL: "http://auth-service", 125 Rev: 123, 126 }) 127 }) 128 129 Convey("Have update", t, func() { 130 srv := &fakeAuthService{LatestRev: 123} 131 ctx := setAuthService(gaetesting.TestingContext(), srv) 132 So(ConfigureAuthService(ctx, "http://base_url", "http://auth-service"), ShouldBeNil) 133 134 srv.LatestRev = 456 135 136 info, err := syncAuthDB(ctx) 137 So(err, ShouldBeNil) 138 So(info, ShouldResemble, &SnapshotInfo{ 139 AuthServiceURL: "http://auth-service", 140 Rev: 456, 141 }) 142 }) 143 } 144 145 func TestSharding(t *testing.T) { 146 t.Parallel() 147 148 Convey("With datastore", t, func() { 149 ctx := gaetesting.TestingContext() 150 151 Convey("Shard+unshard", func() { 152 shardIDs, err := shardAuthDB(ctx, "some-id", []byte("0123456789"), 3) 153 So(err, ShouldBeNil) 154 So(shardIDs, ShouldResemble, []string{ 155 "some-id:bf6aaaab7c143ca12ae448c69fb72bb4cf1b29154b9086a927a0a91ae334cdf7", 156 "some-id:da70dfa4d9f95ac979f921e8e623358236313f334afcd06cddf8a5621cf6a1e9", 157 "some-id:cebe3d9d614ba5c19f633566104315854a11353a333bf96f16b5afa0e90abdc4", 158 "some-id:19581e27de7ced00ff1ce50b2047e7a567c76b1cbaebabe5ef03f7c3017bb5b7", 159 }) 160 161 Convey("OK", func() { 162 blob, err := unshardAuthDB(ctx, shardIDs) 163 So(err, ShouldBeNil) 164 So(string(blob), ShouldEqual, "0123456789") 165 }) 166 167 Convey("Missing one", func() { 168 _, err := unshardAuthDB(ctx, append(shardIDs, "missing")) 169 So(err, ShouldNotBeNil) 170 So(transient.Tag.In(err), ShouldBeFalse) 171 }) 172 }) 173 174 Convey("Store+load unsharded", func() { 175 So(storeDeflated(ctx, "some-id", []byte("0123456789"), time.Now(), 1000), ShouldBeNil) 176 177 Convey("OK", func() { 178 blob, _, err := fetchDeflated(ctx, "some-id") 179 So(err, ShouldBeNil) 180 So(string(blob), ShouldEqual, "0123456789") 181 }) 182 183 Convey("Missing snapshot", func() { 184 _, code, err := fetchDeflated(ctx, "another-id") 185 So(err, ShouldNotBeNil) 186 So(transient.Tag.In(err), ShouldBeFalse) 187 So(code, ShouldEqual, "ERROR_NO_SNAPSHOT") 188 }) 189 }) 190 191 Convey("Store+load sharded", func() { 192 So(storeDeflated(ctx, "some-id", []byte("0123456789"), time.Now(), 3), ShouldBeNil) 193 194 Convey("OK", func() { 195 blob, _, err := fetchDeflated(ctx, "some-id") 196 So(err, ShouldBeNil) 197 So(string(blob), ShouldEqual, "0123456789") 198 }) 199 200 Convey("Missing snapshot", func() { 201 _, code, err := fetchDeflated(ctx, "another-id") 202 So(err, ShouldNotBeNil) 203 So(transient.Tag.In(err), ShouldBeFalse) 204 So(code, ShouldEqual, "ERROR_NO_SNAPSHOT") 205 }) 206 207 Convey("Missing shard", func() { 208 // See the test above for the ID. 209 datastore.Delete(ctx, datastore.KeyForObj(ctx, &SnapshotShard{ 210 ID: "some-id:bf6aaaab7c143ca12ae448c69fb72bb4cf1b29154b9086a927a0a91ae334cdf7", 211 })) 212 _, code, err := fetchDeflated(ctx, "some-id") 213 So(err, ShouldNotBeNil) 214 So(transient.Tag.In(err), ShouldBeTrue) 215 So(code, ShouldEqual, "ERROR_SHARDS_MISSING") 216 }) 217 }) 218 }) 219 } 220 221 /// 222 223 type fakeAuthService struct { 224 LatestRev int64 225 Calls []string 226 Notification *service.Notification 227 } 228 229 func (f *fakeAuthService) EnsureSubscription(ctx context.Context, subscription, pushURL string) error { 230 f.Calls = append(f.Calls, fmt.Sprintf("EnsureSubscription %q %q", subscription, pushURL)) 231 return nil 232 } 233 234 func (f *fakeAuthService) DeleteSubscription(ctx context.Context, subscription string) error { 235 f.Calls = append(f.Calls, fmt.Sprintf("DeleteSubscription %q", subscription)) 236 return nil 237 } 238 239 func (f *fakeAuthService) PullPubSub(ctx context.Context, subscription string) (*service.Notification, error) { 240 f.Calls = append(f.Calls, fmt.Sprintf("PullPubSub %q", subscription)) 241 return f.Notification, nil 242 } 243 244 func (f *fakeAuthService) ProcessPubSubPush(ctx context.Context, body []byte) (*service.Notification, error) { 245 f.Calls = append(f.Calls, "ProcessPubSubPush") 246 return f.Notification, nil 247 } 248 249 func (f *fakeAuthService) GetLatestSnapshotRevision(ctx context.Context) (int64, error) { 250 return f.LatestRev, nil 251 } 252 253 func (f *fakeAuthService) GetSnapshot(ctx context.Context, rev int64) (*service.Snapshot, error) { 254 if rev != f.LatestRev { 255 return nil, fmt.Errorf("fakeAuthService: no snapshot for rev %d", rev) 256 } 257 return &service.Snapshot{ 258 AuthDB: &protocol.AuthDB{ 259 OauthClientId: fmt.Sprintf("client-id-for-rev-%d", f.LatestRev), 260 OauthClientSecret: "secret", 261 }, 262 Rev: f.LatestRev, 263 }, nil 264 }