textmachine/platform/internal/runner/backup_live_test.go

70 lines
3 KiB
Go

package runner
import (
"os"
"os/exec"
"path/filepath"
"testing"
)
// The one claim this platform makes about `tmctl backup` that no unit test can make: that the LINE
// this side parses is the line THIS ENGINE prints, and that what it names is a file.
//
// ⚠ It is the whole reason the parse is allowed to exist. Reading a path out of another zone's prose
// is a coupling with no version on it (17-seam-inbound-law п.3), and the only honest way to hold it
// is against the real binary — a fixture string would prove that this file agrees with itself.
// When the engine grows a `--out` or a `--json` on this verb, this test is what says the prose form
// may go.
//
// Gated like every live-engine test: a bare clone stays green and says why.
func TestTheRealEngineNamesItsRestorePointInTheLineThisPlatformParses(t *testing.T) {
bin := os.Getenv("TM_PLATFORM_TEST_ENGINE_BIN")
tpl := os.Getenv("TM_PLATFORM_TEST_BOOK_TEMPLATE")
if bin == "" || tpl == "" {
t.Skip("TM_PLATFORM_TEST_ENGINE_BIN and TM_PLATFORM_TEST_BOOK_TEMPLATE not set: " +
"the backup verb's output is not checked against a real engine")
}
dir := t.TempDir()
writeProbeBook(t, tpl, dir, "bk_BACKUPPROBE")
// A book with no project database is the state a fresh directory is in, and the engine refuses
// there rather than failing — pinned first, because that refusal is what the backup pass reads as
// "nothing to copy yet" and it must not be an error.
empty, err := New(nil).Backup(t.Context(), bin, dir)
if err != nil {
t.Fatalf("a book with no database raised an error instead of answering: %v", err)
}
if !empty.Exited || empty.ExitCode == 0 || empty.Path != "" {
t.Fatalf("a book with no database was reported as backed up: %+v", empty)
}
// `manifest` is the $0 verb that CREATES and migrates the project database (backend/cmd/tmctl,
// manifestCmd: "it opens the store exactly as `status` does … EXCEPT on the first touch").
cmd := exec.CommandContext(t.Context(), bin, "manifest", "--config", filepath.Join(dir, ConfigFile))
cmd.Dir = dir
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("could not give the probe book a project database: %v\n%s", err, out)
}
out, err := New(nil).Backup(t.Context(), bin, dir)
if err != nil {
t.Fatalf("the live backup verb was not readable: %v (stderr %q)", err, out.Stderr)
}
if !out.Exited || out.ExitCode != 0 {
t.Fatalf("the live backup verb refused: %+v (stderr %q)", out, out.Stderr)
}
if out.Path == "" {
t.Fatal("the live backup verb exited 0 and this platform read no path out of it")
}
fi, err := os.Stat(out.Path)
if err != nil {
t.Fatalf("the path this platform parsed is not a file: %v", err)
}
if fi.Size() == 0 {
t.Errorf("the restore point the engine named is empty: %s", out.Path)
}
// Inside the book, which is the guard the parse enforces and the copier relies on.
if rel, err := filepath.Rel(dir, out.Path); err != nil || rel == ".." {
t.Errorf("the engine's restore point is not inside the book's directory: %s", out.Path)
}
}