blob: 3f55d35027f753885d2ac3c7e6575a9d8d31d209 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"context"
"os"
"path/filepath"
"testing"
"go.fuchsia.dev/infra/cmd/roller-configurator/proto"
"google.golang.org/protobuf/types/known/structpb"
)
var filesWithGitmodules = map[string]string{
".gitmodules": `
[submodule "path/to/submodule"]
url = "https://example.com/asubmodule"
`,
}
func TestValidate_valid(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
config *proto.Config
files map[string]string
}{
{
name: "empty",
config: &proto.Config{},
},
{
name: "submodule with interval schedule",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
Schedule: "with 24h interval",
},
},
},
files: filesWithGitmodules,
},
{
name: "cipd ensure file with cron schedule",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_CipdEnsureFile{CipdEnsureFile: &proto.CIPDEnsureFile{
Path: "path/to/cipd.ensure",
Ref: "foo",
}},
Schedule: "0/20 * * * *",
},
},
},
files: map[string]string{
"path/to/cipd.ensure": ``,
},
},
{
name: "jiri project with notify emails",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriProject{JiriProject: &proto.JiriProject{
Manifest: "path/to/manifest",
Project: "project-name",
}},
NotifyEmails: []string{
"foo@example.com",
"bar@example.com",
},
},
},
},
files: map[string]string{
"path/to/manifest": ``,
},
},
{
name: "jiri packages",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriPackages{JiriPackages: &proto.JiriPackages{
PackagesByManifest: map[string]*structpb.ListValue{
"path/to/manifest": {
Values: []*structpb.Value{
structpb.NewStringValue("package1"),
structpb.NewStringValue("package2"),
},
},
},
}},
},
},
},
files: map[string]string{
"path/to/manifest": ``,
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
repoRoot := t.TempDir()
writeFiles(t, repoRoot, tc.files)
err := validate(context.Background(), repoRoot, tc.config)
if err != nil {
t.Fatal(err)
}
})
}
}
func TestValidate_invalid(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
config *proto.Config
err string
files map[string]string
}{
{
name: "no entity to roll",
config: &proto.Config{
Rollers: []*proto.Roller{
{},
},
},
err: "entry 0 is missing an entity to roll",
},
{
name: "invalid interval schedule",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
Schedule: "with blah",
},
},
},
files: filesWithGitmodules,
err: `invalid schedule "with blah"`,
},
{
name: "invalid interval schedule duration",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
Schedule: "with 6f interval",
},
},
},
files: filesWithGitmodules,
err: `invalid duration in schedule "with 6f interval"`,
},
{
name: "invalid cron schedule",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
Schedule: "foo * * * *",
},
},
},
files: filesWithGitmodules,
err: `invalid cron schedule "foo * * * *"`,
},
{
name: "invalid notify email",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
NotifyEmails: []string{
"valid@example.com",
"not-an-email",
},
},
},
},
files: filesWithGitmodules,
err: `invalid email "not-an-email"`,
},
{
name: "submodule with no .gitmodules file",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
},
},
},
err: "no .gitmodules file in repository root",
},
{
name: "submodule with invalid .gitmodules file",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/submodule",
}},
},
},
},
files: map[string]string{
".gitmodules": `invalid`,
},
err: "invalid `git config --list --file .gitmodules` line: \"invalid\"",
},
{
name: "submodule not listed in .gitmodules",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_Submodule{Submodule: &proto.GitSubmodule{
Path: "path/to/INVALID",
}},
},
},
},
files: map[string]string{
".gitmodules": `
[submodule "path/to/submodule"]
url = "https://example.com/asubmodule"
`,
},
err: `no such submodule "path/to/INVALID" listed in .gitmodules`,
},
{
name: "missing CIPD ensure file",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_CipdEnsureFile{CipdEnsureFile: &proto.CIPDEnsureFile{
Path: "path/to/cipd.ensure",
Ref: "foo",
}},
},
},
},
err: "no such file: path/to/cipd.ensure",
},
{
name: "missing jiri project manifest",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriProject{JiriProject: &proto.JiriProject{
Manifest: "path/to/manifest",
Project: "project-name",
}},
},
},
},
err: "no such file: path/to/manifest",
},
{
name: "missing jiri package manifest",
config: &proto.Config{
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriPackages{JiriPackages: &proto.JiriPackages{
PackagesByManifest: map[string]*structpb.ListValue{
"path/to/manifest": {
Values: []*structpb.Value{
structpb.NewStringValue("package1"),
structpb.NewStringValue("package2"),
},
},
},
}},
},
},
},
err: "no such file: path/to/manifest",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
repoRoot := t.TempDir()
writeFiles(t, repoRoot, tc.files)
err := validate(context.Background(), repoRoot, tc.config)
if err == nil {
t.Fatalf("Expected an error: %s", tc.err)
}
if err.Error() != tc.err {
t.Fatalf("Got error %q, expected %q", err, tc.err)
}
})
}
}
func writeFiles(t *testing.T, rootDir string, files map[string]string) {
for path, contents := range files {
abspath := filepath.Join(rootDir, path)
if err := os.MkdirAll(filepath.Dir(abspath), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(abspath, []byte(contents), 0o600); err != nil {
t.Fatal(err)
}
}
}