blob: b19435da1f1d2feba6adf61593e3052aea84ae2b [file] [log] [blame]
// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package recorder
import (
"context"
"testing"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"go.chromium.org/luci/common/clock/testclock"
"go.chromium.org/luci/server/span"
"go.chromium.org/luci/resultdb/internal/invocations"
"go.chromium.org/luci/resultdb/internal/testutil"
"go.chromium.org/luci/resultdb/internal/testutil/insert"
"go.chromium.org/luci/resultdb/pbutil"
pb "go.chromium.org/luci/resultdb/proto/v1"
. "github.com/smartystreets/goconvey/convey"
. "go.chromium.org/luci/common/testing/assertions"
)
func TestMutateInvocation(t *testing.T) {
Convey("MayMutateInvocation", t, func() {
ctx := testutil.SpannerTestContext(t)
mayMutate := func(id invocations.ID) error {
return mutateInvocation(ctx, id, func(ctx context.Context) error {
return nil
})
}
Convey("no token", func() {
err := mayMutate("inv")
So(err, ShouldHaveAppStatus, codes.Unauthenticated, `missing update-token metadata value`)
})
Convey("with token", func() {
token, err := generateInvocationToken(ctx, "inv")
So(err, ShouldBeNil)
ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(pb.UpdateTokenMetadataKey, token))
Convey(`no invocation`, func() {
err := mayMutate("inv")
So(err, ShouldHaveAppStatus, codes.NotFound, `invocations/inv not found`)
})
Convey(`with finalized invocation`, func() {
testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZED, nil))
err := mayMutate("inv")
So(err, ShouldHaveAppStatus, codes.FailedPrecondition, `invocations/inv is not active`)
})
Convey(`with active invocation and different token`, func() {
testutil.MustApply(ctx, insert.Invocation("inv2", pb.Invocation_ACTIVE, nil))
err := mayMutate("inv2")
So(err, ShouldHaveAppStatus, codes.PermissionDenied, `invalid update token`)
})
Convey(`with active invocation and same token`, func() {
testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_ACTIVE, nil))
err := mayMutate("inv")
So(err, ShouldBeNil)
})
})
})
}
func TestReadInvocation(t *testing.T) {
Convey(`ReadInvocationFull`, t, func() {
ctx := testutil.SpannerTestContext(t)
ct := testclock.TestRecentTimeUTC
readInv := func() *pb.Invocation {
ctx, cancel := span.ReadOnlyTransaction(ctx)
defer cancel()
inv, err := invocations.Read(ctx, "inv")
So(err, ShouldBeNil)
return inv
}
Convey(`Finalized`, func() {
testutil.MustApply(ctx, insert.Invocation("inv", pb.Invocation_FINALIZED, map[string]any{
"CreateTime": ct,
"Deadline": ct.Add(time.Hour),
"FinalizeStartTime": ct.Add(2 * time.Hour),
"FinalizeTime": ct.Add(3 * time.Hour),
}))
inv := readInv()
expected := &pb.Invocation{
Name: "invocations/inv",
State: pb.Invocation_FINALIZED,
CreateTime: pbutil.MustTimestampProto(ct),
Deadline: pbutil.MustTimestampProto(ct.Add(time.Hour)),
FinalizeStartTime: pbutil.MustTimestampProto(ct.Add(2 * time.Hour)),
FinalizeTime: pbutil.MustTimestampProto(ct.Add(3 * time.Hour)),
Realm: insert.TestRealm,
}
So(inv, ShouldResembleProto, expected)
Convey(`with included invocations`, func() {
testutil.MustApply(ctx,
insert.Invocation("included0", pb.Invocation_FINALIZED, nil),
insert.Invocation("included1", pb.Invocation_FINALIZED, nil),
insert.Inclusion("inv", "included0"),
insert.Inclusion("inv", "included1"),
)
inv := readInv()
So(inv.IncludedInvocations, ShouldResemble, []string{"invocations/included0", "invocations/included1"})
})
})
})
}