95 lines
2.9 KiB
Go
95 lines
2.9 KiB
Go
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))
|
||
}
|
||
}
|