74 lines
2.9 KiB
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, 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")
|
|
}
|
|
}
|