go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/internal/services/recorder/mark_invocation_submitted_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 recorder 16 17 import ( 18 "testing" 19 20 . "github.com/smartystreets/goconvey/convey" 21 . "go.chromium.org/luci/common/testing/assertions" 22 "google.golang.org/protobuf/reflect/protoreflect" 23 24 "go.chromium.org/luci/resultdb/internal/invocations" 25 "go.chromium.org/luci/resultdb/internal/tasks/taskspb" 26 "go.chromium.org/luci/resultdb/internal/testutil" 27 "go.chromium.org/luci/resultdb/internal/testutil/insert" 28 "go.chromium.org/luci/server/auth" 29 "go.chromium.org/luci/server/auth/authtest" 30 "go.chromium.org/luci/server/span" 31 "go.chromium.org/luci/server/tq" 32 33 pb "go.chromium.org/luci/resultdb/proto/v1" 34 ) 35 36 func TestMarkInvocationSubmitted(t *testing.T) { 37 Convey(`E2E`, t, func() { 38 ctx := testutil.SpannerTestContext(t) 39 ctx, sched := tq.TestingContext(ctx, nil) 40 recorder := newTestRecorderServer() 41 42 Convey(`Empty Invocation`, func() { 43 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 44 Invocation: "", 45 }) 46 So(err, ShouldBeRPCInvalidArgument, "unspecified") 47 }) 48 49 Convey(`Invalid Invocation`, func() { 50 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 51 Invocation: "random/invocation", 52 }) 53 So(err, ShouldBeRPCInvalidArgument, "invocation: does not match") 54 }) 55 56 Convey(`Invocation Does Not Exist`, func() { 57 ctx = auth.WithState(ctx, &authtest.FakeState{ 58 Identity: "user:someone@example.com", 59 IdentityPermissions: []authtest.RealmPermission{ 60 {Realm: "chromium:@project", Permission: permSetSubmittedInvocation}, 61 }, 62 }) 63 64 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 65 Invocation: "invocations/inv", 66 }) 67 So(err, ShouldBeRPCPermissionDenied, "Caller does not have permission to mark invocations/inv submitted") 68 }) 69 70 Convey(`Insufficient Permissions`, func() { 71 ctx = auth.WithState(ctx, &authtest.FakeState{ 72 Identity: "user:someone@example.com", 73 IdentityPermissions: []authtest.RealmPermission{}, 74 }) 75 76 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 77 Invocation: "invocations/inv", 78 }) 79 So(err, ShouldBeRPCPermissionDenied, "Caller does not have permission to mark invocations/inv submitted") 80 }) 81 82 Convey(`Unfinalized Invocation`, func() { 83 ctx = auth.WithState(ctx, &authtest.FakeState{ 84 Identity: "user:someone@example.com", 85 IdentityPermissions: []authtest.RealmPermission{ 86 {Realm: "chromium:@project", Permission: permSetSubmittedInvocation}, 87 }, 88 }) 89 testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZING, map[string]any{ 90 "Realm": "chromium:try", 91 })) 92 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 93 Invocation: "invocations/inv", 94 }) 95 96 So(err, ShouldBeNil) 97 // No tasks queued by call if the invocation is yet to be finalized. Should be invoked by 98 // finalizer to mark submitted. 99 So(sched.Tasks(), ShouldHaveLength, 0) 100 101 submitted, err := invocations.ReadSubmitted(span.Single(ctx), invocations.ID("inv")) 102 So(err, ShouldBeNil) 103 So(submitted, ShouldBeTrue) 104 }) 105 106 Convey(`Already submitted`, func() { 107 ctx = auth.WithState(ctx, &authtest.FakeState{ 108 Identity: "user:someone@example.com", 109 IdentityPermissions: []authtest.RealmPermission{ 110 {Realm: "chromium:@project", Permission: permSetSubmittedInvocation}, 111 }, 112 }) 113 testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZED, map[string]any{ 114 "Realm": "chromium:try", 115 "Submitted": true, 116 })) 117 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 118 Invocation: "invocations/inv", 119 }) 120 121 // Workflow should terminate early if the invocation is already submitted. 122 So(err, ShouldBeNil) 123 So(sched.Tasks(), ShouldHaveLength, 0) 124 }) 125 126 Convey(`Success`, func() { 127 ctx = auth.WithState(ctx, &authtest.FakeState{ 128 Identity: "user:someone@example.com", 129 IdentityPermissions: []authtest.RealmPermission{ 130 {Realm: "chromium:@project", Permission: permSetSubmittedInvocation}, 131 }, 132 }) 133 testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZED, map[string]any{ 134 "Realm": "chromium:try", 135 })) 136 _, err := recorder.MarkInvocationSubmitted(ctx, &pb.MarkInvocationSubmittedRequest{ 137 Invocation: "invocations/inv", 138 }) 139 140 So(err, ShouldBeNil) 141 So(sched.Tasks(), ShouldHaveLength, 1) 142 So(sched.Tasks().Payloads(), ShouldResembleProto, []protoreflect.ProtoMessage{ 143 &taskspb.MarkInvocationSubmitted{InvocationId: "inv"}, 144 }) 145 }) 146 }) 147 }