129 lines
5.2 KiB
Go
129 lines
5.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
|
|
)
|
|
|
|
func TestConfigCommandsInspectApplicationWithoutEngine(t *testing.T) {
|
|
path := writeAppConfigFile(t, "{}\n")
|
|
for _, mode := range []string{"validate", "show", "sources"} {
|
|
code, stdout, stderr := runCLICommand(t, configCommand, []string{mode, "--config", path})
|
|
if code != ExitOK || stderr != "" {
|
|
t.Fatalf("%s failed: code=%d stdout=%q stderr=%q", mode, code, stdout, stderr)
|
|
}
|
|
switch mode {
|
|
case "validate":
|
|
if stdout != "" {
|
|
t.Fatalf("validation wrote stdout: %q", stdout)
|
|
}
|
|
case "show":
|
|
if !strings.Contains(stdout, "render_format: text") || !strings.Contains(stdout, "max_request_bytes: 16777216") {
|
|
t.Fatalf("effective defaults missing: %q", stdout)
|
|
}
|
|
case "sources":
|
|
var sources []configurationSource
|
|
if err := json.Unmarshal([]byte(stdout), &sources); err != nil || len(sources) != 1 || sources[0].Layer != "base" {
|
|
t.Fatalf("unexpected source records: %q (%v)", stdout, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigCommandsExposeComposedSourcesAndResolvedValues(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(dir, "overlays"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
base := filepath.Join(dir, "config.yml")
|
|
for path, body := range map[string]string{
|
|
base: "configkit:\n imports:\n - path: fragment.yml\nprompt_dir: ' ./before/../prompts '\nprofile_dir: ./profiles\n",
|
|
filepath.Join(dir, "fragment.yml"): "server:\n addr: :8081\n",
|
|
filepath.Join(dir, "overlays", "local.yml"): "server:\n addr: :9090\n",
|
|
} {
|
|
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
args := []string{"--config", base, "--config-overlay", "local", "--profile-dir=", "--addr", ":7070"}
|
|
code, stdout, stderr := runCLICommand(t, configCommand, append([]string{"show"}, args...))
|
|
if code != ExitOK || stderr != "" || !strings.Contains(stdout, "prompt_dir: prompts") || !strings.Contains(stdout, "profile_dir: ''") || !strings.Contains(stdout, "addr: :7070") {
|
|
t.Fatalf("resolved effective output changed: code=%d out=%q err=%q", code, stdout, stderr)
|
|
}
|
|
code, stdout, stderr = runCLICommand(t, configCommand, append([]string{"sources"}, args...))
|
|
if code != ExitOK || stderr != "" || !strings.HasSuffix(stdout, "\n") {
|
|
t.Fatalf("source command failed: code=%d out=%q err=%q", code, stdout, stderr)
|
|
}
|
|
var sources []configurationSource
|
|
if err := json.Unmarshal([]byte(stdout), &sources); err != nil || len(sources) != 5 {
|
|
t.Fatalf("unexpected composed source records: %+v (%v)", sources, err)
|
|
}
|
|
for i, layer := range []string{"base", "base", "overlay", "cli", "cli"} {
|
|
if string(sources[i].Layer) != layer {
|
|
t.Fatalf("source %d layer = %q, want %q", i, sources[i].Layer, layer)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigInspectionKeepsReferencesAndOrdinaryParameters(t *testing.T) {
|
|
const name = "SCRIPTORIUM_CONFIG_TEST_API_KEY"
|
|
const secret = "synthetic-secret-value-should-not-appear"
|
|
path := writeAppConfigFile(t, `backends:
|
|
synthetic:
|
|
endpoint: http://127.0.0.1:1/v1
|
|
api_key_env: SCRIPTORIUM_CONFIG_TEST_API_KEY
|
|
extra_params:
|
|
visible_parameter: synthetic-ordinary-value
|
|
`)
|
|
t.Setenv(name, secret)
|
|
for _, present := range []bool{true, false} {
|
|
if !present {
|
|
if err := os.Unsetenv(name); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
code, stdout, stderr := runCLICommand(t, configCommand, []string{"show", "--config", path})
|
|
if code != ExitOK || stderr != "" || !strings.Contains(stdout, name) || !strings.Contains(stdout, "synthetic-ordinary-value") || strings.Contains(stdout, secret) {
|
|
t.Fatalf("credential boundary changed with present=%t: code=%d out=%q err=%q", present, code, stdout, stderr)
|
|
}
|
|
}
|
|
result, err := appconfig.Load(appconfig.LoadOptions{Path: path, Explicit: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
settings := appconfig.Settings(result.Config)
|
|
if len(settings.Backends) != 1 || settings.Backends[0].ExtraParams["visible_parameter"] != "synthetic-ordinary-value" {
|
|
t.Fatalf("runtime backend parameters changed: %+v", settings.Backends)
|
|
}
|
|
}
|
|
|
|
type errorOutputWriter struct{}
|
|
|
|
func (errorOutputWriter) Write([]byte) (int, error) { return 0, errors.New("output unavailable") }
|
|
|
|
func TestConfigCommandFailuresDoNotProduceSuccessfulOutput(t *testing.T) {
|
|
path := writeAppConfigFile(t, "server:\n max_request_bytes: -1\n")
|
|
for _, mode := range []string{"validate", "show", "sources"} {
|
|
code, stdout, stderr := runCLICommand(t, configCommand, []string{mode, "--config", path})
|
|
if code != ExitRuntimeError || stdout != "" || !strings.Contains(stderr, "validate.error") {
|
|
t.Fatalf("%s misclassified invalid config: code=%d out=%q err=%q", mode, code, stdout, stderr)
|
|
}
|
|
}
|
|
path = writeAppConfigFile(t, "{}\n")
|
|
var stderr bytes.Buffer
|
|
if code := configCommand([]string{"show", "--config", path}, errorOutputWriter{}, &stderr); code != ExitRuntimeError || !strings.Contains(stderr.String(), "output error") {
|
|
t.Fatalf("writer error not reported: code=%d stderr=%q", code, stderr.String())
|
|
}
|
|
if code := configCommand([]string{"sources", "--config", path}, errorOutputWriter{}, io.Discard); code != ExitRuntimeError {
|
|
t.Fatalf("source writer error returned %d", code)
|
|
}
|
|
}
|