203 lines
6.1 KiB
Go
203 lines
6.1 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Pingu-Studio/MaidnCLI/internal/config"
|
|
)
|
|
|
|
func TestEnsureTemplateRevisionsLocksFirstCheckout(t *testing.T) {
|
|
workspace, cfg, git := templateRevisionTestConfig(t)
|
|
original := runGit
|
|
runGit = git.run
|
|
t.Cleanup(func() { runGit = original })
|
|
|
|
if err := ensureTemplateRevisions(workspace, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lock, err := readTemplateRevisionLock(filepath.Join(workspace, "maidn-template-revisions.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if lock.CICD.Commit != git.commits["cicd"] || lock.Manifests.Commit != git.commits["manifests"] || lock.Talos.Commit != git.commits["talos"] {
|
|
t.Fatalf("lock did not record checked-out commits: %#v", lock)
|
|
}
|
|
data, err := os.ReadFile(filepath.Join(workspace, "maidn-template-revisions.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(data), "template-password") || strings.Contains(git.commands(), "template-password") {
|
|
t.Fatal("template credentials reached the lock or Git command arguments")
|
|
}
|
|
}
|
|
|
|
func TestEnsureTemplateRevisionsReusesLockedCommitAfterBranchDrift(t *testing.T) {
|
|
workspace, cfg, git := templateRevisionTestConfig(t)
|
|
original := runGit
|
|
runGit = git.run
|
|
t.Cleanup(func() { runGit = original })
|
|
|
|
if err := ensureTemplateRevisions(workspace, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
locked := git.commits["cicd"]
|
|
git.commits["cicd"] = strings.Repeat("d", 40)
|
|
git.resetCalls()
|
|
if err := ensureTemplateRevisions(workspace, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if git.fetched["cicd"] != locked || git.checkedOut["cicd"] != locked {
|
|
t.Fatalf("branch drift changed locked CICD revision: fetched %q, checked out %q", git.fetched["cicd"], git.checkedOut["cicd"])
|
|
}
|
|
if strings.Contains(git.commands(), "fetch origin main") {
|
|
t.Fatal("later run fetched a mutable branch instead of the lock commit")
|
|
}
|
|
}
|
|
|
|
func TestEnsureTemplateRevisionsRejectsChangedRefWithoutGit(t *testing.T) {
|
|
workspace, cfg, git := templateRevisionTestConfig(t)
|
|
original := runGit
|
|
runGit = git.run
|
|
t.Cleanup(func() { runGit = original })
|
|
|
|
if err := ensureTemplateRevisions(workspace, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
git.resetCalls()
|
|
cfg.Templates.CICDRepoRef = "release"
|
|
err := ensureTemplateRevisions(workspace, cfg)
|
|
if err == nil || !strings.Contains(err.Error(), "workspace revision lock") || !strings.Contains(err.Error(), "new empty workspaceDir") {
|
|
t.Fatalf("changed ref error was not safe and actionable: %v", err)
|
|
}
|
|
if git.commands() != "" {
|
|
t.Fatal("changed ref touched Git before rejecting the lock mismatch")
|
|
}
|
|
}
|
|
|
|
func TestEnsureTemplateRevisionsHidesSourceWhenLockedCommitIsUnavailable(t *testing.T) {
|
|
workspace, cfg, git := templateRevisionTestConfig(t)
|
|
original := runGit
|
|
runGit = git.run
|
|
t.Cleanup(func() { runGit = original })
|
|
|
|
if err := ensureTemplateRevisions(workspace, cfg); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
git.failFetch = true
|
|
err := ensureTemplateRevisions(workspace, cfg)
|
|
if err == nil || !strings.Contains(err.Error(), "locked template revision cannot be resolved") || !strings.Contains(err.Error(), "new empty workspaceDir") || strings.Contains(err.Error(), "template-password") {
|
|
t.Fatalf("locked revision failure exposed source details or lacked recovery guidance: %v", err)
|
|
}
|
|
}
|
|
|
|
func templateRevisionTestConfig(t *testing.T) (string, config.Config, *fakeTemplateGit) {
|
|
t.Helper()
|
|
workspace := t.TempDir()
|
|
cloneParent := filepath.Join(workspace, "checkouts")
|
|
if err := os.MkdirAll(cloneParent, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
git := &fakeTemplateGit{
|
|
commits: map[string]string{
|
|
"cicd": strings.Repeat("a", 40),
|
|
"manifests": strings.Repeat("b", 40),
|
|
"talos": strings.Repeat("c", 40),
|
|
},
|
|
origins: map[string]string{},
|
|
fetched: map[string]string{},
|
|
checkedOut: map[string]string{},
|
|
}
|
|
return workspace, config.Config{
|
|
WorkspaceDir: workspace,
|
|
Git: config.GitConfig{CloneParent: cloneParent},
|
|
Talos: config.TalosConfig{RepoDirName: "talos"},
|
|
Templates: config.TemplateConfig{
|
|
CICDRepoURL: "https://reader:template-password@git.example.test/templates/cicd.git",
|
|
CICDRepoRef: "main",
|
|
ManifestsRepoURL: "https://git.example.test/templates/manifests.git",
|
|
ManifestsRepoRef: "main",
|
|
TalosRepoURL: "https://git.example.test/templates/talos.git",
|
|
TalosRepoRef: "main",
|
|
},
|
|
}, git
|
|
}
|
|
|
|
type fakeTemplateGit struct {
|
|
commits map[string]string
|
|
origins map[string]string
|
|
fetched map[string]string
|
|
checkedOut map[string]string
|
|
calls []string
|
|
failFetch bool
|
|
}
|
|
|
|
func (git *fakeTemplateGit) run(dir string, args ...string) ([]byte, error) {
|
|
git.calls = append(git.calls, strings.Join(args, " "))
|
|
if len(args) == 0 {
|
|
return nil, errors.New("missing Git command")
|
|
}
|
|
switch args[0] {
|
|
case "clone":
|
|
dir = args[len(args)-1]
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
git.origins[dir] = args[len(args)-2]
|
|
return nil, nil
|
|
case "remote":
|
|
return []byte(git.origins[dir] + "\n"), nil
|
|
case "status":
|
|
return nil, nil
|
|
case "fetch":
|
|
if git.failFetch {
|
|
return nil, errors.New("not found")
|
|
}
|
|
target := args[len(args)-1]
|
|
name := git.templateName(dir)
|
|
if len(target) == 40 {
|
|
git.fetched[name] = target
|
|
} else {
|
|
git.fetched[name] = git.commits[name]
|
|
}
|
|
return nil, nil
|
|
case "checkout":
|
|
git.checkedOut[git.templateName(dir)] = args[len(args)-1]
|
|
return nil, nil
|
|
case "rev-parse":
|
|
if len(args) == 2 && args[1] == "--is-inside-work-tree" {
|
|
return []byte("true\n"), nil
|
|
}
|
|
target := args[len(args)-1]
|
|
if target == "FETCH_HEAD^{commit}" {
|
|
return []byte(git.fetched[git.templateName(dir)] + "\n"), nil
|
|
}
|
|
if target == "HEAD^{commit}" {
|
|
return []byte(git.checkedOut[git.templateName(dir)] + "\n"), nil
|
|
}
|
|
}
|
|
return nil, errors.New("unexpected Git command")
|
|
}
|
|
|
|
func (git *fakeTemplateGit) templateName(dir string) string {
|
|
switch filepath.Base(dir) {
|
|
case "maidn-cicd-cluster-template":
|
|
return "cicd"
|
|
case "cicd-deployment-manifests-template":
|
|
return "manifests"
|
|
default:
|
|
return "talos"
|
|
}
|
|
}
|
|
|
|
func (git *fakeTemplateGit) resetCalls() {
|
|
git.calls = nil
|
|
}
|
|
|
|
func (git *fakeTemplateGit) commands() string {
|
|
return strings.Join(git.calls, "\n")
|
|
}
|