go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/auth_service/impl/servers/authdb/server_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 authdb 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "net/http" 22 "net/http/httptest" 23 "net/url" 24 "strconv" 25 "testing" 26 "time" 27 28 "github.com/julienschmidt/httprouter" 29 "google.golang.org/grpc/codes" 30 "google.golang.org/protobuf/types/known/timestamppb" 31 32 "go.chromium.org/luci/gae/impl/memory" 33 "go.chromium.org/luci/gae/service/datastore" 34 "go.chromium.org/luci/server/router" 35 36 "go.chromium.org/luci/auth_service/api/rpcpb" 37 "go.chromium.org/luci/auth_service/impl/model" 38 39 . "github.com/smartystreets/goconvey/convey" 40 . "go.chromium.org/luci/common/testing/assertions" 41 ) 42 43 func TestAuthDBServing(t *testing.T) { 44 testTS := time.Date(2021, time.August, 16, 15, 20, 0, 0, time.UTC) 45 testTSMicro := testTS.UnixNano() / 1000 46 testHash := "SHA256-hash" 47 testDeflated := []byte("deflated-groups") 48 49 testAuthDBSnapshotLatest := func(rid int64) *model.AuthDBSnapshotLatest { 50 return &model.AuthDBSnapshotLatest{ 51 Kind: "AuthDBSnapshotLatest", 52 ID: "latest", 53 AuthDBRev: rid, 54 AuthDBSha256: testHash, 55 ModifiedTS: testTS, 56 } 57 } 58 59 testAuthDBSnapshot := func(rid int64) *model.AuthDBSnapshot { 60 return &model.AuthDBSnapshot{ 61 Kind: "AuthDBSnapshot", 62 ID: rid, 63 AuthDBDeflated: testDeflated, 64 AuthDBSha256: testHash, 65 CreatedTS: testTS, 66 } 67 } 68 69 legacyCall := func(server Server, ctx context.Context, rid int64, skipBody bool) []byte { 70 rw := httptest.NewRecorder() 71 var revIDStr string 72 var sb string 73 if rid == 0 { 74 revIDStr = "latest" 75 } else { 76 revIDStr = strconv.FormatInt(rid, 10) 77 } 78 79 if skipBody { 80 sb = "1" 81 } else { 82 sb = "0" 83 } 84 rctx := &router.Context{ 85 Request: (&http.Request{ 86 URL: &url.URL{ 87 RawQuery: fmt.Sprintf("skip_body=%s", sb), 88 }, 89 }).WithContext(ctx), 90 Params: []httprouter.Param{ 91 {Key: "revID", Value: revIDStr}, 92 }, 93 Writer: rw, 94 } 95 err := server.HandleLegacyAuthDBServing(rctx) 96 So(err, ShouldBeNil) 97 return rw.Body.Bytes() 98 } 99 100 t.Parallel() 101 ctx := memory.Use(context.Background()) 102 103 Convey("Testing pRPC API", t, func() { 104 server := Server{} 105 So(datastore.Put(ctx, 106 testAuthDBSnapshot(1), 107 testAuthDBSnapshot(2), 108 testAuthDBSnapshot(3), 109 testAuthDBSnapshot(4)), ShouldBeNil) 110 111 Convey("Testing Error Codes", func() { 112 requestNegative := &rpcpb.GetSnapshotRequest{ 113 Revision: -1, 114 } 115 _, err := server.GetSnapshot(ctx, requestNegative) 116 So(err, ShouldHaveGRPCStatus, codes.InvalidArgument) 117 118 requestNotPresent := &rpcpb.GetSnapshotRequest{ 119 Revision: 42, 120 } 121 _, err = server.GetSnapshot(ctx, requestNotPresent) 122 So(err, ShouldHaveGRPCStatus, codes.NotFound) 123 124 requestNotPresent.Revision, err = getLatestRevision(ctx, requestNotPresent) 125 So(err, ShouldHaveGRPCStatus, codes.NotFound) 126 }) 127 128 Convey("Testing valid revision ID", func() { 129 request := &rpcpb.GetSnapshotRequest{ 130 Revision: 2, 131 } 132 snapshot, err := server.GetSnapshot(ctx, request) 133 So(err, ShouldBeNil) 134 So(snapshot, ShouldResembleProto, &rpcpb.Snapshot{ 135 AuthDbRev: 2, 136 AuthDbSha256: testHash, 137 AuthDbDeflated: testDeflated, 138 CreatedTs: timestamppb.New(testTS), 139 }) 140 }) 141 142 Convey("Testing GetSnapshotLatest", func() { 143 So(datastore.Put(ctx, testAuthDBSnapshotLatest(4)), ShouldBeNil) 144 request := &rpcpb.GetSnapshotRequest{ 145 Revision: 0, 146 } 147 latestSnapshot, err := server.GetSnapshot(ctx, request) 148 So(err, ShouldBeNil) 149 So(latestSnapshot, ShouldResembleProto, &rpcpb.Snapshot{ 150 AuthDbRev: 4, 151 AuthDbSha256: testHash, 152 AuthDbDeflated: testDeflated, 153 CreatedTs: timestamppb.New(testTS), 154 }) 155 }) 156 157 Convey("Testing skipbody", func() { 158 requestSnapshotSkip := &rpcpb.GetSnapshotRequest{ 159 Revision: 3, 160 SkipBody: true, 161 } 162 snapshot, err := server.GetSnapshot(ctx, requestSnapshotSkip) 163 So(err, ShouldBeNil) 164 So(snapshot, ShouldResembleProto, &rpcpb.Snapshot{ 165 AuthDbRev: 3, 166 AuthDbSha256: testHash, 167 CreatedTs: timestamppb.New(testTS), 168 }) 169 170 So(datastore.Put(ctx, testAuthDBSnapshotLatest(4)), ShouldBeNil) 171 requestLatestSkip := &rpcpb.GetSnapshotRequest{ 172 Revision: 0, 173 SkipBody: true, 174 } 175 latest, err := server.GetSnapshot(ctx, requestLatestSkip) 176 So(err, ShouldBeNil) 177 So(latest, ShouldResembleProto, &rpcpb.Snapshot{ 178 AuthDbRev: 4, 179 AuthDbSha256: testHash, 180 CreatedTs: timestamppb.New(testTS), 181 }) 182 }) 183 184 Convey("Testing GetSnapshot with sharded DB in datastore.", func() { 185 shardedSnapshot := &model.AuthDBSnapshot{ 186 Kind: "AuthDBSnapshot", 187 ID: 42, 188 ShardIDs: []string{ 189 "42:shard-1", 190 "42:shard-2", 191 }, 192 AuthDBSha256: testHash, 193 CreatedTS: testTS, 194 } 195 196 shard1 := &model.AuthDBShard{ 197 Kind: "AuthDBShard", 198 ID: "42:shard-1", 199 Blob: []byte("shard-1-groups"), 200 } 201 shard2 := &model.AuthDBShard{ 202 Kind: "AuthDBShard", 203 ID: "42:shard-2", 204 Blob: []byte("shard-2-groups"), 205 } 206 So(datastore.Put(ctx, shard1, shard2, shardedSnapshot), ShouldBeNil) 207 requestSnapshot := &rpcpb.GetSnapshotRequest{ 208 Revision: 42, 209 } 210 snapshot, err := server.GetSnapshot(ctx, requestSnapshot) 211 So(err, ShouldBeNil) 212 So(snapshot, ShouldResembleProto, &rpcpb.Snapshot{ 213 AuthDbRev: 42, 214 AuthDbSha256: testHash, 215 AuthDbDeflated: []byte("shard-1-groupsshard-2-groups"), 216 CreatedTs: timestamppb.New(testTS), 217 }) 218 }) 219 }) 220 221 Convey("Testing legacy API Server with JSON response", t, func() { 222 server := Server{} 223 type TestSnapshotJSON struct { 224 AuthDBRev int64 `json:"auth_db_rev"` 225 AuthDBDeflated []byte `json:"deflated_body,omitempty"` 226 AuthDBSha256 string `json:"sha256"` 227 CreatedTS int64 `json:"created_ts"` 228 } 229 expectedJSON := func(rid int64, sb bool) ([]byte, error) { 230 if sb { 231 testDeflated = []byte{} 232 } 233 return json.Marshal(map[string]any{ 234 "snapshot": TestSnapshotJSON{ 235 AuthDBRev: rid, 236 AuthDBDeflated: testDeflated, 237 AuthDBSha256: testHash, 238 CreatedTS: testTSMicro, 239 }, 240 }) 241 } 242 243 So(datastore.Put(ctx, 244 testAuthDBSnapshot(1), 245 testAuthDBSnapshot(2), 246 testAuthDBSnapshot(3), 247 testAuthDBSnapshot(4)), ShouldBeNil) 248 249 Convey("Testing GetSnapshotLegacy skipBody=true", func() { 250 rid := int64(3) 251 skipBody := true 252 actualBlob := legacyCall(server, ctx, rid, skipBody) 253 expectedBlob, err := expectedJSON(rid, skipBody) 254 So(err, ShouldBeNil) 255 So(actualBlob, ShouldResemble, expectedBlob) 256 }) 257 258 Convey("Testing GetSnapshotLegacy skipBody=false", func() { 259 rid := int64(3) 260 skipBody := false 261 actualBlob := legacyCall(server, ctx, rid, skipBody) 262 expectedBlob, err := expectedJSON(rid, skipBody) 263 So(err, ShouldBeNil) 264 So(actualBlob, ShouldResemble, expectedBlob) 265 }) 266 267 Convey("Testing GetSnapshotLatestLegacy skipBody=true", func() { 268 rid := int64(4) 269 skipBody := true 270 actualBlob := legacyCall(server, ctx, rid, skipBody) 271 expectedBlob, err := expectedJSON(rid, skipBody) 272 So(err, ShouldBeNil) 273 So(actualBlob, ShouldResemble, expectedBlob) 274 275 }) 276 277 Convey("Testing GetSnapshotLatestLegacy skipBody=false", func() { 278 rid := int64(4) 279 skipBody := false 280 actualBlob := legacyCall(server, ctx, rid, skipBody) 281 expectedBlob, err := expectedJSON(rid, skipBody) 282 So(err, ShouldBeNil) 283 So(actualBlob, ShouldResemble, expectedBlob) 284 }) 285 }) 286 }