textmachine/backend/internal/config/book_refusal_test.go

95 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package config
import (
"os"
"path/filepath"
"strings"
"testing"
)
// book_refusal_test.go: the ONE distinction PD-196 rests on — "the text we were handed is unusable"
// against "the operator's config is broken". They have opposite repairs, and upstream the first one
// authorises deleting a user's upload while the second must not.
func bookFixture(t *testing.T, sourceName string) string {
t.Helper()
dir := t.TempDir()
if sourceName != "" {
if err := os.WriteFile(filepath.Join(dir, sourceName), []byte("текст"), 0o644); err != nil {
t.Fatal(err)
}
}
path := filepath.Join(dir, "book.yaml")
body := `
book_id: b
title: Т
source_lang: ja
target_lang: ru
genre: g
audience: a
venuti: 0.5
honorifics: keep
transcription: polivanov
footnotes: minimal
pipeline: pipeline.yaml
models: models.yaml
source_file: source.txt
ceilings: { book_usd: 1.0 }
`
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
t.Fatal(err)
}
return path
}
func TestAMissingSourceIsAnOrdinaryConfigProblem(t *testing.T) {
// It is deliberately NOT a verdict about the user's text. A path that is not there says something
// about the configuration or the host — a template naming a filename the upload route did not use, a
// mount that vanished — and the class that blames the text is the one an automated intake acts on by
// DELETING the upload (PD-196, pipeline/refusal.go). So this stays an ordinary problem, and the
// caller maps it to the config exit code.
path := bookFixture(t, "") // the source_file the config names is not there
err := loadErr(t, path)
if !strings.Contains(err.Error(), "source_file") || !strings.Contains(err.Error(), "not readable") {
t.Fatalf("the operator must be told which setting is wrong: %v", err)
}
}
func TestEveryFaultIsReportedTogether(t *testing.T) {
// The aggregate is the contract: an operator fixing a hand-written config wants every complaint at
// once, not one per run.
path := bookFixture(t, "")
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(strings.Replace(string(body), "book_id: b", "book_id: \"\"", 1)), 0o644); err != nil {
t.Fatal(err)
}
msg := loadErr(t, path).Error()
if !strings.Contains(msg, "book_id") || !strings.Contains(msg, "source_file") {
t.Fatalf("want both faults named, got %v", msg)
}
}
func loadErr(t *testing.T, path string) error {
t.Helper()
_, err := LoadBook(path)
if err == nil {
t.Fatal("this book must not load")
}
return err
}
func TestTheBookRemembersTheDirectoryItWasLoadedFrom(t *testing.T) {
// The run-event journal lives in the BOOK's directory (D39.106 §2) — the directory of the config the
// caller passed, which is not necessarily the project database's.
path := bookFixture(t, "source.txt")
b, err := LoadBook(path)
if err != nil {
t.Fatal(err)
}
if b.Dir != filepath.Dir(path) {
t.Fatalf("Dir = %q, want %q", b.Dir, filepath.Dir(path))
}
}