textmachine/backend/cmd/tmctl/backup_test.go

74 lines
2.9 KiB
Go

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); 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")
}
}