202 lines
6.1 KiB
Go
202 lines
6.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
|
|
)
|
|
|
|
func TestParseMappingsSingleAndRepeated(t *testing.T) {
|
|
got, err := parseMappings([]string{"transcript=./t.md", "glossary=./g.yml"}, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got["transcript"] != "./t.md" || got["glossary"] != "./g.yml" {
|
|
t.Fatalf("unexpected mappings: %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestParseMappingsCommaSeparated(t *testing.T) {
|
|
got, err := parseMappings([]string{"transcript=./t.md,glossary=./g.yml"}, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got["transcript"] != "./t.md" || got["glossary"] != "./g.yml" {
|
|
t.Fatalf("unexpected mappings: %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestParseMappingsVarWithEqualsInValue(t *testing.T) {
|
|
got, err := parseMappings([]string{"session_note=a=b=c"}, false)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got["session_note"] != "a=b=c" {
|
|
t.Fatalf("unexpected variable value: %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestParseMappingsMalformed(t *testing.T) {
|
|
tests := []string{"", "novalue", "=emptyname", "name="}
|
|
for _, tc := range tests {
|
|
_, err := parseMappings([]string{tc}, false)
|
|
if err == nil {
|
|
t.Fatalf("expected error for %q", tc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseRunArgsRequiredFlags(t *testing.T) {
|
|
_, err := parseRunArgs([]string{"--profile-id", "p", "--input", "a=b", "--llm-base-url", "http://x/v1", "--model", "m"})
|
|
if err == nil {
|
|
t.Fatal("expected missing --profile-dir error")
|
|
}
|
|
|
|
_, err = parseRunArgs([]string{"--profile-dir", "./profiles", "--input", "a=b", "--llm-base-url", "http://x/v1", "--model", "m"})
|
|
if err == nil {
|
|
t.Fatal("expected missing --profile-id error")
|
|
}
|
|
|
|
_, err = parseRunArgs([]string{"--profile-dir", "./profiles", "--profile-id", "p", "--llm-base-url", "http://x/v1", "--model", "m"})
|
|
if err == nil {
|
|
t.Fatal("expected missing --input error")
|
|
}
|
|
}
|
|
|
|
func TestParseRunArgsAllowsOmittedModelAndBaseURL(t *testing.T) {
|
|
cfg, err := parseRunArgs([]string{
|
|
"--profile-dir", "./profiles",
|
|
"--profile-id", "p",
|
|
"--input", "a=b",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("expected valid args without model/base url, got %v", err)
|
|
}
|
|
if cfg.llmBaseURL != "" || cfg.model != "" {
|
|
t.Fatalf("expected empty model/baseurl, got model=%q base=%q", cfg.model, cfg.llmBaseURL)
|
|
}
|
|
}
|
|
|
|
func TestParseServeArgsRequiredFlags(t *testing.T) {
|
|
_, err := parseServeArgs([]string{"--llm-base-url", "http://x/v1"})
|
|
if err == nil {
|
|
t.Fatal("expected missing --profile-dir error")
|
|
}
|
|
|
|
_, err = parseServeArgs([]string{"--profile-dir", "./profiles"})
|
|
if err == nil {
|
|
t.Fatal("expected missing --llm-base-url error")
|
|
}
|
|
|
|
cfg, err := parseServeArgs([]string{"--profile-dir", "./profiles", "--llm-base-url", "http://x/v1"})
|
|
if err != nil {
|
|
t.Fatalf("expected valid serve args, got %v", err)
|
|
}
|
|
if cfg.addr != ":8080" {
|
|
t.Fatalf("expected default addr :8080, got %q", cfg.addr)
|
|
}
|
|
if cfg.timeout != 10*time.Minute {
|
|
t.Fatalf("expected default timeout 10m, got %s", cfg.timeout)
|
|
}
|
|
}
|
|
|
|
func TestParseRunArgsTimeout(t *testing.T) {
|
|
cfg, err := parseRunArgs([]string{
|
|
"--profile-dir", "./profiles",
|
|
"--profile-id", "p",
|
|
"--input", "a=b",
|
|
"--llm-base-url", "http://x/v1",
|
|
"--model", "m",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("expected valid run args, got %v", err)
|
|
}
|
|
if cfg.timeout != 10*time.Minute {
|
|
t.Fatalf("expected default timeout 10m, got %s", cfg.timeout)
|
|
}
|
|
|
|
cfg, err = parseRunArgs([]string{
|
|
"--profile-dir", "./profiles",
|
|
"--profile-id", "p",
|
|
"--input", "a=b",
|
|
"--llm-base-url", "http://x/v1",
|
|
"--model", "m",
|
|
"--timeout", "2m30s",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("expected valid run args with timeout override, got %v", err)
|
|
}
|
|
if cfg.timeout != 2*time.Minute+30*time.Second {
|
|
t.Fatalf("expected timeout override 2m30s, got %s", cfg.timeout)
|
|
}
|
|
}
|
|
|
|
func TestDetermineExitCode(t *testing.T) {
|
|
if got := determineExitCode(errors.New("boom"), nil); got != ExitRuntimeError {
|
|
t.Fatalf("expected runtime exit code, got %d", got)
|
|
}
|
|
if got := determineExitCode(nil, &domain.RunResult{Validation: domain.ValidationResult{Status: domain.ValidationFailed}}); got != ExitValidationFailed {
|
|
t.Fatalf("expected validation exit code, got %d", got)
|
|
}
|
|
if got := determineExitCode(nil, &domain.RunResult{Validation: domain.ValidationResult{Status: domain.ValidationPassed}}); got != ExitOK {
|
|
t.Fatalf("expected success exit code for passed validation, got %d", got)
|
|
}
|
|
if got := determineExitCode(nil, &domain.RunResult{Validation: domain.ValidationResult{Status: domain.ValidationSkipped}}); got != ExitOK {
|
|
t.Fatalf("expected success exit code for skipped validation, got %d", got)
|
|
}
|
|
}
|
|
|
|
func TestRunCommandVarsOptional(t *testing.T) {
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
|
|
code := runCommand([]string{
|
|
"--profile-dir", "./profiles",
|
|
"--profile-id", "p",
|
|
"--input", "transcript=./t.md",
|
|
"--llm-base-url", "://bad-url",
|
|
"--model", "m",
|
|
}, &stdout, &stderr)
|
|
|
|
if code != ExitRuntimeError {
|
|
t.Fatalf("expected runtime error exit code, got %d", code)
|
|
}
|
|
if strings.Contains(stderr.String(), "var parse error") {
|
|
t.Fatalf("expected --var to be optional, got stderr=%q", stderr.String())
|
|
}
|
|
if !strings.Contains(stderr.String(), "llm client error") {
|
|
t.Fatalf("expected llm client error after parsing succeeds, got stderr=%q", stderr.String())
|
|
}
|
|
if stdout.Len() != 0 {
|
|
t.Fatalf("expected no stdout output on error, got %q", stdout.String())
|
|
}
|
|
}
|
|
|
|
func TestWriteOutputAndSummaryUseSeparateWriters(t *testing.T) {
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
|
|
if err := writeOutput(&stdout, "", []byte("artifact-body")); err != nil {
|
|
t.Fatalf("unexpected writeOutput error: %v", err)
|
|
}
|
|
printSummary(&stderr, &domain.RunResult{
|
|
ProfileID: "p",
|
|
ProfileVersion: "1",
|
|
ModelName: "m",
|
|
Validation: domain.ValidationResult{Status: domain.ValidationPassed, Mode: domain.ValidationBasic},
|
|
PromptHash: "h",
|
|
InputHashes: map[string]string{"in": "x"},
|
|
})
|
|
|
|
if stdout.String() != "artifact-body" {
|
|
t.Fatalf("expected artifact output on stdout, got %q", stdout.String())
|
|
}
|
|
if !strings.Contains(stderr.String(), "profile=p@1") {
|
|
t.Fatalf("expected summary on stderr, got %q", stderr.String())
|
|
}
|
|
}
|