98 lines
3.7 KiB
Go
98 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"textmachine/platform/internal/runner"
|
|
)
|
|
|
|
// exit-marker runs INSIDE a run's transient unit, as ExecStopPost, where there is no database and no
|
|
// credential to open one with. Reaching for the DSN there would fail every marker in production and
|
|
// lose the only record of how the run ended — so the dispatch has to answer this command before it
|
|
// reads a secret. Pinned here because the dispatch order is invisible at the call site.
|
|
func TestTheExitMarkerCommandNeedsNoDatabase(t *testing.T) {
|
|
t.Setenv("TM_PLATFORM_DSN", "")
|
|
t.Setenv("TM_PLATFORM_DSN_FILE", "")
|
|
t.Setenv("SERVICE_RESULT", "oom-kill")
|
|
t.Setenv("EXIT_CODE", "killed")
|
|
t.Setenv("EXIT_STATUS", "TERM")
|
|
path := filepath.Join(t.TempDir(), "runs", "run_1-1.exit")
|
|
|
|
if err := run([]string{"exit-marker", path, "tm-run-run_1-1"}, os.Stdout); err != nil {
|
|
t.Fatalf("exit-marker with no DSN: %v", err)
|
|
}
|
|
m, err := runner.ReadMarker(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m.Result != "oom-kill" || m.Code != "killed" || m.Status != "TERM" || m.Unit != "tm-run-run_1-1" {
|
|
t.Errorf("marker: %+v", m)
|
|
}
|
|
// Every other command still needs one, or it would silently do nothing.
|
|
if err := run([]string{"balance", "--user", "u1"}, os.Stdout); err == nil {
|
|
t.Error("a database command ran without a DSN")
|
|
}
|
|
}
|
|
|
|
func TestTheExitMarkerCommandNeedsBothArguments(t *testing.T) {
|
|
t.Setenv("TM_PLATFORM_DSN", "")
|
|
for _, args := range [][]string{{"exit-marker"}, {"exit-marker", "only-a-path"}} {
|
|
if err := run(append([]string{}, args...), os.Stdout); err == nil {
|
|
t.Errorf("%v was accepted", args)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The development intake is what puts a book in the read model until the contract's upload exists.
|
|
// It refuses a directory that is not an engine project, because the alternative is a run that dies
|
|
// at argument parsing inside a transient unit, where the only trace is "exit-code 1".
|
|
func TestAddingABookRefusesADirectoryTheEngineCannotUse(t *testing.T) {
|
|
dir := t.TempDir()
|
|
args := []string{"--user", "u1", "--workdir", dir, "--title", "b",
|
|
"--source-lang", "zh", "--target-lang", "ru", "--chapters", "10", "--characters", "23000000"}
|
|
_, err := parseBookIntake(args)
|
|
if err == nil || !strings.Contains(err.Error(), "engine project directory") {
|
|
t.Fatalf("a directory without book.yaml gave %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, runner.ConfigFile), []byte("book_id: x\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := parseBookIntake(args)
|
|
if err != nil {
|
|
t.Fatalf("a valid project directory was refused: %v", err)
|
|
}
|
|
if got.ChapterCount != 10 || got.CharacterCount != 23_000_000 || got.OwnerID != "u1" {
|
|
t.Errorf("intake: %+v", got)
|
|
}
|
|
if !filepath.IsAbs(got.Workdir) {
|
|
t.Errorf("the workdir was not made absolute: %q — the unit runs with its own working directory", got.Workdir)
|
|
}
|
|
}
|
|
|
|
// Chapters are what bound the run-ceiling scale, so a book registered without them would offer a
|
|
// scale of zero and no run could ever start on it.
|
|
func TestAddingABookRequiresTheFactsTheScaleIsBuiltFrom(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, runner.ConfigFile), []byte("book_id: x\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
full := []string{"--user", "u1", "--workdir", dir, "--title", "b",
|
|
"--source-lang", "zh", "--target-lang", "ru", "--chapters", "10"}
|
|
drop := func(flag string) []string {
|
|
out := []string{}
|
|
for i := 0; i < len(full); i += 2 {
|
|
if full[i] != flag {
|
|
out = append(out, full[i], full[i+1])
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
for _, flag := range []string{"--user", "--workdir", "--title", "--source-lang", "--target-lang", "--chapters"} {
|
|
if _, err := parseBookIntake(drop(flag)); err == nil {
|
|
t.Errorf("a book without %s was accepted", flag)
|
|
}
|
|
}
|
|
}
|