go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/rpc/get_build_status_test.go (about) 1 // Copyright 2023 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 "go.chromium.org/luci/auth/identity" 22 "go.chromium.org/luci/gae/impl/memory" 23 "go.chromium.org/luci/gae/service/datastore" 24 "go.chromium.org/luci/server/auth" 25 "go.chromium.org/luci/server/auth/authtest" 26 27 "go.chromium.org/luci/buildbucket/appengine/model" 28 "go.chromium.org/luci/buildbucket/appengine/rpc/testutil" 29 "go.chromium.org/luci/buildbucket/bbperms" 30 pb "go.chromium.org/luci/buildbucket/proto" 31 32 . "github.com/smartystreets/goconvey/convey" 33 . "go.chromium.org/luci/common/testing/assertions" 34 ) 35 36 func TestValidateGetBuildStatusRequest(t *testing.T) { 37 t.Parallel() 38 Convey("ValidateGetBuildStatusRequest", t, func() { 39 Convey("nil", func() { 40 err := validateGetBuildStatusRequest(nil) 41 So(err, ShouldErrLike, "either id or (builder + build_number) is required") 42 }) 43 44 Convey("empty", func() { 45 req := &pb.GetBuildStatusRequest{} 46 err := validateGetBuildStatusRequest(req) 47 So(err, ShouldErrLike, "either id or (builder + build_number) is required") 48 }) 49 50 Convey("builder", func() { 51 req := &pb.GetBuildStatusRequest{ 52 Builder: &pb.BuilderID{}, 53 } 54 err := validateGetBuildStatusRequest(req) 55 So(err, ShouldErrLike, "either id or (builder + build_number) is required") 56 }) 57 58 Convey("build number", func() { 59 req := &pb.GetBuildStatusRequest{ 60 BuildNumber: 1, 61 } 62 err := validateGetBuildStatusRequest(req) 63 So(err, ShouldErrLike, "either id or (builder + build_number) is required") 64 }) 65 66 Convey("mutual exclusion", func() { 67 Convey("builder", func() { 68 req := &pb.GetBuildStatusRequest{ 69 Id: 1, 70 Builder: &pb.BuilderID{}, 71 } 72 err := validateGetBuildStatusRequest(req) 73 So(err, ShouldErrLike, "id is mutually exclusive with (builder + build_number)") 74 }) 75 76 Convey("build number", func() { 77 req := &pb.GetBuildStatusRequest{ 78 Id: 1, 79 BuildNumber: 1, 80 } 81 err := validateGetBuildStatusRequest(req) 82 So(err, ShouldErrLike, "id is mutually exclusive with (builder + build_number)") 83 }) 84 }) 85 86 Convey("builder ID", func() { 87 Convey("project", func() { 88 req := &pb.GetBuildStatusRequest{ 89 Builder: &pb.BuilderID{}, 90 BuildNumber: 1, 91 } 92 err := validateGetBuildStatusRequest(req) 93 So(err, ShouldErrLike, "project must match") 94 }) 95 96 Convey("bucket", func() { 97 Convey("empty", func() { 98 req := &pb.GetBuildStatusRequest{ 99 Builder: &pb.BuilderID{ 100 Project: "project", 101 }, 102 BuildNumber: 1, 103 } 104 err := validateGetBuildStatusRequest(req) 105 So(err, ShouldErrLike, "bucket is required") 106 }) 107 108 Convey("v1", func() { 109 req := &pb.GetBuildStatusRequest{ 110 Builder: &pb.BuilderID{ 111 Project: "project", 112 Bucket: "luci.project.bucket", 113 Builder: "builder", 114 }, 115 BuildNumber: 1, 116 } 117 err := validateGetBuildStatusRequest(req) 118 So(err, ShouldErrLike, "invalid use of v1 bucket in v2 API") 119 }) 120 }) 121 122 Convey("builder", func() { 123 req := &pb.GetBuildStatusRequest{ 124 Builder: &pb.BuilderID{ 125 Project: "project", 126 Bucket: "bucket", 127 }, 128 BuildNumber: 1, 129 } 130 err := validateGetBuildStatusRequest(req) 131 So(err, ShouldErrLike, "builder is required") 132 }) 133 }) 134 }) 135 } 136 137 func TestGetBuildStatus(t *testing.T) { 138 Convey("GetBuildStatus", t, func() { 139 srv := &Builds{} 140 ctx := memory.Use(context.Background()) 141 datastore.GetTestable(ctx).AutoIndex(true) 142 datastore.GetTestable(ctx).Consistent(true) 143 testutil.PutBucket(ctx, "project", "bucket", nil) 144 145 Convey("no access", func() { 146 ctx = auth.WithState(ctx, &authtest.FakeState{ 147 Identity: identity.Identity("user:unauthorized@example.com"), 148 }) 149 bID := 123 150 bs := &model.BuildStatus{ 151 Build: datastore.MakeKey(ctx, "Build", bID), 152 BuildAddress: "project/bucket/builder/123", 153 Status: pb.Status_SCHEDULED, 154 } 155 So(datastore.Put(ctx, bs), ShouldBeNil) 156 _, err := srv.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 157 Id: int64(bID), 158 }) 159 So(err, ShouldErrLike, `requested resource not found or "user:unauthorized@example.com" does not have permission to view it`) 160 }) 161 162 Convey("get build status", func() { 163 const userID = identity.Identity("user:user@example.com") 164 ctx = auth.WithState(ctx, &authtest.FakeState{ 165 Identity: userID, 166 FakeDB: authtest.NewFakeDB( 167 authtest.MockPermission(userID, "project:bucket", bbperms.BuildsGet), 168 ), 169 }) 170 171 Convey("builder not found for BuildStatus entity", func() { 172 bID := 123 173 bs := &model.BuildStatus{ 174 Build: datastore.MakeKey(ctx, "Build", bID), 175 Status: pb.Status_SCHEDULED, 176 } 177 So(datastore.Put(ctx, bs), ShouldBeNil) 178 _, err := srv.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 179 Id: int64(bID), 180 }) 181 So(err, ShouldErrLike, `failed to parse build_address of build 123`) 182 }) 183 184 Convey("id", func() { 185 bID := 123 186 bs := &model.BuildStatus{ 187 Build: datastore.MakeKey(ctx, "Build", bID), 188 BuildAddress: "project/bucket/builder/123", 189 Status: pb.Status_SCHEDULED, 190 } 191 So(datastore.Put(ctx, bs), ShouldBeNil) 192 b, err := srv.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 193 Id: int64(bID), 194 }) 195 So(err, ShouldBeNil) 196 So(b, ShouldResembleProto, &pb.Build{ 197 Id: int64(bID), 198 Status: pb.Status_SCHEDULED, 199 }) 200 }) 201 202 Convey("id by getBuild", func() { 203 bID := 123 204 bld := &model.Build{ 205 ID: 123, 206 Proto: &pb.Build{ 207 Id: 123, 208 Builder: &pb.BuilderID{ 209 Project: "project", 210 Bucket: "bucket", 211 Builder: "builder", 212 }, 213 Status: pb.Status_STARTED, 214 }, 215 } 216 So(datastore.Put(ctx, bld), ShouldBeNil) 217 b, err := srv.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 218 Id: int64(bID), 219 }) 220 So(err, ShouldBeNil) 221 So(b, ShouldResembleProto, &pb.Build{ 222 Id: int64(bID), 223 Status: pb.Status_STARTED, 224 }) 225 }) 226 227 Convey("builder + number", func() { 228 bs := &model.BuildStatus{ 229 Build: datastore.MakeKey(ctx, "Build", 333), 230 BuildAddress: "project/bucket/builder/3", 231 Status: pb.Status_SCHEDULED, 232 } 233 So(datastore.Put(ctx, bs), ShouldBeNil) 234 b, err := srv.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 235 Builder: &pb.BuilderID{ 236 Project: "project", 237 Bucket: "bucket", 238 Builder: "builder", 239 }, 240 BuildNumber: 3, 241 }) 242 So(err, ShouldBeNil) 243 So(b, ShouldResembleProto, &pb.Build{ 244 Builder: &pb.BuilderID{ 245 Project: "project", 246 Bucket: "bucket", 247 Builder: "builder", 248 }, 249 Number: 3, 250 Status: pb.Status_SCHEDULED, 251 }) 252 }) 253 254 Convey("builder + number by getBuild", func() { 255 So(datastore.Put(ctx, &model.Build{ 256 ID: 333, 257 Proto: &pb.Build{ 258 Id: 333, 259 Builder: &pb.BuilderID{ 260 Project: "project", 261 Bucket: "bucket", 262 Builder: "builder", 263 }, 264 Number: 3, 265 Status: pb.Status_STARTED, 266 }, 267 BucketID: "project/bucket", 268 Tags: []string{"build_address:luci.project.bucket/builder/1"}, 269 }), ShouldBeNil) 270 So(datastore.Put(ctx, &model.TagIndex{ 271 ID: ":2:build_address:luci.project.bucket/builder/3", 272 Entries: []model.TagIndexEntry{ 273 { 274 BuildID: 333, 275 BucketID: "project/bucket", 276 }, 277 }, 278 }), ShouldBeNil) 279 b, err := srv.GetBuildStatus(ctx, &pb.GetBuildStatusRequest{ 280 Builder: &pb.BuilderID{ 281 Project: "project", 282 Bucket: "bucket", 283 Builder: "builder", 284 }, 285 BuildNumber: 3, 286 }) 287 So(err, ShouldBeNil) 288 So(b, ShouldResembleProto, &pb.Build{ 289 Builder: &pb.BuilderID{ 290 Project: "project", 291 Bucket: "bucket", 292 Builder: "builder", 293 }, 294 Number: 3, 295 Status: pb.Status_STARTED, 296 }) 297 }) 298 }) 299 }) 300 }