blob: da558de792dcc84c47ef5b47a3a45d50e28b98b2 [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 resultdb
import (
"sort"
"testing"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"go.chromium.org/luci/common/tsmon"
"go.chromium.org/luci/server/auth"
"go.chromium.org/luci/server/auth/authtest"
"go.chromium.org/luci/resultdb/internal/testutil"
"go.chromium.org/luci/resultdb/internal/testutil/insert"
pb "go.chromium.org/luci/resultdb/proto/v1"
"go.chromium.org/luci/resultdb/rdbperms"
. "github.com/smartystreets/goconvey/convey"
. "go.chromium.org/luci/common/testing/assertions"
)
func TestValidateQueryRequest(t *testing.T) {
t.Parallel()
Convey(`TestValidateQueryRequest`, t, func() {
Convey(`no invocations`, func() {
err := validateQueryRequest(&pb.QueryTestResultsRequest{})
So(err, ShouldErrLike, `invocations: unspecified`)
})
Convey(`invalid invocation`, func() {
err := validateQueryRequest(&pb.QueryTestResultsRequest{
Invocations: []string{"x"},
})
So(err, ShouldErrLike, `invocations: "x": does not match`)
})
Convey(`invalid page size`, func() {
err := validateQueryRequest(&pb.QueryTestResultsRequest{
Invocations: []string{"invocations/x"},
PageSize: -1,
})
So(err, ShouldErrLike, `page_size: negative`)
})
})
}
func TestValidateQueryTestResultsRequest(t *testing.T) {
t.Parallel()
Convey(`Valid`, t, func() {
err := validateQueryTestResultsRequest(&pb.QueryTestResultsRequest{
Invocations: []string{"invocations/x"},
PageSize: 50,
})
So(err, ShouldBeNil)
})
Convey(`invalid invocation`, t, func() {
err := validateQueryTestResultsRequest(&pb.QueryTestResultsRequest{
Invocations: []string{"x"},
})
So(err, ShouldErrLike, `invocations: "x": does not match`)
})
}
func TestQueryTestResults(t *testing.T) {
Convey(`QueryTestResults`, t, func() {
ctx := auth.WithState(testutil.SpannerTestContext(t), &authtest.FakeState{
Identity: "user:someone@example.com",
IdentityPermissions: []authtest.RealmPermission{
{Realm: "testproject:testrealm", Permission: rdbperms.PermListTestResults},
},
})
ctx, _ = tsmon.WithDummyInMemory(ctx)
insertInv := insert.FinalizedInvocationWithInclusions
insertTRs := insert.TestResults
testutil.MustApply(ctx, testutil.CombineMutations(
insertInv("x", map[string]any{"Realm": "secretproject:testrealm"}, "a"),
insertInv("a", map[string]any{"Realm": "testproject:testrealm"}, "b"),
insertInv("b", map[string]any{"Realm": "otherproject:testrealm"}, "c"),
// The invocation c doesn't have any included invocation.
insertInv("c", map[string]any{"Realm": "testproject:testrealm"}),
insertTRs("a", "A", nil, pb.TestStatus_FAIL, pb.TestStatus_PASS),
insertTRs("b", "B", nil, pb.TestStatus_CRASH, pb.TestStatus_PASS),
insertTRs("c", "C", nil, pb.TestStatus_PASS),
)...)
srv := newTestResultDBService()
Convey(`Without readMask`, func() {
res, err := srv.QueryTestResults(ctx, &pb.QueryTestResultsRequest{
Invocations: []string{"invocations/a"},
})
So(err, ShouldBeNil)
So(res.TestResults, ShouldHaveLength, 5)
})
Convey(`Permission denied`, func() {
_, err := srv.QueryTestResults(ctx, &pb.QueryTestResultsRequest{
Invocations: []string{"invocations/x"},
})
So(err, ShouldHaveAppStatus, codes.PermissionDenied)
})
Convey(`Valid with included invocation`, func() {
res, err := srv.QueryTestResults(ctx, &pb.QueryTestResultsRequest{
Invocations: []string{"invocations/a"},
})
So(err, ShouldBeNil)
So(res.TestResults, ShouldHaveLength, 5)
sort.Slice(res.TestResults, func(i, j int) bool {
return res.TestResults[i].Name < res.TestResults[j].Name
})
So(res.TestResults[0].Name, ShouldEqual, "invocations/a/tests/A/results/0")
So(res.TestResults[0].Status, ShouldEqual, pb.TestStatus_FAIL)
So(res.TestResults[0].SummaryHtml, ShouldEqual, "")
So(res.TestResults[1].Name, ShouldEqual, "invocations/a/tests/A/results/1")
So(res.TestResults[1].Status, ShouldEqual, pb.TestStatus_PASS)
So(res.TestResults[2].Name, ShouldEqual, "invocations/b/tests/B/results/0")
So(res.TestResults[2].Status, ShouldEqual, pb.TestStatus_CRASH)
So(res.TestResults[3].Name, ShouldEqual, "invocations/b/tests/B/results/1")
So(res.TestResults[3].Status, ShouldEqual, pb.TestStatus_PASS)
So(res.TestResults[4].Name, ShouldEqual, "invocations/c/tests/C/results/0")
So(res.TestResults[4].Status, ShouldEqual, pb.TestStatus_PASS)
})
Convey(`Valid without included invocation`, func() {
res, err := srv.QueryTestResults(ctx, &pb.QueryTestResultsRequest{
Invocations: []string{"invocations/c"},
})
So(err, ShouldBeNil)
So(res.TestResults, ShouldHaveLength, 1)
sort.Slice(res.TestResults, func(i, j int) bool {
return res.TestResults[i].Name < res.TestResults[j].Name
})
So(res.TestResults[0].Name, ShouldEqual, "invocations/c/tests/C/results/0")
So(res.TestResults[0].Status, ShouldEqual, pb.TestStatus_PASS)
})
Convey(`Valid with missing included invocation`, func() {
testutil.MustApply(
ctx,
// The invocation missinginv is missing in Invocations table.
insert.Inclusion("a", "missinginv"),
)
res, err := srv.QueryTestResults(ctx, &pb.QueryTestResultsRequest{
Invocations: []string{"invocations/a"},
})
So(err, ShouldBeNil)
So(res.TestResults, ShouldHaveLength, 5)
sort.Slice(res.TestResults, func(i, j int) bool {
return res.TestResults[i].Name < res.TestResults[j].Name
})
So(res.TestResults[0].Name, ShouldEqual, "invocations/a/tests/A/results/0")
So(res.TestResults[0].Status, ShouldEqual, pb.TestStatus_FAIL)
So(res.TestResults[0].SummaryHtml, ShouldEqual, "")
So(res.TestResults[1].Name, ShouldEqual, "invocations/a/tests/A/results/1")
So(res.TestResults[1].Status, ShouldEqual, pb.TestStatus_PASS)
So(res.TestResults[2].Name, ShouldEqual, "invocations/b/tests/B/results/0")
So(res.TestResults[2].Status, ShouldEqual, pb.TestStatus_CRASH)
So(res.TestResults[3].Name, ShouldEqual, "invocations/b/tests/B/results/1")
So(res.TestResults[3].Status, ShouldEqual, pb.TestStatus_PASS)
So(res.TestResults[4].Name, ShouldEqual, "invocations/c/tests/C/results/0")
So(res.TestResults[4].Status, ShouldEqual, pb.TestStatus_PASS)
})
Convey(`With readMask`, func() {
res, err := srv.QueryTestResults(ctx, &pb.QueryTestResultsRequest{
Invocations: []string{"invocations/a"},
PageSize: 1,
ReadMask: &field_mask.FieldMask{
Paths: []string{
"status",
"summary_html",
}},
})
So(err, ShouldBeNil)
So(res.TestResults[0].Name, ShouldEqual, "invocations/c/tests/C/results/0")
So(res.TestResults[0].TestId, ShouldEqual, "")
So(res.TestResults[0].SummaryHtml, ShouldEqual, "SummaryHtml")
})
})
}