251 lines
9.8 KiB
Go
251 lines
9.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
ck "gitea.maximumdirect.net/eric/ConfigKit"
|
|
"gitea.maximumdirect.net/eric/audita/internal/core/config"
|
|
)
|
|
|
|
func TestSharedConfigInput(t *testing.T) {
|
|
for _, command := range []string{"process", "validate", "print-effective"} {
|
|
t.Run(command, func(t *testing.T) {
|
|
args := []string{"--config", "main.yml", "--overlay", "testing", "--proposal-profile", "economy", "--modules", "", "--target-sections", "0"}
|
|
if command == "process" {
|
|
args = append([]string{"transcript.json", "--glossary", "glossary.yml"}, args...)
|
|
}
|
|
input, help, err := parseConfigInput(command, args, io.Discard)
|
|
if err != nil || help {
|
|
t.Fatalf("parse: %+v %v %v", input, help, err)
|
|
}
|
|
if !input.ConfigPathSet || input.ConfigPath != "main.yml" || input.Overlay != "testing" || len(input.Overrides) != 3 {
|
|
t.Fatalf("selection: %+v", input)
|
|
}
|
|
for _, override := range input.Overrides {
|
|
switch override.Path.String() {
|
|
case ck.Keys("pipeline", "modules").String():
|
|
if values, ok := override.Value.([]any); !ok || len(values) != 0 {
|
|
t.Fatalf("empty modules: %#v", override.Value)
|
|
}
|
|
case ck.Keys("chunking", "target_sections").String():
|
|
if override.Value != 0 {
|
|
t.Fatalf("zero target: %#v", override.Value)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigInputRejectsRemovedAndProcessOnlyFlags(t *testing.T) {
|
|
for _, command := range []string{"process", "validate", "print-effective"} {
|
|
for _, flag := range []string{"--model", "--llm-api-key", "--max-retries", "--llm-concurrency", "--report-json", "--work-dir-retention"} {
|
|
args := []string{flag, "value"}
|
|
if command == "process" {
|
|
args = append([]string{"transcript.json", "--glossary", "glossary.yml"}, args...)
|
|
}
|
|
if _, _, err := parseConfigInput(command, args, io.Discard); err == nil {
|
|
t.Fatalf("%s accepted %s", command, flag)
|
|
}
|
|
}
|
|
}
|
|
for _, command := range []string{"validate", "print-effective"} {
|
|
if _, _, err := parseConfigInput(command, []string{"--glossary", "glossary.yml"}, io.Discard); err == nil {
|
|
t.Fatalf("%s accepted process path", command)
|
|
}
|
|
if _, _, err := parseConfigInput(command, []string{"transcript.json"}, io.Discard); err == nil {
|
|
t.Fatalf("%s accepted positional", command)
|
|
}
|
|
}
|
|
for _, command := range []string{"process", "validate", "print-effective"} {
|
|
_, help, err := parseConfigInput(command, []string{"--help"}, io.Discard)
|
|
if err != nil || !help {
|
|
t.Fatalf("%s help: %v %v", command, help, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigSelection(t *testing.T) {
|
|
dir := t.TempDir()
|
|
first, second := filepath.Join(dir, "first.yml"), filepath.Join(dir, "second.yml")
|
|
if err := os.WriteFile(second, []byte("version: 2\n"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path, source, err := selectConfigPath("", false, os.Stat, []string{first, second})
|
|
if err != nil || path != second || source != "default" {
|
|
t.Fatalf("discovery: %q %q %v", path, source, err)
|
|
}
|
|
for _, tc := range []struct {
|
|
explicit string
|
|
set bool
|
|
candidates []string
|
|
}{
|
|
{"", true, []string{second}}, {first, true, []string{second}}, {"", false, []string{first}},
|
|
} {
|
|
if _, _, err := selectConfigPath(tc.explicit, tc.set, os.Stat, tc.candidates); err == nil {
|
|
t.Fatalf("accepted unavailable selection: %+v", tc)
|
|
}
|
|
}
|
|
statDenied := func(string) (os.FileInfo, error) { return nil, os.ErrPermission }
|
|
if _, _, err := selectConfigPath("", false, statDenied, []string{first}); !errors.Is(err, os.ErrPermission) {
|
|
t.Fatalf("inaccessible candidate: %v", err)
|
|
}
|
|
path, source, err = selectConfigPath(second, true, os.Stat, []string{first})
|
|
if err != nil || path != second || source != "flag" {
|
|
t.Fatalf("explicit path: %q %q %v", path, source, err)
|
|
}
|
|
}
|
|
|
|
func TestLegacyEnvironmentDoesNotSelectConfig(t *testing.T) {
|
|
t.Setenv("AUDITA_CONFIG", filepath.Join(t.TempDir(), "legacy.yml"))
|
|
_, _, err := selectConfigPath("", false, os.Stat, []string{filepath.Join(t.TempDir(), "missing.yml")})
|
|
if err == nil || !strings.Contains(err.Error(), "no configuration file") {
|
|
t.Fatalf("legacy environment changed discovery: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEffectiveViewParsing(t *testing.T) {
|
|
for _, view := range []string{"settings", "profiles", "explain"} {
|
|
input, _, err := parseConfigInput("print-effective", []string{"--view", view}, io.Discard)
|
|
if err != nil || input.View != view {
|
|
t.Fatalf("%q: %+v %v", view, input, err)
|
|
}
|
|
}
|
|
if _, _, err := parseConfigInput("print-effective", []string{"--view", "raw"}, io.Discard); err == nil {
|
|
t.Fatal("unknown view accepted")
|
|
}
|
|
if _, _, err := parseConfigInput("validate", []string{"--view", "profiles"}, io.Discard); err == nil {
|
|
t.Fatal("validate accepted inspection-only flag")
|
|
}
|
|
}
|
|
|
|
func TestSharedLoadingUsesSelectedFileOverlayAndVisitedOverrides(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.Mkdir(filepath.Join(dir, "overlays"), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
main := filepath.Join(dir, "config.yml")
|
|
base := "version: 2\nbackends:\n local:\n endpoint: https://local.invalid/v1\nllm:\n proposal_profile: base\nchunking:\n max_section_tokens: 900\n"
|
|
if err := os.WriteFile(main, []byte(base), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "overlays", "testing.yml"), []byte("llm:\n proposal_profile: testing\n"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, command := range []string{"process", "validate", "print-effective"} {
|
|
args := []string{"--config", main, "--overlay", "testing", "--min-section-tokens", "500"}
|
|
if command == "process" {
|
|
args = append([]string{"input.json", "--glossary", "glossary.yml"}, args...)
|
|
}
|
|
input, _, err := parseConfigInput(command, args, io.Discard)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
selected, err := loadConfigInput(input, command)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c := selected.Result.Config
|
|
if c.LLM.ProposalProfile != "testing" || c.LLM.ValidationProfile != "testing" || c.Chunking.MinSectionTokens != 500 || c.Chunking.MaxSectionTokens != 900 {
|
|
t.Fatalf("%s resolved %+v", command, c)
|
|
}
|
|
}
|
|
input, _, err := parseConfigInput("validate", []string{"--overlay", "testing"}, io.Discard)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := loadConfigInput(input, "validate"); err == nil {
|
|
t.Fatal("validate accepted discovery")
|
|
}
|
|
input, _, err = parseConfigInput("validate", []string{"--config", main}, io.Discard)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := loadConfigInput(input, "validate"); err == nil {
|
|
t.Fatal("unmodified invalid lower-precedence section relation accepted")
|
|
}
|
|
}
|
|
|
|
func TestReportingAndDebugOverridesRemainIndependent(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.Mkdir(filepath.Join(dir, "overlays"), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
main := filepath.Join(dir, "config.yml")
|
|
base := "version: 2\nbackends:\n local:\n endpoint: https://local.invalid/v1\nllm:\n proposal_profile: base\n"
|
|
if err := os.WriteFile(main, []byte(base), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "overlays", "testing.yml"), []byte("reporting:\n enabled: true\n stdout_dir: reports\ndiagnostics:\n debug: true\n"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defaultInput, _, err := parseConfigInput("validate", []string{"--config", main}, io.Discard)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defaults, err := loadConfigInput(defaultInput, "validate")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !defaults.Result.Config.Reporting.Enabled || defaults.Result.Config.Reporting.StdoutDir != "." || defaults.Result.Config.Diagnostics.Debug {
|
|
t.Fatalf("unexpected defaults: %+v", defaults.Result.Config)
|
|
}
|
|
for _, command := range []string{"process", "validate", "print-effective"} {
|
|
args := []string{"--config", main, "--overlay", "testing", "--reporting=false", "--debug=false"}
|
|
if command == "process" {
|
|
args = append([]string{"input.json", "--glossary", "glossary.yml"}, args...)
|
|
}
|
|
input, _, err := parseConfigInput(command, args, io.Discard)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(input.Overrides) != 2 || input.Overrides[0].Value != false || input.Overrides[1].Value != false {
|
|
t.Fatalf("boolean overrides must remain typed: %+v", input.Overrides)
|
|
}
|
|
selected, err := loadConfigInput(input, command)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.Result.Config.Reporting.Enabled || selected.Result.Config.Diagnostics.Debug || selected.Result.Config.Reporting.StdoutDir != "reports" {
|
|
t.Fatalf("%s did not honor explicit false: %+v", command, selected.Result.Config)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReportDirectorySelectionOnlyUsesActiveDestination(t *testing.T) {
|
|
cfg := config.Config{Reporting: config.Reporting{Enabled: true, StdoutDir: "reports"}}
|
|
if got, err := selectedReportDir(cfg, ""); err != nil || got != "reports" {
|
|
t.Fatalf("stdout destination: %q %v", got, err)
|
|
}
|
|
cfg.Reporting.StdoutDir = ""
|
|
if got, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json")); err != nil || got != "out" {
|
|
t.Fatalf("file destination ignored stdout setting: %q %v", got, err)
|
|
}
|
|
if _, err := selectedReportDir(cfg, ""); err == nil {
|
|
t.Fatal("enabled stdout reporting accepted blank destination")
|
|
}
|
|
cfg.Reporting.Enabled = false
|
|
if got, err := selectedReportDir(cfg, ""); err != nil || got != "" {
|
|
t.Fatalf("disabled reporting probed destination: %q %v", got, err)
|
|
}
|
|
}
|
|
|
|
func TestBareBooleanFlagsAndReportDirectoryOverride(t *testing.T) {
|
|
input, _, err := parseConfigInput("process", []string{"input.json", "--glossary", "glossary.yml", "--reporting", "--debug", "--report-dir", "reports"}, io.Discard)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
values := map[string]any{}
|
|
for _, override := range input.Overrides {
|
|
values[override.Path.String()] = override.Value
|
|
}
|
|
if values[ck.Keys("reporting", "enabled").String()] != true || values[ck.Keys("diagnostics", "debug").String()] != true || values[ck.Keys("reporting", "stdout_dir").String()] != "reports" {
|
|
t.Fatalf("incorrect visited overrides: %+v", values)
|
|
}
|
|
}
|