package main import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "textmachine/backend/internal/pipeline" "textmachine/backend/internal/store" ) // TestPreflightBackupCreatesRestorePoint pins the F4 boevoy guard: a book whose project DB already exists // gets a fresh, integrity-checked backup before the paid run. func TestPreflightBackupCreatesRestorePoint(t *testing.T) { bookPath := setupCLIProject(t, "http://127.0.0.1:1") // no provider call on this path — only the config loads dbPath := filepath.Join(filepath.Dir(bookPath), "cli-book.db") s, err := store.Open(dbPath) // a prior run would have created this if err != nil { t.Fatal(err) } s.Close() var buf bytes.Buffer if err := preflightBackup(bookPath, &buf); err != nil { t.Fatalf("preflight: %v", err) } backups, _ := filepath.Glob(filepath.Join(filepath.Dir(dbPath), "backups", "*.db")) if len(backups) != 1 { t.Fatalf("the pre-flight must leave exactly one restore point, got %d", len(backups)) } if !strings.Contains(buf.String(), "integrity_check green") { t.Errorf("the pre-flight must report the integrity check, got %q", buf.String()) } } // TestPreflightBackupSkipsFreshBook pins the fresh-book case: no DB yet means no signed bank to lose, so the // guard is a silent no-op that creates no backups directory. func TestPreflightBackupSkipsFreshBook(t *testing.T) { bookPath := setupCLIProject(t, "http://127.0.0.1:1") if err := preflightBackup(bookPath, io.Discard); err != nil { t.Fatalf("a fresh book (no DB) pre-flight must be a no-op, got %v", err) } if _, err := os.Stat(filepath.Join(filepath.Dir(bookPath), "backups")); err == nil { t.Error("a fresh book must create no backups directory") } } // TestFakeTranslatePathCreatesNoBackup is the discriminator proof: the fake-provider harness drives // translate() DIRECTLY (as the golden/rebill tests do), which never enters run()'s boevoy dispatch — so it // must create NO backup. This is what lets the guard avoid a provider-name magic string (generality §0). func TestFakeTranslatePathCreatesNoBackup(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.ReadAll(r.Body) tb, _ := json.Marshal("Тихое утро в библиотеке.") fmt.Fprintf(w, `{"id":"fake","model":"fake-model","choices":[{"message":{"content":%s},"finish_reason":"stop"}],"usage":{"prompt_tokens":10,"completion_tokens":5}}`, tb) })) defer srv.Close() bookPath := setupCLIProject(t, srv.URL) if err := translate(context.Background(), bookPath, false, pipeline.RebillConsent{}, false, 0, 0); err != nil { t.Fatalf("fake translate: %v", err) } if _, err := os.Stat(filepath.Join(filepath.Dir(bookPath), "backups")); err == nil { t.Error("translate() driven directly (the fake path) must NOT create backups — the pre-flight is in run() only") } }