blob: 4b5a55e0ef6b9330a0711f702ac615fc91be5c1a [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 rpc
import (
"context"
"fmt"
"strings"
"testing"
"time"
"go.chromium.org/luci/buildbucket/appengine/model"
pb "go.chromium.org/luci/buildbucket/proto"
"google.golang.org/grpc/metadata"
. "github.com/smartystreets/goconvey/convey"
"go.chromium.org/luci/common/clock/testclock"
. "go.chromium.org/luci/common/testing/assertions"
)
func init() {
// By default cause Build.Proto.UpdateTime fields to be unset
model.OverrideGlobalBuildUpdateTimeClock(nil)
}
func TestValidateTags(t *testing.T) {
t.Parallel()
Convey("validate build set", t, func() {
Convey("valid", func() {
// test gitiles format
gitiles := fmt.Sprintf("commit/gitiles/chromium.googlesource.com/chromium/src/+/%s", strings.Repeat("a", 40))
So(validateBuildSet(gitiles), ShouldBeNil)
// test gerrit format
So(validateBuildSet("patch/gerrit/chromium-review.googlesource.com/123/456"), ShouldBeNil)
// test user format
So(validateBuildSet("myformat/x"), ShouldBeNil)
})
Convey("invalid", func() {
So(validateBuildSet("commit/gitiles/chromium.googlesource.com/chromium/src.git/+/aaa"), ShouldErrLike, `does not match regex "^commit/gitiles/([^/]+)/(.+?)/\+/([a-f0-9]{40})$"`)
gitiles := fmt.Sprintf("commit/gitiles/chromium.googlesource.com/a/chromium/src/+/%s", strings.Repeat("a", 40))
So(validateBuildSet(gitiles), ShouldErrLike, `gitiles project must not start with "a/"`)
gitiles = fmt.Sprintf("commit/gitiles/chromium.googlesource.com/chromium/src.git/+/%s", strings.Repeat("a", 40))
So(validateBuildSet(gitiles), ShouldErrLike, `gitiles project must not end with ".git"`)
So(validateBuildSet("patch/gerrit/chromium-review.googlesource.com/aa/bb"), ShouldErrLike, `does not match regex "^patch/gerrit/([^/]+)/(\d+)/(\d+)$"`)
So(validateBuildSet(strings.Repeat("a", 2000)), ShouldErrLike, "buildset tag is too long")
})
})
Convey("validate tags", t, func() {
Convey("invalid", func() {
// in general
So(validateTags([]*pb.StringPair{{Key: "k:1", Value: "v"}}, TagNew), ShouldErrLike, "cannot have a colon")
// build address
So(validateTags([]*pb.StringPair{{Key: "build_address", Value: "v"}}, TagNew), ShouldErrLike, `tag "build_address" is reserved`)
So(validateTags([]*pb.StringPair{{Key: "build_address", Value: "v"}}, TagAppend), ShouldErrLike, `cannot be added to an existing build`)
// buildset
So(validateTags([]*pb.StringPair{{Key: "buildset", Value: "patch/gerrit/foo"}}, TagNew), ShouldErrLike, `does not match regex "^patch/gerrit/([^/]+)/(\d+)/(\d+)$"`)
gitiles1 := fmt.Sprintf("commit/gitiles/chromium.googlesource.com/chromium/src/+/%s", strings.Repeat("a", 40))
gitiles2 := fmt.Sprintf("commit/gitiles/chromium.googlesource.com/chromium/src/+/%s", strings.Repeat("b", 40))
So(validateTags([]*pb.StringPair{
{Key: "buildset", Value: gitiles1},
{Key: "buildset", Value: gitiles2},
}, TagNew),
ShouldErrLike,
fmt.Sprintf(`tag "buildset:%s" conflicts with tag "buildset:%s"`, gitiles2, gitiles1))
So(validateTags([]*pb.StringPair{
{Key: "buildset", Value: gitiles1},
{Key: "buildset", Value: gitiles1},
}, TagNew),
ShouldBeNil)
// builder
So(validateTags([]*pb.StringPair{
{Key: "builder", Value: "1"},
{Key: "builder", Value: "2"},
}, TagNew),
ShouldErrLike,
`tag "builder:2" conflicts with tag "builder:1"`)
So(validateTags([]*pb.StringPair{
{Key: "builder", Value: "1"},
{Key: "builder", Value: "1"},
}, TagNew),
ShouldBeNil)
So(validateTags([]*pb.StringPair{{Key: "builder", Value: "v"}}, TagAppend), ShouldErrLike, "cannot be added to an existing build")
})
})
Convey("validate summary_markdown", t, func() {
Convey("valid", func() {
So(validateSummaryMarkdown("[this](http://example.org) is a link"), ShouldBeNil)
})
Convey("too big", func() {
So(validateSummaryMarkdown(strings.Repeat("☕", summaryMarkdownMaxLength)), ShouldErrLike, "too big to accept")
})
})
Convey("validateCommit", t, func() {
Convey("nil", func() {
err := validateCommit(nil)
So(err, ShouldErrLike, "host is required")
})
Convey("empty", func() {
cm := &pb.GitilesCommit{}
err := validateCommit(cm)
So(err, ShouldErrLike, "host is required")
})
Convey("project", func() {
cm := &pb.GitilesCommit{
Host: "host",
}
err := validateCommit(cm)
So(err, ShouldErrLike, "project is required")
})
Convey("id", func() {
Convey("invalid id", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Id: "id",
}
err := validateCommit(cm)
// sha1
So(err, ShouldErrLike, "id must match")
})
Convey("position", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Id: "id",
Position: 1,
}
err := validateCommit(cm)
So(err, ShouldErrLike, "position requires ref")
})
})
Convey("ref", func() {
Convey("invalid ref", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Ref: "ref",
}
err := validateCommit(cm)
So(err, ShouldErrLike, "ref must match")
})
Convey("valid, but w/ invalid id", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Ref: "refs/r1",
Id: "id",
}
err := validateCommit(cm)
So(err, ShouldErrLike, "id must match")
})
})
Convey("neither id nor ref", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
}
err := validateCommit(cm)
So(err, ShouldErrLike, "one of")
})
Convey("valid", func() {
Convey("id", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Id: "1234567890123456789012345678901234567890",
}
err := validateCommit(cm)
So(err, ShouldBeNil)
})
Convey("ref", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Ref: "refs/ref",
Position: 1,
}
err := validateCommit(cm)
So(err, ShouldBeNil)
})
})
})
Convey("validateCommitWithRef", t, func() {
Convey("nil", func() {
So(validateCommitWithRef(nil), ShouldErrLike, "ref is required")
})
Convey("empty", func() {
So(validateCommitWithRef(&pb.GitilesCommit{}), ShouldErrLike, "ref is required")
})
Convey("with id", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Id: "id",
}
So(validateCommitWithRef(cm), ShouldErrLike, "ref is required")
})
Convey("with ref", func() {
cm := &pb.GitilesCommit{
Host: "host",
Project: "project",
Ref: "refs/",
Position: 1,
}
So(validateCommitWithRef(cm), ShouldBeNil)
})
})
}
func TestValidateBuildToken(t *testing.T) {
t.Parallel()
Convey("validateBuildToken", t, func() {
ctx := context.Background()
ctx, _ = testclock.UseTime(ctx, time.Unix(1444945245, 0))
b := &model.Build{}
tk1 := "a token"
tk2 := "b token"
Convey("Works", func() {
b.UpdateToken = tk1
ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(BuildTokenKey, tk1))
So(validateBuildToken(ctx, b), ShouldBeNil)
})
Convey("Fails", func() {
Convey("if unmatched", func() {
b.UpdateToken = tk1
ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(BuildTokenKey, tk2))
So(validateBuildToken(ctx, b), ShouldNotBeNil)
})
Convey("if missing", func() {
b.UpdateToken = tk1
So(validateBuildToken(ctx, b), ShouldNotBeNil)
})
})
})
}