blob: 7d790c4274ad6dd6a2cda6c94b10d7d084d2b7d4 [file] [log] [blame]
// Copyright 2020 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 invocations
import (
"testing"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
"go.chromium.org/luci/common/clock/testclock"
"go.chromium.org/luci/server/span"
"go.chromium.org/luci/resultdb/internal/testutil"
"go.chromium.org/luci/resultdb/pbutil"
pb "go.chromium.org/luci/resultdb/proto/v1"
. "github.com/smartystreets/goconvey/convey"
)
// historyAccumulator has a method that can be passed as a callback to ByTimestamp.
type historyAccumulator struct {
invs []ID
}
func (ia *historyAccumulator) accumulate(inv ID, ts *timestamppb.Timestamp) error {
// We only need the invocationID in these tests.
_ = ts
ia.invs = append(ia.invs, inv)
return nil
}
func newHistoryAccumulator(capacity int) historyAccumulator {
return historyAccumulator{
invs: make([]ID, 0, capacity),
}
}
func TestByTimestamp(t *testing.T) {
Convey(`ByTimestamp`, t, func() {
ctx := testutil.SpannerTestContext(t)
realm := "testproject:bytimestamprealm"
start := testclock.TestRecentTimeUTC
middle := start.Add(time.Hour)
middle2 := middle.Add(30 * time.Minute)
end := middle.Add(time.Hour)
// Insert some Invocations.
testutil.MustApply(ctx,
insertInvocation("first", map[string]interface{}{
"CreateTime": start,
"HistoryTime": start,
"Realm": realm,
}),
insertInvocation("second", map[string]interface{}{
"CreateTime": middle,
"HistoryTime": middle,
"Realm": realm,
"TestResultVariantUnionRecursive": []string{"k:v"},
}),
insertInvocation("secondWrongRealm", map[string]interface{}{
"CreateTime": middle,
"HistoryTime": middle,
"Realm": "testproject:wrongrealm",
}),
insertInvocation("secondUnindexed", map[string]interface{}{
"CreateTime": middle,
// No HistoryTime.
"Realm": realm,
}),
insertInvocation("fourth", map[string]interface{}{
"CreateTime": end,
"HistoryTime": end,
"Realm": realm,
"TestResultVariantUnionRecursive": []string{"a:b", "k:v"},
}),
insertInvocation("third", map[string]interface{}{
"CreateTime": middle2,
"HistoryTime": middle2,
"Realm": realm,
"State": pb.Invocation_ACTIVE,
}),
)
ctx, cancel := span.ReadOnlyTransaction(ctx)
defer cancel()
beforePB := timestamppb.New(start.Add(-1 * time.Hour))
justBeforePB := timestamppb.New(start.Add(-1 * time.Second))
startPB := timestamppb.New(start)
middlePB := timestamppb.New(middle)
afterPB := timestamppb.New(end.Add(time.Hour))
muchLaterPB := timestamppb.New(end.Add(24 * time.Hour))
q := &HistoryQuery{
Realm: realm,
}
Convey(`all but the most recent`, func() {
ac := newHistoryAccumulator(2)
q.TimeRange = &pb.TimeRange{Earliest: startPB, Latest: middlePB}
err := q.ByTimestamp(ctx, ac.accumulate)
So(err, ShouldBeNil)
So(ac.invs, ShouldHaveLength, 2)
So(string(ac.invs[0]), ShouldEndWith, "second")
So(string(ac.invs[1]), ShouldEndWith, "first")
})
Convey(`all but the oldest`, func() {
ac := newHistoryAccumulator(2)
q.TimeRange = &pb.TimeRange{Earliest: middlePB, Latest: afterPB}
err := q.ByTimestamp(ctx, ac.accumulate)
So(err, ShouldBeNil)
So(ac.invs, ShouldHaveLength, 3)
So(string(ac.invs[0]), ShouldEndWith, "fourth")
So(string(ac.invs[1]), ShouldEndWith, "third")
So(string(ac.invs[2]), ShouldEndWith, "second")
})
Convey(`all results`, func() {
ac := newHistoryAccumulator(2)
q.TimeRange = &pb.TimeRange{Earliest: startPB, Latest: afterPB}
err := q.ByTimestamp(ctx, ac.accumulate)
So(err, ShouldBeNil)
So(ac.invs, ShouldHaveLength, 4)
So(string(ac.invs[0]), ShouldEndWith, "fourth")
So(string(ac.invs[1]), ShouldEndWith, "third")
So(string(ac.invs[2]), ShouldEndWith, "second")
So(string(ac.invs[3]), ShouldEndWith, "first")
})
Convey(`before first result`, func() {
ac := newHistoryAccumulator(1)
q.TimeRange = &pb.TimeRange{Earliest: beforePB, Latest: justBeforePB}
err := q.ByTimestamp(ctx, ac.accumulate)
So(err, ShouldBeNil)
So(ac.invs, ShouldHaveLength, 0)
})
Convey(`after last result`, func() {
ac := newHistoryAccumulator(1)
q.TimeRange = &pb.TimeRange{Earliest: afterPB, Latest: muchLaterPB}
err := q.ByTimestamp(ctx, ac.accumulate)
So(err, ShouldBeNil)
So(ac.invs, ShouldHaveLength, 0)
})
Convey(`matched variant`, func() {
ac := newHistoryAccumulator(2)
q.TimeRange = &pb.TimeRange{Earliest: startPB, Latest: afterPB}
q.Predicate = &pb.TestResultPredicate{
Variant: &pb.VariantPredicate{
Predicate: &pb.VariantPredicate_Equals{
Equals: pbutil.Variant("a", "b"),
},
},
}
err := q.ByTimestamp(ctx, ac.accumulate)
So(err, ShouldBeNil)
So(ac.invs, ShouldHaveLength, 2)
So(string(ac.invs[0]), ShouldEndWith, "fourth")
So(string(ac.invs[1]), ShouldEndWith, "third")
})
})
}