blob: 81d3cb1198f11ce517bd4a040421da6b00c0df24 [file] [log] [blame]
package config
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.skia.org/infra/autoroll/go/config_vars"
"go.skia.org/infra/go/chrome_branch/mocks"
)
func makeConfig() *Config {
gerritConfig := &GerritConfig{
Url: "fake.gerrit.url",
Project: "fake-project",
Config: GerritConfig_CHROMIUM,
}
transitiveDeps := []*TransitiveDepConfig{
{
Child: &VersionFileConfig{
Id: "transitive-dep-1",
File: []*VersionFileConfig_File{
{Path: "DEPS"},
},
},
Parent: &VersionFileConfig{
Id: "transitive-dep-1",
File: []*VersionFileConfig_File{
{Path: "DEPS"},
},
},
},
{
Child: &VersionFileConfig{
Id: "transitive-dep-2",
File: []*VersionFileConfig_File{
{Path: "DEPS"},
},
},
Parent: &VersionFileConfig{
Id: "transitive-dep-2",
File: []*VersionFileConfig_File{
{Path: "DEPS"},
},
},
},
}
childDeps := []*VersionFileConfig{}
for _, dep := range transitiveDeps {
childDeps = append(childDeps, dep.Child.Copy())
}
return &Config{
RollerName: "test",
ChildDisplayName: "test",
ParentDisplayName: "test",
ParentWaterfall: "https://test",
OwnerPrimary: "me",
OwnerSecondary: "you",
Contacts: []string{"us@google.com"},
ServiceAccount: "my-service-account",
Reviewer: []string{"me@google.com"},
CommitMsg: &CommitMsgConfig{
BuiltIn: CommitMsgConfig_DEFAULT,
},
CodeReview: &Config_Gerrit{
Gerrit: gerritConfig,
},
Kubernetes: &KubernetesConfig{
Cpu: "1m",
Memory: "10MB",
Image: "fake-image",
Disk: "10GB",
},
RepoManager: &Config_ParentChildRepoManager{
ParentChildRepoManager: &ParentChildRepoManagerConfig{
Parent: &ParentChildRepoManagerConfig_DepsLocalGerritParent{
&DEPSLocalGerritParentConfig{
DepsLocal: &DEPSLocalParentConfig{
GitCheckout: &GitCheckoutParentConfig{
GitCheckout: &GitCheckoutConfig{
Branch: "main",
RepoUrl: "https://parent.repo.git",
},
Dep: &DependencyConfig{
Primary: &VersionFileConfig{
Id: "primary-dep-id",
File: []*VersionFileConfig_File{
{Path: "DEPS"},
},
},
Transitive: transitiveDepConfigSlice(transitiveDeps).Copy(),
},
},
ChildPath: "path/to/child",
},
Gerrit: gerritConfig,
},
},
Child: &ParentChildRepoManagerConfig_GitCheckoutChild{
&GitCheckoutChildConfig{
GitCheckout: &GitCheckoutConfig{
Branch: "main",
RepoUrl: "https://child.repo.git",
Dependencies: childDeps,
},
},
},
},
},
TransitiveDeps: transitiveDepConfigSlice(transitiveDeps).Copy(),
}
}
func TestValidation_TransitiveDeps(t *testing.T) {
// Verify that the vanilla config passes validation.
t.Run("baseline", func(t *testing.T) {
require.NoError(t, makeConfig().Validate())
})
t.Run("missing child dep", func(t *testing.T) {
cfg := makeConfig()
cfg.GetParentChildRepoManager().GetGitCheckoutChild().GitCheckout.Dependencies = cfg.GetParentChildRepoManager().GetGitCheckoutChild().GitCheckout.Dependencies[1:]
require.ErrorContains(t, cfg.Validate(), "top level transitive dependency count 2 does not match dependency count 1 set on child")
})
t.Run("missing parent dep", func(t *testing.T) {
cfg := makeConfig()
cfg.GetParentChildRepoManager().GetDepsLocalGerritParent().DepsLocal.GitCheckout.Dep.Transitive = cfg.GetParentChildRepoManager().GetDepsLocalGerritParent().DepsLocal.GitCheckout.Dep.Transitive[1:]
require.ErrorContains(t, cfg.Validate(), "top level transitive dependency count 2 does not match transitive dependency count 1 set on parent")
})
t.Run("missing top level dep", func(t *testing.T) {
cfg := makeConfig()
cfg.TransitiveDeps = cfg.TransitiveDeps[1:]
require.ErrorContains(t, cfg.Validate(), "top level transitive dependency count 1 does not match transitive dependency count 2 set on parent")
})
t.Run("mismatched child dep", func(t *testing.T) {
cfg := makeConfig()
cfg.GetParentChildRepoManager().GetGitCheckoutChild().GitCheckout.Dependencies[0].Id += "oops"
require.ErrorContains(t, cfg.Validate(), "top level transitive dependency differs from dependency set on child")
})
t.Run("mismatched parent dep", func(t *testing.T) {
cfg := makeConfig()
cfg.GetParentChildRepoManager().GetDepsLocalGerritParent().DepsLocal.GitCheckout.Dep.Transitive[0].LogUrlTmpl = "oops"
require.ErrorContains(t, cfg.Validate(), "top level transitive dependency differs from transitive dependency set on parent")
})
t.Run("mismatched top level dep", func(t *testing.T) {
cfg := makeConfig()
cfg.TransitiveDeps[0].LogUrlTmpl = "oops"
require.ErrorContains(t, cfg.Validate(), "top level transitive dependency differs from transitive dependency set on parent")
})
}
func TestValidation_RollerName(t *testing.T) {
t.Run("starts with dash", func(t *testing.T) {
cfg := makeConfig()
cfg.RollerName = "-" + cfg.RollerName
require.ErrorContains(t, cfg.Validate(), "RollerName is invalid")
})
t.Run("ends with dash", func(t *testing.T) {
cfg := makeConfig()
cfg.RollerName = cfg.RollerName + "-"
require.ErrorContains(t, cfg.Validate(), "RollerName is invalid")
})
t.Run("contains invalid chars", func(t *testing.T) {
cfg := makeConfig()
cfg.RollerName = "bad.name"
require.ErrorContains(t, cfg.Validate(), "RollerName is invalid")
})
}
func TestParseTrybotName(t *testing.T) {
cbc := &mocks.Client{}
fakeVars := config_vars.FakeVars()
cbc.On("Get", mock.Anything).Return(fakeVars.Branches.Chromium, fakeVars.Branches.ActiveMilestones, nil)
reg, err := config_vars.NewRegistry(t.Context(), cbc)
require.NoError(t, err)
t.Run("luci.project.bucket:builder format", func(t *testing.T) {
project, bucket, builders, err := ParseTrybotName(reg, "luci.chromium.try:dawn-linux-x64-deps-rel")
assert.NoError(t, err)
assert.Equal(t, "chromium", project)
assert.Equal(t, "try", bucket)
assert.Equal(t, []string{"dawn-linux-x64-deps-rel"}, builders)
})
t.Run("project/bucket:builder format", func(t *testing.T) {
project, bucket, builders, err := ParseTrybotName(reg, "skia/skia.primary:Build-Ubuntu24.04-Clang-x86_64-Release-ANGLE")
assert.NoError(t, err)
assert.Equal(t, "skia", project)
assert.Equal(t, "skia.primary", bucket)
assert.Equal(t, []string{"Build-Ubuntu24.04-Clang-x86_64-Release-ANGLE"}, builders)
})
t.Run("multiple builders", func(t *testing.T) {
project, bucket, builders, err := ParseTrybotName(reg, "luci.chromium.try:android-11-x86-rel,android-webview-10-x86-rel-tests")
assert.NoError(t, err)
assert.Equal(t, "chromium", project)
assert.Equal(t, "try", bucket)
assert.Equal(t, []string{"android-11-x86-rel", "android-webview-10-x86-rel-tests"}, builders)
})
t.Run("templated builder", func(t *testing.T) {
project, bucket, builders, err := ParseTrybotName(reg, "luci.chrome-m{{.Branches.Chromium.Beta.Milestone}}.try:linux-chrome")
assert.NoError(t, err)
assert.Equal(t, "chrome-m81", project)
assert.Equal(t, "try", bucket)
assert.Equal(t, []string{"linux-chrome"}, builders)
})
// Failure cases.
testFailure := func(name, input string) {
t.Run(name, func(t *testing.T) {
project, bucket, builders, err := ParseTrybotName(reg, input)
assert.Error(t, err, "got project=%s bucket=%s builders=%v", project, bucket, builders)
})
}
testFailure("lone builder name", "lone-builder-name")
testFailure("lone builder name with luci prefix", "luci.no-project-or-bucket")
testFailure("builder without bucket", "luci.some-project:builder-without-bucket")
testFailure("multiple builders without project or bucket", "multiple,builders,without,project")
testFailure("empty name", "")
testFailure("bad template", "luci.chromium.try/builder-at-{{.SomethingBogus}}")
}