Switch CLI configuration to ConfigKit
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -18,8 +17,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/promptkit"
|
||||
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
|
||||
"gitea.maximumdirect.net/eric/scriptorium/internal/defaults"
|
||||
renderformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
|
||||
)
|
||||
@@ -65,7 +64,7 @@ func TestParseMappingsMalformed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseRunArgsRequiredFlags(t *testing.T) {
|
||||
configPath := writeAppConfigFile(t, "")
|
||||
configPath := writeAppConfigFile(t, "{}\n")
|
||||
|
||||
_, err := parseRunArgs([]string{"--config", configPath, "--profile-dir", "./profiles", "--prompt", "p", "--input", "a=b"})
|
||||
if err == nil {
|
||||
@@ -161,7 +160,7 @@ func TestParseRunArgsRejectsRawLLMAPIKeyFlag(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseServeArgsRequiredFlags(t *testing.T) {
|
||||
configPath := writeAppConfigFile(t, "")
|
||||
configPath := writeAppConfigFile(t, "{}\n")
|
||||
|
||||
_, err := parseServeArgs([]string{"--config", configPath, "--profile-dir", "./profiles"})
|
||||
if err == nil {
|
||||
@@ -364,22 +363,9 @@ func TestParseRunArgsInvalidExplicitConfigReturnsError(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("expected invalid explicit config error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "application config error") {
|
||||
t.Fatalf("expected application config context, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAppSettingsMissingImplicitConfigDoesNotError(t *testing.T) {
|
||||
fs := flag.NewFlagSet("test", flag.ContinueOnError)
|
||||
settings, err := resolveAppSettings(fs, filepath.Join(t.TempDir(), "missing.yml"), appconfig.CLIOverrides{})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if settings.SchemaDir != defaults.SchemaDirDefault {
|
||||
t.Fatalf("expected built-in schema dir, got %q", settings.SchemaDir)
|
||||
}
|
||||
if settings.ServerAddr != defaults.HTTPAddrDefault {
|
||||
t.Fatalf("expected built-in server addr, got %q", settings.ServerAddr)
|
||||
var loadError *configkit.LoadError
|
||||
if !errors.As(err, &loadError) {
|
||||
t.Fatalf("expected structured application config error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +399,85 @@ schema_dir: ./from-config/schemas
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationFlagsPreserveExplicitEmptyValues(t *testing.T) {
|
||||
configPath := writeAppConfigFile(t, `prompt_dir: ./configured/prompts
|
||||
profile_dir: ./configured/profiles
|
||||
schema_dir: ./configured/schemas
|
||||
server:
|
||||
addr: ":9090"
|
||||
artifact_root: ./configured/artifacts
|
||||
`)
|
||||
if _, err := parseRunArgs([]string{"--config", configPath, "--prompt", "p", "--prompt-dir="}); err == nil || !strings.Contains(err.Error(), "prompt directory is required") {
|
||||
t.Fatalf("explicit empty prompt directory restored lower value: %v", err)
|
||||
}
|
||||
render, err := parseRenderArgs([]string{"--config", configPath, "--prompt", "p", "--profile-dir=", "--schema-dir="})
|
||||
if err != nil || render.profileDir != "" || render.schemaDir != "" {
|
||||
t.Fatalf("render did not preserve explicit empty sources: cfg=%+v err=%v", render, err)
|
||||
}
|
||||
serve, err := parseServeArgs([]string{"--config", configPath, "--addr=", "--artifact-root="})
|
||||
if err != nil || serve.addr != "" || serve.artifactRoot != "" {
|
||||
t.Fatalf("serve did not preserve explicit empty settings: cfg=%+v err=%v", serve, err)
|
||||
}
|
||||
|
||||
prompt, err := parsePromptInspectionArgs([]string{"--config", configPath, "--prompt", "p", "--prompt-dir="})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := resolveAppSettingsForPromptInspection(prompt); err == nil || !strings.Contains(err.Error(), "prompt directory is required") {
|
||||
t.Fatalf("prompt inspection restored lower directory: %v", err)
|
||||
}
|
||||
profile, err := parseProfileInspectionArgs([]string{"--config", configPath, "--profile", "p", "--profile-dir="})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
settings, err := resolveAppSettingsForProfileInspection(profile)
|
||||
if err != nil || settings.profileDir != "" {
|
||||
t.Fatalf("profile inspection required a source or restored lower value: settings=%+v err=%v", settings, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationLoadUsesNativeYAMLAndSafeDiagnostics(t *testing.T) {
|
||||
for _, body := range []string{
|
||||
"",
|
||||
"server: null\n",
|
||||
"server:\n addr: null\n",
|
||||
"server:\n unknown: 1\n",
|
||||
"prompt_dir: &alias ./prompts\n",
|
||||
"prompt_dir: ./prompts\n---\nprofile_dir: ./profiles\n",
|
||||
} {
|
||||
path := writeAppConfigFile(t, body)
|
||||
_, err := parseRunArgs([]string{"--config", path, "--prompt-dir", "./prompts", "--prompt", "p"})
|
||||
var loadError *configkit.LoadError
|
||||
if !errors.As(err, &loadError) {
|
||||
t.Fatalf("native YAML failure %q lost structured diagnostics: %v", body, err)
|
||||
}
|
||||
}
|
||||
|
||||
const rejected = "sensitive-synthetic-marker"
|
||||
path := writeAppConfigFile(t, "defaults:\n render_format: "+rejected+"\n")
|
||||
code, stdout, stderr := runCLICommand(t, renderCommand, []string{"--config", path, "--prompt-dir", "./prompts", "--prompt", "p"})
|
||||
if code != ExitRuntimeError || stdout != "" || !strings.Contains(stderr, "validate.error") || strings.Contains(stderr, rejected) {
|
||||
t.Fatalf("unsafe or misclassified load diagnostic: code=%d stdout=%q stderr=%q", code, stdout, stderr)
|
||||
}
|
||||
}
|
||||
|
||||
type failingDiagnosticWriter struct{}
|
||||
|
||||
func (failingDiagnosticWriter) Write([]byte) (int, error) {
|
||||
return 0, errors.New("diagnostic sink unavailable")
|
||||
}
|
||||
|
||||
func TestApplicationDiagnosticWriterFailure(t *testing.T) {
|
||||
diagnostics := []configkit.Diagnostic{{Code: "test.error", Message: "safe diagnostic"}}
|
||||
if err := writeApplicationWarnings(failingDiagnosticWriter{}, diagnostics); err == nil {
|
||||
t.Fatal("expected diagnostic writer failure")
|
||||
}
|
||||
path := writeAppConfigFile(t, "unknown_field: secret-value\n")
|
||||
if code := runCommand([]string{"--config", path, "--prompt", "sample"}, io.Discard, failingDiagnosticWriter{}); code != ExitRuntimeError {
|
||||
t.Fatalf("diagnostic writer failure must retain runtime error exit status, got %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRenderArgsWithExplicitConfigLoadsDirectoriesAndFormat(t *testing.T) {
|
||||
configPath := writeAppConfigFile(t, `
|
||||
prompt_dir: ./from-config/prompts
|
||||
@@ -1533,7 +1598,7 @@ func TestExecutionReportPathValidationAndPreInvocationFailure(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if code != ExitRuntimeError || stdout != "" || !strings.Contains(stderr, "run parse error") || string(data) != "old report" {
|
||||
if code != ExitRuntimeError || stdout != "" || !strings.Contains(stderr, "source.missing") || string(data) != "old report" {
|
||||
t.Fatalf("pre-invocation failure changed report: code=%d stdout=%q stderr=%q report=%q", code, stdout, stderr, data)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user