maidn-cli/internal/bootstrap/template_revisions_test.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")
}