Files
felhom-controller/controller/internal/appexport/r1253_busy_test.go
T

49 lines
1.8 KiB
Go

package appexport
import (
"io"
"log"
"os"
"path/filepath"
"strings"
"testing"
)
// busyGuard is the app-stop seam with another operation holding the marker.
type busyGuard struct{ begins []string }
func (g *busyGuard) Begin(opID string, _ []string) error {
g.begins = append(g.begins, opID)
return nil
}
func (g *busyGuard) End() {}
func (g *busyGuard) ReleaseFailed(...string) {}
func (g *busyGuard) BusyWith() string { return "unit-restore:bookstack" }
// R-1253: an export that stops its app is refused BEFORE anything starts while another operation holds the marker;
// an export that does not stop its app writes no marker and still runs. RED-PROOF (2026-10-24): with the BusyWith check
// removed from StartExport, the stop-first export starts (StartExport returns nil) and this fails.
func TestR1253_AStopFirstExportWaitsForTheOtherOperation(t *testing.T) {
stackDir := filepath.Join(t.TempDir(), "immich")
if err := os.MkdirAll(stackDir, 0o755); err != nil {
t.Fatal(err)
}
prov := &rtProvider{stackDir: stackDir, stacksDir: filepath.Dir(stackDir), deployed: true, running: true}
e := NewExporter(prov, log.New(io.Discard, "", 0), "test")
g := &busyGuard{}
e.SetStopGuard(g)
err := e.StartExport(ExportRequest{StackName: "immich", DestDrive: t.TempDir(), StopApp: true})
if err == nil {
t.Fatal("a stop-first export must be refused while a restore holds the app-stop marker")
}
if k, ok := err.(interface{ Key() string }); !ok || k.Key() != "err.appexport.another_operation_running" {
t.Fatalf("the refusal must carry its household sentence key, got %v", err)
}
if !strings.Contains(err.Error(), "visszaállítás") {
t.Fatalf("the Hungarian sentence must name the cause: %q", err.Error())
}
if len(g.begins) != 0 {
t.Fatalf("nothing may be marked or stopped, begins=%v", g.begins)
}
}