go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/internal/services/recorder/invocation_test.go (about) 1 // Copyright 2019 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 "context" 19 "testing" 20 "time" 21 22 "google.golang.org/grpc/codes" 23 "google.golang.org/grpc/metadata" 24 25 "go.chromium.org/luci/common/clock/testclock" 26 "go.chromium.org/luci/server/span" 27 28 "go.chromium.org/luci/resultdb/internal/invocations" 29 "go.chromium.org/luci/resultdb/internal/testutil" 30 "go.chromium.org/luci/resultdb/internal/testutil/insert" 31 "go.chromium.org/luci/resultdb/pbutil" 32 pb "go.chromium.org/luci/resultdb/proto/v1" 33 34 . "github.com/smartystreets/goconvey/convey" 35 . "go.chromium.org/luci/common/testing/assertions" 36 ) 37 38 func TestMutateInvocation(t *testing.T) { 39 Convey("MayMutateInvocation", t, func() { 40 ctx := testutil.SpannerTestContext(t) 41 42 mayMutate := func(id invocations.ID) error { 43 return mutateInvocation(ctx, id, func(ctx context.Context) error { 44 return nil 45 }) 46 } 47 48 Convey("no token", func() { 49 err := mayMutate("inv") 50 So(err, ShouldHaveAppStatus, codes.Unauthenticated, `missing update-token metadata value`) 51 }) 52 53 Convey("with token", func() { 54 token, err := generateInvocationToken(ctx, "inv") 55 So(err, ShouldBeNil) 56 ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(pb.UpdateTokenMetadataKey, token)) 57 58 Convey(`no invocation`, func() { 59 err := mayMutate("inv") 60 So(err, ShouldHaveAppStatus, codes.NotFound, `invocations/inv not found`) 61 }) 62 63 Convey(`with finalized invocation`, func() { 64 testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZED, nil)) 65 err := mayMutate("inv") 66 So(err, ShouldHaveAppStatus, codes.FailedPrecondition, `invocations/inv is not active`) 67 }) 68 69 Convey(`with active invocation and different token`, func() { 70 testutil.MustApply(ctx, insert.Invocation("inv2", pb.Invocation_ACTIVE, nil)) 71 err := mayMutate("inv2") 72 So(err, ShouldHaveAppStatus, codes.PermissionDenied, `invalid update token`) 73 }) 74 75 Convey(`with active invocation and same token`, func() { 76 testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_ACTIVE, nil)) 77 err := mayMutate("inv") 78 So(err, ShouldBeNil) 79 }) 80 }) 81 }) 82 } 83 84 func TestReadInvocation(t *testing.T) { 85 Convey(`ReadInvocationFull`, t, func() { 86 ctx := testutil.SpannerTestContext(t) 87 ct := testclock.TestRecentTimeUTC 88 89 readInv := func() *pb.Invocation { 90 ctx, cancel := span.ReadOnlyTransaction(ctx) 91 defer cancel() 92 93 inv, err := invocations.Read(ctx, "inv") 94 So(err, ShouldBeNil) 95 return inv 96 } 97 98 Convey(`Finalized`, func() { 99 testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZED, map[string]any{ 100 "CreateTime": ct, 101 "Deadline": ct.Add(time.Hour), 102 "FinalizeTime": ct.Add(time.Hour), 103 })) 104 105 inv := readInv() 106 expected := &pb.Invocation{ 107 Name: "invocations/inv", 108 State: pb.Invocation_FINALIZED, 109 CreateTime: pbutil.MustTimestampProto(ct), 110 Deadline: pbutil.MustTimestampProto(ct.Add(time.Hour)), 111 FinalizeTime: pbutil.MustTimestampProto(ct.Add(time.Hour)), 112 Realm: insert.TestRealm, 113 } 114 So(inv, ShouldResembleProto, expected) 115 116 Convey(`with included invocations`, func() { 117 testutil.MustApply(ctx, 118 insert.Invocation("included0", pb.Invocation_FINALIZED, nil), 119 insert.Invocation("included1", pb.Invocation_FINALIZED, nil), 120 insert.Inclusion("inv", "included0"), 121 insert.Inclusion("inv", "included1"), 122 ) 123 124 inv := readInv() 125 So(inv.IncludedInvocations, ShouldResemble, []string{"invocations/included0", "invocations/included1"}) 126 }) 127 }) 128 }) 129 }