blob: 339a523c082505546bb4bb9c55e7d623d15e0dc7 [file] [log] [blame]
// Copyright 2021 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 bq
import (
"context"
"testing"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
"go.chromium.org/luci/common/proto/gerrit"
"go.chromium.org/luci/gae/service/datastore"
"go.chromium.org/luci/server/tq/tqtesting"
cvbqpb "go.chromium.org/luci/cv/api/bigquery/v1"
"go.chromium.org/luci/cv/internal/changelist"
"go.chromium.org/luci/cv/internal/common"
"go.chromium.org/luci/cv/internal/cvtesting"
"go.chromium.org/luci/cv/internal/run"
. "github.com/smartystreets/goconvey/convey"
. "go.chromium.org/luci/common/testing/assertions"
)
func TestExportRunToBQ(t *testing.T) {
t.Parallel()
Convey("Exporting a Run to BQ works", t, func() {
ct := cvtesting.Test{AppID: "cv"}
ctx, cancel := ct.SetUp()
defer cancel()
exporter := NewExporter(ct.TQDispatcher, ct.BQFake)
// Set up datastore by putting a sample Run + RunCLs.
epoch := ct.Clock.Now().UTC()
runID := common.MakeRunID("lproject", epoch, 1, []byte("aaa"))
r := run.Run{
ID: runID,
Status: run.Status_SUCCEEDED,
ConfigGroupID: "sha256:deadbeefdeadbeef/cgroup",
CreateTime: epoch,
StartTime: epoch.Add(time.Minute * 2),
EndTime: epoch.Add(time.Minute * 25),
CLs: common.CLIDs{1},
Submission: nil,
Mode: run.DryRun,
}
So(datastore.Put(ctx, &r), ShouldBeNil)
So(datastore.Put(ctx, &run.RunCL{
ID: 1,
Run: datastore.MakeKey(ctx, run.RunKind, string(runID)),
ExternalID: "gerrit/foo-review.googlesource.com/111",
Detail: &changelist.Snapshot{
LuciProject: "lproject",
Patchset: 2,
MinEquivalentPatchset: 2,
Kind: &changelist.Snapshot_Gerrit{
Gerrit: &changelist.Gerrit{
Host: "foo-review.googlesource.com",
Info: &gerrit.ChangeInfo{
Number: 111,
Project: "gproject",
Ref: "refs/heads/main",
},
},
},
},
Trigger: &run.Trigger{Time: timestamppb.New(epoch)},
}), ShouldBeNil)
So(nil, ShouldBeNil)
// BQ Export tasks must be scheduled in a transaction.
schedule := func() error {
return datastore.RunInTransaction(ctx, func(tCtx context.Context) error {
return exporter.Schedule(tCtx, runID)
}, nil)
}
Convey("A row is sent", func() {
Convey("in prod", func() {
So(schedule(), ShouldBeNil)
ct.TQ.Run(ctx, tqtesting.StopAfterTask(exportRunToBQTaskClass))
rows := ct.BQFake.Rows("", CVDataset, CVTable)
So(rows, ShouldResembleProto, []*cvbqpb.Attempt{{
Key: "616161",
LuciProject: "lproject",
ConfigGroup: "cgroup",
ClGroupKey: "331ea2a6a5d5f3b3",
EquivalentClGroupKey: "47337d4707144297",
StartTime: timestamppb.New(epoch),
EndTime: timestamppb.New(epoch.Add(25 * time.Minute)),
GerritChanges: []*cvbqpb.GerritChange{
{
Host: "foo-review.googlesource.com",
Project: "gproject",
Change: 111,
Patchset: 2,
EarliestEquivalentPatchset: 2,
TriggerTime: timestamppb.New(epoch),
Mode: cvbqpb.Mode_DRY_RUN,
SubmitStatus: cvbqpb.GerritChange_PENDING,
},
},
Status: cvbqpb.AttemptStatus_SUCCESS,
Substatus: cvbqpb.AttemptSubstatus_NO_SUBSTATUS,
}})
// The same rows must be sent to legacy CQ table.
cqRows := ct.BQFake.Rows(legacyProject, legacyDataset, legacyTable)
So(cqRows, ShouldResembleProto, rows)
// And only these 2 rows have been sent.
So(ct.BQFake.TotalSent(), ShouldEqual, 2)
})
Convey("in dev", func() {
So(schedule(), ShouldBeNil)
ctx = common.SetDev(ctx)
ct.TQ.Run(ctx, tqtesting.StopAfterTask(exportRunToBQTaskClass))
So(ct.BQFake.Rows("", CVDataset, CVTable), ShouldHaveLength, 1)
// Must not send to production legacy.
So(ct.BQFake.Rows(legacyProject, legacyDataset, legacyTable), ShouldHaveLength, 0)
So(ct.BQFake.Rows(legacyProjectDev, legacyDataset, legacyTable), ShouldHaveLength, 1)
})
})
})
}