go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/rpc/get_builder_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 rpc 16 17 import ( 18 "context" 19 "testing" 20 21 "google.golang.org/grpc/codes" 22 23 "go.chromium.org/luci/auth/identity" 24 "go.chromium.org/luci/gae/impl/memory" 25 "go.chromium.org/luci/gae/service/datastore" 26 "go.chromium.org/luci/server/auth" 27 "go.chromium.org/luci/server/auth/authtest" 28 29 "go.chromium.org/luci/buildbucket/appengine/model" 30 "go.chromium.org/luci/buildbucket/bbperms" 31 pb "go.chromium.org/luci/buildbucket/proto" 32 33 . "github.com/smartystreets/goconvey/convey" 34 . "go.chromium.org/luci/common/testing/assertions" 35 ) 36 37 func TestGetBuilder(t *testing.T) { 38 t.Parallel() 39 40 const userID = identity.Identity("user:user@example.com") 41 42 Convey("GetBuilder", t, func() { 43 srv := &Builders{} 44 ctx := memory.Use(context.Background()) 45 datastore.GetTestable(ctx).AutoIndex(true) 46 datastore.GetTestable(ctx).Consistent(true) 47 48 bid := &pb.BuilderID{ 49 Project: "project", 50 Bucket: "bucket", 51 Builder: "builder", 52 } 53 54 Convey(`Request validation`, func() { 55 Convey(`Invalid ID`, func() { 56 _, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{}) 57 So(err, ShouldHaveAppStatus, codes.InvalidArgument, "id: project must match") 58 }) 59 }) 60 61 Convey(`No permissions`, func() { 62 ctx = auth.WithState(ctx, &authtest.FakeState{ 63 Identity: userID, 64 }) 65 So(datastore.Put( 66 ctx, 67 &model.Bucket{ 68 Parent: model.ProjectKey(ctx, "project"), 69 ID: "bucket", 70 }, 71 &model.Builder{ 72 Parent: model.BucketKey(ctx, "project", "bucket"), 73 ID: "builder", 74 Config: &pb.BuilderConfig{Name: "builder"}, 75 }, 76 ), ShouldBeNil) 77 78 _, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: bid}) 79 So(err, ShouldHaveAppStatus, codes.NotFound, "not found") 80 }) 81 82 Convey(`End to end`, func() { 83 ctx = auth.WithState(ctx, &authtest.FakeState{ 84 Identity: userID, 85 FakeDB: authtest.NewFakeDB( 86 authtest.MockPermission(userID, "project:bucket", bbperms.BuildersGet), 87 ), 88 }) 89 So(datastore.Put( 90 ctx, 91 &model.Bucket{ 92 Parent: model.ProjectKey(ctx, "project"), 93 ID: "bucket", 94 Proto: &pb.Bucket{}, 95 }, 96 &model.Builder{ 97 Parent: model.BucketKey(ctx, "project", "bucket"), 98 ID: "builder", 99 Config: &pb.BuilderConfig{Name: "builder"}, 100 }, 101 ), ShouldBeNil) 102 103 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: bid}) 104 So(err, ShouldBeNil) 105 So(res, ShouldResembleProto, &pb.BuilderItem{ 106 Id: bid, 107 Config: &pb.BuilderConfig{Name: "builder"}, 108 }) 109 }) 110 111 Convey(`metadata`, func() { 112 ctx = auth.WithState(ctx, &authtest.FakeState{ 113 Identity: userID, 114 FakeDB: authtest.NewFakeDB( 115 authtest.MockPermission(userID, "project:bucket", bbperms.BuildersGet), 116 ), 117 }) 118 So(datastore.Put( 119 ctx, 120 &model.Bucket{ 121 Parent: model.ProjectKey(ctx, "project"), 122 ID: "bucket", 123 Proto: &pb.Bucket{}, 124 }, 125 &model.Builder{ 126 Parent: model.BucketKey(ctx, "project", "bucket"), 127 ID: "builder", 128 Config: &pb.BuilderConfig{Name: "builder"}, 129 Metadata: &pb.BuilderMetadata{ 130 Owner: "owner", 131 Health: &pb.HealthStatus{ 132 HealthScore: 9, 133 }, 134 }, 135 }, 136 &model.Builder{ 137 Parent: model.BucketKey(ctx, "project", "bucket"), 138 ID: "builder2", 139 Config: &pb.BuilderConfig{Name: "builder2"}, 140 }, 141 ), ShouldBeNil) 142 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{ 143 Id: bid, 144 Mask: &pb.BuilderMask{ 145 Type: pb.BuilderMask_ALL, 146 }, 147 }) 148 So(err, ShouldBeNil) 149 So(res, ShouldResembleProto, &pb.BuilderItem{ 150 Id: bid, 151 Config: &pb.BuilderConfig{Name: "builder"}, 152 Metadata: &pb.BuilderMetadata{ 153 Owner: "owner", 154 Health: &pb.HealthStatus{ 155 HealthScore: 9, 156 }, 157 }, 158 }) 159 bid.Builder = "builder2" 160 res, err = srv.GetBuilder(ctx, &pb.GetBuilderRequest{ 161 Id: bid, 162 Mask: &pb.BuilderMask{ 163 Type: pb.BuilderMask_ALL, 164 }, 165 }) 166 So(err, ShouldBeNil) 167 So(res, ShouldResembleProto, &pb.BuilderItem{ 168 Id: bid, 169 Config: &pb.BuilderConfig{Name: "builder2"}, 170 }) 171 }) 172 173 Convey(`mask`, func() { 174 ctx = auth.WithState(ctx, &authtest.FakeState{ 175 Identity: userID, 176 FakeDB: authtest.NewFakeDB( 177 authtest.MockPermission(userID, "project:bucket", bbperms.BuildersGet), 178 ), 179 }) 180 So(datastore.Put( 181 ctx, 182 &model.Bucket{ 183 Parent: model.ProjectKey(ctx, "project"), 184 ID: "bucket", 185 Proto: &pb.Bucket{}, 186 }, 187 &model.Builder{ 188 Parent: model.BucketKey(ctx, "project", "bucket"), 189 ID: "builder", 190 Config: &pb.BuilderConfig{Name: "builder"}, 191 Metadata: &pb.BuilderMetadata{ 192 Owner: "owner", 193 Health: &pb.HealthStatus{ 194 HealthScore: 9, 195 }, 196 }, 197 }, 198 ), ShouldBeNil) 199 200 Convey(`default mask`, func() { 201 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: bid}) 202 So(err, ShouldBeNil) 203 So(res, ShouldResembleProto, &pb.BuilderItem{ 204 Id: bid, 205 Config: &pb.BuilderConfig{Name: "builder"}, 206 }) 207 }) 208 209 Convey(`config only`, func() { 210 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{ 211 Id: bid, 212 Mask: &pb.BuilderMask{ 213 Type: pb.BuilderMask_CONFIG_ONLY, 214 }, 215 }) 216 So(err, ShouldBeNil) 217 So(res, ShouldResembleProto, &pb.BuilderItem{ 218 Id: bid, 219 Config: &pb.BuilderConfig{Name: "builder"}, 220 }) 221 }) 222 223 Convey(`metadata only`, func() { 224 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{ 225 Id: bid, 226 Mask: &pb.BuilderMask{ 227 Type: pb.BuilderMask_METADATA_ONLY, 228 }, 229 }) 230 So(err, ShouldBeNil) 231 So(res, ShouldResembleProto, &pb.BuilderItem{ 232 Id: bid, 233 Metadata: &pb.BuilderMetadata{ 234 Owner: "owner", 235 Health: &pb.HealthStatus{ 236 HealthScore: 9, 237 }, 238 }, 239 }) 240 }) 241 242 Convey(`all`, func() { 243 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{ 244 Id: bid, 245 Mask: &pb.BuilderMask{ 246 Type: pb.BuilderMask_ALL, 247 }, 248 }) 249 So(err, ShouldBeNil) 250 So(res, ShouldResembleProto, &pb.BuilderItem{ 251 Id: bid, 252 Config: &pb.BuilderConfig{Name: "builder"}, 253 Metadata: &pb.BuilderMetadata{ 254 Owner: "owner", 255 Health: &pb.HealthStatus{ 256 HealthScore: 9, 257 }, 258 }, 259 }) 260 }) 261 }) 262 263 Convey(`shadow`, func() { 264 ctx = auth.WithState(ctx, &authtest.FakeState{ 265 Identity: userID, 266 FakeDB: authtest.NewFakeDB( 267 authtest.MockPermission(userID, "project:bucket.shadow", bbperms.BuildersGet), 268 ), 269 }) 270 So(datastore.Put( 271 ctx, 272 &model.Bucket{ 273 Parent: model.ProjectKey(ctx, "project"), 274 ID: "bucket", 275 Proto: &pb.Bucket{ 276 Shadow: "bucket.shadow", 277 }, 278 }, 279 &model.Bucket{ 280 Parent: model.ProjectKey(ctx, "project"), 281 ID: "bucket.shadow", 282 Proto: &pb.Bucket{}, 283 Shadows: []string{"bucket"}, 284 }, 285 &model.Builder{ 286 Parent: model.BucketKey(ctx, "project", "bucket"), 287 ID: "builder", 288 Config: &pb.BuilderConfig{ 289 Name: "builder", 290 ServiceAccount: "example@example.com", 291 Dimensions: []string{"os:Linux", "pool:luci.chromium.try", "todelete:random"}, 292 Properties: `{"a":"b"}`, 293 ShadowBuilderAdjustments: &pb.BuilderConfig_ShadowBuilderAdjustments{ 294 ServiceAccount: "shadow@example.com", 295 Pool: "shadow.pool", 296 Properties: `{"a":"b2"}`, 297 Dimensions: []string{ 298 "pool:shadow.pool", 299 "todelete:", 300 "120:toadd:v", 301 }, 302 }, 303 }, 304 }, 305 ), ShouldBeNil) 306 307 shadowBID := &pb.BuilderID{ 308 Project: "project", 309 Bucket: "bucket.shadow", 310 Builder: "builder", 311 } 312 res, err := srv.GetBuilder(ctx, &pb.GetBuilderRequest{Id: shadowBID}) 313 So(err, ShouldBeNil) 314 So(res, ShouldResembleProto, &pb.BuilderItem{ 315 Id: shadowBID, 316 Config: &pb.BuilderConfig{ 317 Name: "builder", 318 ServiceAccount: "shadow@example.com", 319 Dimensions: []string{"120:toadd:v", "os:Linux", "pool:shadow.pool"}, 320 Properties: `{"a":"b2"}`, 321 }, 322 }) 323 }) 324 }) 325 }