235 lines
7.9 KiB
Go
235 lines
7.9 KiB
Go
package config
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDefaultConfigValues(t *testing.T) {
|
|
cfg := Default()
|
|
|
|
if got, want := strings.Join(cfg.Modules, ","), DefaultModulesCSV; got != want {
|
|
t.Fatalf("modules mismatch: got %q want %q", got, want)
|
|
}
|
|
if cfg.PrimaryLLM.Model != DefaultPrimaryModel {
|
|
t.Fatalf("unexpected default primary model: %q", cfg.PrimaryLLM.Model)
|
|
}
|
|
if cfg.PrimaryLLM.BaseURL != DefaultPrimaryBaseURL {
|
|
t.Fatalf("unexpected default primary base url: %q", cfg.PrimaryLLM.BaseURL)
|
|
}
|
|
if cfg.PrimaryLLM.TimeoutSeconds != DefaultPrimaryLLMTimeoutSeconds {
|
|
t.Fatalf("unexpected default timeout seconds: %d", cfg.PrimaryLLM.TimeoutSeconds)
|
|
}
|
|
if cfg.PrimaryLLM.MaxRetries != DefaultMaxRetries {
|
|
t.Fatalf("unexpected default max retries: %d", cfg.PrimaryLLM.MaxRetries)
|
|
}
|
|
if cfg.PrimaryLLM.Concurrency != DefaultLLMConcurrency {
|
|
t.Fatalf("unexpected default llm concurrency: %d", cfg.PrimaryLLM.Concurrency)
|
|
}
|
|
if cfg.ValidationLLM.TimeoutSeconds != nil {
|
|
t.Fatalf("expected validation timeout to be unset by default")
|
|
}
|
|
if cfg.ValidationLLM.MaxRetries != nil {
|
|
t.Fatalf("expected validation max retries to be unset by default")
|
|
}
|
|
if cfg.ValidationLLM.Concurrency != nil {
|
|
t.Fatalf("expected validation llm concurrency to be unset by default")
|
|
}
|
|
if cfg.TargetSections != nil {
|
|
t.Fatalf("expected target sections to be unset by default")
|
|
}
|
|
if cfg.WorkDir != DefaultWorkDir {
|
|
t.Fatalf("unexpected default work dir: %q", cfg.WorkDir)
|
|
}
|
|
if cfg.WorkDirRetention != DefaultWorkDirRetention {
|
|
t.Fatalf("unexpected default work dir retention: %q", cfg.WorkDirRetention)
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatalf("default config should validate: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadFromEnvOverridesAndFallback(t *testing.T) {
|
|
env := map[string]string{
|
|
"AUDITA_MODEL": "openai/gpt-4.1-mini",
|
|
"AUDITA_BASE_URL": "https://api.openai.com/v1",
|
|
"AUDITA_LLM_TIMEOUT_SECONDS": "120",
|
|
"AUDITA_MAX_RETRIES": "7",
|
|
"AUDITA_LLM_CONCURRENCY": "6",
|
|
"AUDITA_VALIDATION_LLM_CONCURRENCY": "2",
|
|
"AUDITA_VALIDATION_MAX_PROMPT_TOKENS": "4096",
|
|
"AUDITA_MAX_SECTION_TOKENS": "9000",
|
|
"AUDITA_MIN_SECTION_TOKENS": "3000",
|
|
"AUDITA_TARGET_SECTIONS": "5",
|
|
"AUDITA_GLOSSARY_CONFIDENCE_THRESHOLD": "0.9",
|
|
"AUDITA_GRAMMAR_CONFIDENCE_THRESHOLD": "0.7",
|
|
"AUDITA_HOMOPHONES_CONFIDENCE_THRESHOLD": "0.6",
|
|
"AUDITA_SPOKEN_WORD_CONFIDENCE_THRESHOLD": "0.5",
|
|
"AUDITA_NORMALIZE_MAX_SEGMENT_GAP": "2.5",
|
|
"AUDITA_NORMALIZE_ELLIPSIS_GAP": "2.0",
|
|
"AUDITA_NORMALIZE_MAX_SEGMENT_DURATION": "30.0",
|
|
"AUDITA_NORMALIZE_MAX_SEGMENT_TOKENS": "1024",
|
|
"AUDITA_WORK_DIR": "/var/tmp/audita",
|
|
"AUDITA_WORK_DIR_RETENTION": "always",
|
|
"OPENROUTER_API_KEY": "fallback-key",
|
|
}
|
|
|
|
cfg, err := loadFromLookup(mapLookup(env))
|
|
if err != nil {
|
|
t.Fatalf("loadFromLookup returned error: %v", err)
|
|
}
|
|
|
|
if cfg.PrimaryLLM.APIKey != "fallback-key" {
|
|
t.Fatalf("expected OPENROUTER_API_KEY fallback, got %q", cfg.PrimaryLLM.APIKey)
|
|
}
|
|
if cfg.PrimaryLLM.Model != env["AUDITA_MODEL"] {
|
|
t.Fatalf("unexpected model: %q", cfg.PrimaryLLM.Model)
|
|
}
|
|
if cfg.PrimaryLLM.BaseURL != env["AUDITA_BASE_URL"] {
|
|
t.Fatalf("unexpected base url: %q", cfg.PrimaryLLM.BaseURL)
|
|
}
|
|
if cfg.TargetSections == nil || *cfg.TargetSections != 5 {
|
|
t.Fatalf("unexpected target sections: %#v", cfg.TargetSections)
|
|
}
|
|
if cfg.PrimaryLLM.Concurrency != 6 {
|
|
t.Fatalf("unexpected primary llm concurrency: %d", cfg.PrimaryLLM.Concurrency)
|
|
}
|
|
if cfg.ValidationLLM.Concurrency == nil || *cfg.ValidationLLM.Concurrency != 2 {
|
|
t.Fatalf("unexpected validation llm concurrency: %#v", cfg.ValidationLLM.Concurrency)
|
|
}
|
|
if cfg.WorkDirRetention != WorkDirRetentionAlways {
|
|
t.Fatalf("unexpected work dir retention: %q", cfg.WorkDirRetention)
|
|
}
|
|
}
|
|
|
|
func TestLoadFromEnvUsesAuditaLLMAPIKeyOverFallback(t *testing.T) {
|
|
env := map[string]string{
|
|
"AUDITA_LLM_API_KEY": "primary-key",
|
|
"OPENROUTER_API_KEY": "fallback-key",
|
|
}
|
|
|
|
cfg, err := loadFromLookup(mapLookup(env))
|
|
if err != nil {
|
|
t.Fatalf("loadFromLookup returned error: %v", err)
|
|
}
|
|
|
|
if cfg.PrimaryLLM.APIKey != "primary-key" {
|
|
t.Fatalf("expected AUDITA_LLM_API_KEY to win, got %q", cfg.PrimaryLLM.APIKey)
|
|
}
|
|
}
|
|
|
|
func TestApplyCLIOverridesPrecedence(t *testing.T) {
|
|
cfg := Default()
|
|
cfg.PrimaryLLM.Model = "env-model"
|
|
cfg.WorkDir = "/env/work"
|
|
|
|
model := "cli-model"
|
|
workDir := "/cli/work"
|
|
modules := "grammar"
|
|
overrides := CLIOverrides{
|
|
PrimaryModel: &model,
|
|
WorkDir: &workDir,
|
|
ModulesCSV: &modules,
|
|
}
|
|
|
|
if err := cfg.ApplyCLIOverrides(overrides); err != nil {
|
|
t.Fatalf("ApplyCLIOverrides failed: %v", err)
|
|
}
|
|
|
|
if cfg.PrimaryLLM.Model != "cli-model" {
|
|
t.Fatalf("expected CLI model override, got %q", cfg.PrimaryLLM.Model)
|
|
}
|
|
if cfg.WorkDir != "/cli/work" {
|
|
t.Fatalf("expected CLI work dir override, got %q", cfg.WorkDir)
|
|
}
|
|
if !reflect.DeepEqual(cfg.Modules, []string{"grammar"}) {
|
|
t.Fatalf("unexpected modules: %#v", cfg.Modules)
|
|
}
|
|
}
|
|
|
|
func TestValidationFailures(t *testing.T) {
|
|
cfg := Default()
|
|
cfg.PrimaryLLM.TimeoutSeconds = -1
|
|
cfg.PrimaryLLM.Concurrency = 0
|
|
cfg.ValidationMaxPromptTokens = 0
|
|
cfg.MaxSectionTokens = 100
|
|
cfg.MinSectionTokens = 200
|
|
cfg.Thresholds.Grammar = 1.5
|
|
cfg.WorkDirRetention = WorkDirRetention("sometimes")
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Fatalf("expected validation error")
|
|
}
|
|
|
|
message := err.Error()
|
|
for _, expected := range []string{
|
|
"primary llm timeout seconds",
|
|
"primary llm concurrency",
|
|
"validation max prompt tokens",
|
|
"min section tokens",
|
|
"grammar confidence threshold",
|
|
"work dir retention",
|
|
} {
|
|
if !strings.Contains(message, expected) {
|
|
t.Fatalf("expected error to contain %q, got %q", expected, message)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEffectiveValidationLLMInheritance(t *testing.T) {
|
|
cfg := Default()
|
|
cfg.PrimaryLLM.APIKey = "primary-key"
|
|
cfg.PrimaryLLM.Model = "primary-model"
|
|
cfg.PrimaryLLM.BaseURL = "https://primary.example/v1"
|
|
cfg.PrimaryLLM.TimeoutSeconds = 111
|
|
cfg.PrimaryLLM.MaxRetries = 2
|
|
cfg.PrimaryLLM.Concurrency = 7
|
|
|
|
effective := cfg.EffectiveValidationLLMConfig()
|
|
if effective.APIKey != "primary-key" || effective.Model != "primary-model" || effective.BaseURL != "https://primary.example/v1" || effective.TimeoutSeconds != 111 || effective.MaxRetries != 2 || effective.Concurrency != 7 {
|
|
t.Fatalf("unexpected inherited config: %#v", effective)
|
|
}
|
|
|
|
validationTimeout := 222
|
|
validationRetries := 9
|
|
cfg.ValidationLLM.APIKey = "validation-key"
|
|
cfg.ValidationLLM.Model = "validation-model"
|
|
cfg.ValidationLLM.BaseURL = "https://validation.example/v1"
|
|
cfg.ValidationLLM.TimeoutSeconds = &validationTimeout
|
|
cfg.ValidationLLM.MaxRetries = &validationRetries
|
|
validationConcurrency := 4
|
|
cfg.ValidationLLM.Concurrency = &validationConcurrency
|
|
|
|
effective = cfg.EffectiveValidationLLMConfig()
|
|
if effective.APIKey != "validation-key" || effective.Model != "validation-model" || effective.BaseURL != "https://validation.example/v1" || effective.TimeoutSeconds != 222 || effective.MaxRetries != 9 || effective.Concurrency != 4 {
|
|
t.Fatalf("unexpected overridden validation config: %#v", effective)
|
|
}
|
|
}
|
|
|
|
func TestRedactedConfig(t *testing.T) {
|
|
cfg := Default()
|
|
cfg.PrimaryLLM.APIKey = "secret-primary"
|
|
cfg.ValidationLLM.APIKey = "secret-validation"
|
|
|
|
redacted := cfg.Redacted()
|
|
|
|
if redacted.PrimaryLLM.APIKey != redactedSecret {
|
|
t.Fatalf("expected primary api key to be redacted, got %q", redacted.PrimaryLLM.APIKey)
|
|
}
|
|
if redacted.ValidationLLM.APIKey != redactedSecret {
|
|
t.Fatalf("expected validation api key to be redacted, got %q", redacted.ValidationLLM.APIKey)
|
|
}
|
|
if cfg.PrimaryLLM.APIKey != "secret-primary" {
|
|
t.Fatalf("redaction should not mutate original config")
|
|
}
|
|
}
|
|
|
|
func mapLookup(values map[string]string) func(string) (string, bool) {
|
|
return func(key string) (string, bool) {
|
|
value, ok := values[key]
|
|
return value, ok
|
|
}
|
|
}
|