harness: config layer + model routing v0 (tier map, no heuristics)
harness.toml loading via a stdlib-only TOML subset parser (tables, bare
keys, strings/ints/bools, multi-line arrays; anything richer fails loudly).
Secrets are refs only (env:/file:/literal:, bw: reserved) and are redacted
from every error path. Model routing v0: static class -> tier alias ->
concrete model map per the DESIGN model-selection layer; requests carry the
resolved concrete model and unknown classes fail hard so routing stays
auditable. Table-driven tests cover the parser, validation, key refs, and
routing decisions.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseTOMLValid(t *testing.T) {
|
||||
src := `
|
||||
# top comment
|
||||
vertical = "demo" # trailing comment
|
||||
work_root = "."
|
||||
|
||||
[loop]
|
||||
max_rounds = 4
|
||||
|
||||
[redmine]
|
||||
url = "https://rm.example.org"
|
||||
scope_query_id = 42
|
||||
class_field = "Class"
|
||||
|
||||
[litellm]
|
||||
base_url = "http://h:4001"
|
||||
timeout_secs = 30
|
||||
max_retries = 3
|
||||
|
||||
[models]
|
||||
mopac-study = "glm-4.7-flash" # hyphenated bare key
|
||||
default_tier = "mopac-study"
|
||||
|
||||
[models.classes]
|
||||
study = "mopac-study"
|
||||
code = "mopac-code"
|
||||
|
||||
[tools.bash]
|
||||
enabled = false
|
||||
default = "deny"
|
||||
allow = ["pwd", "ls *", "cat *"]
|
||||
deny = [
|
||||
"sudo *", # comment inside array
|
||||
"ssh *",
|
||||
]
|
||||
|
||||
[deep.nested.table]
|
||||
flag = true
|
||||
note = "quote \" and # inside string"
|
||||
empty = []
|
||||
`
|
||||
doc, err := ParseTOML(src)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseTOML: %v", err)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
got any
|
||||
want any
|
||||
}{
|
||||
{"vertical", str(doc.String("vertical")), "demo"},
|
||||
{"work_root", str(doc.String("work_root")), "."},
|
||||
{"max_rounds", intg(doc.Table("loop").Int("max_rounds")), int64(4)},
|
||||
{"scope_query_id", intg(doc.Table("redmine").Int("scope_query_id")), int64(42)},
|
||||
{"class_field", str(doc.Table("redmine").String("class_field")), "Class"},
|
||||
{"timeout_secs", intg(doc.Table("litellm").Int("timeout_secs")), int64(30)},
|
||||
{"max_retries", intg(doc.Table("litellm").Int("max_retries")), int64(3)},
|
||||
{"tier", str(doc.Table("models").String("mopac-study")), "glm-4.7-flash"},
|
||||
{"default_tier", str(doc.Table("models").String("default_tier")), "mopac-study"},
|
||||
{"class map", str(doc.Table("models", "classes").String("study")), "mopac-study"},
|
||||
{"bash enabled", boolean(doc.Table("tools", "bash").Bool("enabled")), false},
|
||||
{"deep bool", boolean(doc.Table("deep", "nested", "table").Bool("flag")), true},
|
||||
{"escaped string", str(doc.Table("deep", "nested", "table").String("note")), `quote " and # inside string`},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
if tc.got != tc.want {
|
||||
t.Errorf("%s = %v, want %v", tc.name, tc.got, tc.want)
|
||||
}
|
||||
}
|
||||
|
||||
allow, ok := doc.Table("tools", "bash").StringList("allow")
|
||||
if !ok || len(allow) != 3 || allow[1] != "ls *" {
|
||||
t.Errorf("allow list = %v ok=%v", allow, ok)
|
||||
}
|
||||
deny, ok := doc.Table("tools", "bash").StringList("deny")
|
||||
if !ok || len(deny) != 2 || deny[0] != "sudo *" {
|
||||
t.Errorf("deny list = %v ok=%v", deny, ok)
|
||||
}
|
||||
if empty, ok := doc.Table("deep", "nested", "table").StringList("empty"); !ok || len(empty) != 0 {
|
||||
t.Errorf("empty array = %v ok=%v", empty, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func str(v string, ok bool) string {
|
||||
if !ok {
|
||||
panic("string key missing")
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func intg(v int64, ok bool) int64 {
|
||||
if !ok {
|
||||
panic("int key missing")
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func boolean(v bool, ok bool) bool {
|
||||
if !ok {
|
||||
panic("bool key missing")
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func TestParseTOMLErrors(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
src string
|
||||
want string
|
||||
}{
|
||||
{"unterminated string", `a = "oops`, "unterminated string"},
|
||||
{"bare value", `a = sk-123`, "unsupported value"},
|
||||
{"missing value", `a =`, "missing value"},
|
||||
{"bad key", `a b = "c"`, "invalid key"},
|
||||
{"duplicate key", "a = \"1\"\na = \"2\"", "duplicate key"},
|
||||
{"scalar to table", "a = \"1\"\n[a.b]", "cannot become table"},
|
||||
{"unterminated array", "a = [\n\"x\",", "unterminated array"},
|
||||
{"bad table name", `[a.]`, "invalid table name"},
|
||||
{"no equals", `[t]\njustakey`, "expected key = value"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := ParseTOML(strings.ReplaceAll(tc.src, `\n`, "\n"))
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q, got nil", tc.want)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("error %q does not contain %q", err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user