Files
felhom-controller/controller/internal/sync/r607_r615_test.go
T
admin 0b349e2415 R-607 + R-615: the catalog sync follows a changed repo_url and rescans when the catalog moved
R-615: a changed git.repo_url was inert (the clone's stored origin was fetched for ever). The sync
now compares the configured URL with the clone's origin: another repository -> drop the cache and
clone it; the same repository with new credentials -> set-url.
R-607: a catalog move that changed no stack directory (a deployed, pinned app keeps its stored
definition) skipped the rescan, so CatalogImages and the update badge stayed stale, and the sync
said 'nincs változás'. The sync now notes the cache commit before and after, rescans when it moved,
says so, and returns catalog_moved.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-05 21:55:37 +02:00

133 lines
4.1 KiB
Go

package sync
import (
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
)
// Local git repositories only (file://) — no network.
func gitT(t *testing.T, dir string, args ...string) string {
t.Helper()
cmd := exec.Command("git", append([]string{"-c", "user.email=t@example.invalid", "-c", "user.name=t", "-c", "init.defaultBranch=main", "-c", "commit.gpgsign=false"}, args...)...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %v: %v\n%s", args, err, out)
}
return strings.TrimSpace(string(out))
}
// newCatalogRepo makes a work repo with one template and a marker file, on branch main.
func newCatalogRepo(t *testing.T, marker string) string {
t.Helper()
dir := t.TempDir()
gitT(t, dir, "init", "-q", "-b", "main")
app := filepath.Join(dir, "templates", "demo")
if err := os.MkdirAll(app, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(app, "docker-compose.yml"), []byte("services:\n web:\n image: busybox:1\n"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "MARKER"), []byte(marker), 0o644); err != nil {
t.Fatal(err)
}
gitT(t, dir, "add", "-A")
gitT(t, dir, "commit", "-q", "-m", "init")
return dir
}
func newLocalSyncer(t *testing.T, repo string, rescans *int) *Syncer {
t.Helper()
root := t.TempDir()
cfg := &config.Config{}
cfg.Git.RepoURL = "file://" + repo
cfg.Git.Branch = "main"
cfg.Git.SyncInterval = "15m"
cfg.Paths.DataDir = filepath.Join(root, "data")
cfg.Paths.StacksDir = filepath.Join(root, "stacks")
if err := os.MkdirAll(cfg.Paths.StacksDir, 0o755); err != nil {
t.Fatal(err)
}
return New(cfg, log.New(os.Stderr, "", 0), func() error { *rescans++; return nil }, nil)
}
func syncNow(t *testing.T, s *Syncer) SyncResult {
t.Helper()
s.mu.Lock()
s.lastSync = time.Now().Add(-time.Hour) // past the debounce
s.mu.Unlock()
res := s.TriggerSync()
if !res.OK {
t.Fatalf("sync failed: %s", res.Message)
}
return res
}
// R-615: changing git.repo_url under an existing cache makes the next sync read the NEW repository.
func TestR615_ChangedRepoURLIsFollowed(t *testing.T) {
a := newCatalogRepo(t, "catalog-A")
b := newCatalogRepo(t, "catalog-B")
n := 0
s := newLocalSyncer(t, a, &n)
syncNow(t, s)
readMarker := func() string {
bs, err := os.ReadFile(filepath.Join(s.cacheDir, "MARKER"))
if err != nil {
t.Fatal(err)
}
return string(bs)
}
if readMarker() != "catalog-A" {
t.Fatalf("setup: cache should hold catalog A")
}
s.cfg.Git.RepoURL = "file://" + b // the operator repoints the box
syncNow(t, s)
if got := readMarker(); got != "catalog-B" {
t.Fatalf("R-615: after changing repo_url the cache still reads %q — the old repository is still being followed", got)
}
if origin := gitT(t, s.cacheDir, "config", "--get", "remote.origin.url"); origin != "file://"+b {
t.Errorf("the cache's origin must be the configured repository, got %q", origin)
}
}
// R-607: a catalog move that changes no stack directory still rescans (CatalogImages is read from the
// cache) and is not reported as „nincs változás".
func TestR607_CatalogMoveWithoutStackChangeRescansAndSaysSo(t *testing.T) {
a := newCatalogRepo(t, "v1")
n := 0
s := newLocalSyncer(t, a, &n)
syncNow(t, s)
before := n
// No change at all: honest „nincs változás", no rescan.
res := syncNow(t, s)
if res.CatalogMoved || !strings.Contains(res.Message, "nincs v") || n != before {
t.Fatalf("an unchanged catalog: moved=%v msg=%q rescans %d->%d", res.CatalogMoved, res.Message, before, n)
}
// The catalog moves in a file no stack dir copies.
if err := os.WriteFile(filepath.Join(a, "MARKER"), []byte("v2"), 0o644); err != nil {
t.Fatal(err)
}
gitT(t, a, "commit", "-q", "-am", "move")
res = syncNow(t, s)
if !res.CatalogMoved {
t.Fatal("R-607: the sync did not notice the catalog moved")
}
if n != before+1 {
t.Fatalf("R-607: a catalog move must rescan (CatalogImages is read from the cache); rescans %d->%d", before, n)
}
if strings.Contains(res.Message, "nincs v") {
t.Fatalf("R-607: a moved catalog was reported as unchanged: %q", res.Message)
}
}