146 lines
6.1 KiB
Go
146 lines
6.1 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
ck "gitea.maximumdirect.net/eric/ConfigKit"
|
|
)
|
|
|
|
const base = "version: 2\nbackends:\n local:\n endpoint: https://local.invalid/v1\nllm:\n proposal_profile: proposal\n"
|
|
|
|
func loadText(text, overlay string, overrides ...ck.Override) (*ck.Result[Config], error) {
|
|
return Load(ck.Bytes("config.yml", []byte(text)), overlay, overrides...)
|
|
}
|
|
|
|
func TestDefaultsAndDomainSettings(t *testing.T) {
|
|
loaded, err := loadText(base, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c := loaded.Config
|
|
if c.LLM.ValidationProfile != "proposal" || c.Backends["local"].Concurrency != 1 || c.Backends["local"].QueueCapacity != 1024 {
|
|
t.Fatalf("derived settings: %+v %+v", c.LLM, c.Backends)
|
|
}
|
|
if !reflect.DeepEqual(c.Pipeline.Modules, []string{"glossary", "homophones", "glossary", "spoken_word", "grammar"}) {
|
|
t.Fatalf("module order: %v", c.Pipeline.Modules)
|
|
}
|
|
if explanation, ok := loaded.Explain(ck.Keys("llm", "validation_profile")); !ok || len(explanation.History) == 0 {
|
|
t.Fatal("validation inheritance has no recorded derivation")
|
|
}
|
|
if _, err := loadText(base+"pipeline:\n modules: []\n", ""); err != nil {
|
|
t.Fatalf("empty pipeline: %v", err)
|
|
}
|
|
if _, err := loadText(base+"pipeline:\n modules: [grammar, glossary, grammar]\n", ""); err != nil {
|
|
t.Fatalf("repeated modules: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStrictInputAndConstraints(t *testing.T) {
|
|
for _, tc := range []struct{ name, text string }{
|
|
{"old version", strings.Replace(base, "version: 2", "version: 1", 1)},
|
|
{"missing version", strings.Replace(base, "version: 2\n", "", 1)},
|
|
{"null version", strings.Replace(base, "version: 2", "version: null", 1)},
|
|
{"quoted version", strings.Replace(base, "version: 2", "version: '2'", 1)},
|
|
{"unknown field", base + "mystery: true\n"},
|
|
{"empty validation profile", base + " validation_profile: ''\n"},
|
|
{"null validation profile", base + " validation_profile: null\n"},
|
|
{"null proposal profile", strings.Replace(base, "proposal_profile: proposal", "proposal_profile: null", 1)},
|
|
{"zero backend concurrency", base + " concurrency: 0\n"},
|
|
{"invalid target", base + "chunking:\n target_sections: 0\n"},
|
|
{"invalid section relation", base + "chunking:\n min_section_tokens: 10000\n"},
|
|
{"string normalization", base + "normalization:\n max_segment_gap: 4s\n"},
|
|
{"invalid threshold", base + "thresholds:\n glossary: 1.1\n"},
|
|
{"removed retention", base + "diagnostics:\n retention: always\n"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if _, err := loadText(tc.text, ""); err == nil {
|
|
t.Fatal("invalid config accepted")
|
|
}
|
|
})
|
|
}
|
|
_, err := loadText(strings.Replace(base, "version: 2", "version: 1", 1), "")
|
|
var loadErr *ck.LoadError
|
|
if !errors.As(err, &loadErr) || len(loadErr.Diagnostics()) == 0 || !strings.Contains(loadErr.Diagnostics()[0].Message, "docs/migrations/v2.md") {
|
|
t.Fatalf("migration guidance: %v", err)
|
|
}
|
|
for _, tc := range []struct{ name, text string }{
|
|
{"optional target null", base + "chunking:\n target_sections: null\n"},
|
|
{"zero queue", strings.Replace(base, "llm:\n", " queue_capacity: 0\nllm:\n", 1)},
|
|
{"zero retries", base + " transient_retries: 0\n repair_attempts: 0\n"},
|
|
{"zero threshold", base + "thresholds:\n grammar: 0\n"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if _, err := loadText(tc.text, ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOverlayOverrideAndInheritance(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")
|
|
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\nchunking:\n max_section_tokens: 1000\n"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
loaded, err := Load(ck.File(main), "testing", ck.Override{Path: ck.Keys("llm", "proposal_profile"), Value: "explicit", Flag: "--proposal-profile"}, ck.Override{Path: ck.Keys("chunking", "min_section_tokens"), Value: 500, Flag: "--min-section-tokens"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if loaded.Config.LLM.ProposalProfile != "explicit" || loaded.Config.LLM.ValidationProfile != "explicit" || loaded.Config.Chunking.MaxSectionTokens != 1000 || loaded.Config.Chunking.MinSectionTokens != 500 {
|
|
t.Fatalf("resolved: %+v", loaded.Config)
|
|
}
|
|
if got, _ := loaded.Metadata().Lookup(ck.Keys("llm", "proposal_profile")); got.Origin.Layer != ck.LayerCLI {
|
|
t.Fatalf("override origin: %+v", got)
|
|
}
|
|
if got, _ := loaded.Metadata().Lookup(ck.Keys("chunking", "max_section_tokens")); got.Origin.Layer != ck.LayerOverlay {
|
|
t.Fatalf("overlay origin: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestSurvivingCredentials(t *testing.T) {
|
|
t.Setenv("BASE_SECRET", "")
|
|
t.Setenv("NEW_SECRET", "synthetic-secret")
|
|
withSecret := strings.Replace(base, "llm:\n", " other:\n endpoint: https://other.invalid/v1\n credential: BASE_SECRET\nllm:\n", 1)
|
|
if _, err := loadText(withSecret, ""); err == nil {
|
|
t.Fatal("missing unselected backend secret accepted")
|
|
}
|
|
replacement := ck.Override{Path: ck.Keys("backends", "other", "credential"), Value: "NEW_SECRET", Flag: "--credential"}
|
|
loaded, err := loadText(withSecret, "", replacement)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := loaded.Config.Backends["other"].Credential.Value(); got != "synthetic-secret" {
|
|
t.Fatalf("resolved secret: %q", got)
|
|
}
|
|
var effective bytes.Buffer
|
|
if err := loaded.WriteEffective(&effective); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(effective.String(), "synthetic-secret") {
|
|
t.Fatal("effective output leaked a secret")
|
|
}
|
|
null := ck.Override{Path: ck.Keys("backends", "other", "credential"), Value: nil, Flag: "--credential"}
|
|
loaded, err = loadText(withSecret, "", null)
|
|
if err != nil || loaded.Config.Backends["other"].Credential != nil {
|
|
t.Fatalf("null credential: %+v %v", loaded, err)
|
|
}
|
|
t.Setenv("AUDITA_MODEL", "legacy-model")
|
|
loaded, err = loadText(base, "")
|
|
if err != nil || loaded.Config.LLM.ProposalProfile != "proposal" {
|
|
t.Fatalf("legacy environment affected loading: %+v %v", loaded, err)
|
|
}
|
|
}
|