192 lines
5.9 KiB
Go
192 lines
5.9 KiB
Go
package config_test
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.wntrmute.dev/kyle/crossbar/internal/config"
|
|
)
|
|
|
|
func TestGoodFile(t *testing.T) {
|
|
c, err := config.Load(filepath.Join("testdata", "good.toml"))
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if c.Listen != "100.64.0.9:7777" {
|
|
t.Errorf("Listen = %q", c.Listen)
|
|
}
|
|
if c.PollInterval.Duration != 5*time.Second {
|
|
t.Errorf("PollInterval = %v", c.PollInterval.Duration)
|
|
}
|
|
if c.QueueMax != 4 {
|
|
t.Errorf("QueueMax = %d", c.QueueMax)
|
|
}
|
|
alpha := c.Hosts["alpha"]
|
|
if alpha.BaseURL != "http://alpha.example:11434" {
|
|
t.Errorf("trailing slash not stripped: %q", alpha.BaseURL)
|
|
}
|
|
if alpha.Weight != 2 {
|
|
t.Errorf("alpha.Weight = %v", alpha.Weight)
|
|
}
|
|
if alpha.Models["ornith-1.5-35b-a3b"].Parallel != 4 || alpha.Models["small-9b"].Parallel != 6 {
|
|
t.Errorf("alpha.Models = %+v", alpha.Models)
|
|
}
|
|
beta := c.Hosts["beta"]
|
|
if beta.Weight != 1 {
|
|
t.Errorf("beta.Weight default = %v, want 1", beta.Weight)
|
|
}
|
|
if beta.Models["ornith-1.5-35b-a3b"].Parallel != 1 {
|
|
t.Errorf("beta parallel default = %d, want 1", beta.Models["ornith-1.5-35b-a3b"].Parallel)
|
|
}
|
|
r := c.Routes["opencode-a"]
|
|
if len(r.Hosts) != 2 || r.Hosts[0] != "alpha" || r.Hosts[1] != "beta" {
|
|
t.Errorf("route hosts = %v", r.Hosts)
|
|
}
|
|
if r.DefaultModel != "ornith-1.5-35b-a3b" {
|
|
t.Errorf("DefaultModel = %q", r.DefaultModel)
|
|
}
|
|
if c.Routes["hermes-x"].DefaultModel != "" {
|
|
t.Errorf("hermes-x DefaultModel should be empty")
|
|
}
|
|
if !c.Serves("alpha", "small-9b") || c.Serves("beta", "small-9b") || c.Serves("nope", "m") {
|
|
t.Errorf("Serves is wrong")
|
|
}
|
|
}
|
|
|
|
func TestDefaults(t *testing.T) {
|
|
c, err := config.Parse(strings.NewReader(`
|
|
listen = "127.0.0.1:1"
|
|
[hosts.a]
|
|
base_url = "http://a:1"
|
|
models = { "m" = { } }
|
|
[routes.r]
|
|
hosts = ["a"]
|
|
`))
|
|
if err != nil {
|
|
t.Fatalf("Parse: %v", err)
|
|
}
|
|
if c.PollInterval.Duration != config.DefaultPollInterval {
|
|
t.Errorf("PollInterval default = %v", c.PollInterval.Duration)
|
|
}
|
|
if c.QueueMax != config.DefaultQueueMax {
|
|
t.Errorf("QueueMax default = %d", c.QueueMax)
|
|
}
|
|
}
|
|
|
|
func TestBadFiles(t *testing.T) {
|
|
cases := []struct{ file, field string }{
|
|
{"bad-listen.toml", "listen"},
|
|
{"bad-unknown-host.toml", "routes.r.hosts"},
|
|
{"bad-default-model.toml", "routes.r.default_model"},
|
|
{"bad-unknown-key.toml", "bogus_key"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.file, func(t *testing.T) {
|
|
_, err := config.Load(filepath.Join("testdata", tc.file))
|
|
if err == nil {
|
|
t.Fatalf("want error")
|
|
}
|
|
e, ok := config.IsError(err)
|
|
if !ok {
|
|
t.Fatalf("want *config.Error, got %T: %v", err, err)
|
|
}
|
|
if e.Field != tc.field {
|
|
t.Errorf("Field = %q, want %q (%v)", e.Field, tc.field, err)
|
|
}
|
|
if !strings.HasPrefix(err.Error(), "config: "+tc.field+": ") {
|
|
t.Errorf("Error() = %q", err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBadValues(t *testing.T) {
|
|
base := `
|
|
listen = %q
|
|
poll_interval = %q
|
|
[hosts.a]
|
|
base_url = %q
|
|
weight = %v
|
|
models = { "m" = { parallel = %d } }
|
|
[routes.%s]
|
|
hosts = ["a"]
|
|
`
|
|
cases := []struct {
|
|
name string
|
|
listen, poll, url, route string
|
|
weight float64
|
|
parallel int
|
|
field string
|
|
}{
|
|
{"empty listen", "", "5s", "http://a:1", "r", 1, 1, "listen"},
|
|
{"no port", "127.0.0.1", "5s", "http://a:1", "r", 1, 1, "listen"},
|
|
{"v6 any", "[::]:7", "5s", "http://a:1", "r", 1, 1, "listen"},
|
|
{"poll too short", "127.0.0.1:7", "500ms", "http://a:1", "r", 1, 1, "poll_interval"},
|
|
{"ftp url", "127.0.0.1:7", "5s", "ftp://a:1", "r", 1, 1, "hosts.a.base_url"},
|
|
{"no host", "127.0.0.1:7", "5s", "http://", "r", 1, 1, "hosts.a.base_url"},
|
|
{"query", "127.0.0.1:7", "5s", "http://a:1/v1?x=1", "r", 1, 1, "hosts.a.base_url"},
|
|
{"negative weight", "127.0.0.1:7", "5s", "http://a:1", "r", -1, 1, "hosts.a.weight"},
|
|
{"negative parallel", "127.0.0.1:7", "5s", "http://a:1", "r", 1, -2, "hosts.a.models.m.parallel"},
|
|
{"route name", "127.0.0.1:7", "5s", "http://a:1", "Bad_Name", 1, 1, "routes.Bad_Name"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
text := fmt.Sprintf(base, tc.listen, tc.poll, tc.url, tc.weight, tc.parallel, tc.route)
|
|
_, err := config.Parse(strings.NewReader(text))
|
|
if err == nil {
|
|
t.Fatalf("want error for %s", tc.name)
|
|
}
|
|
e, ok := config.IsError(err)
|
|
if !ok {
|
|
t.Fatalf("want *config.Error, got %T: %v", err, err)
|
|
}
|
|
if e.Field != tc.field {
|
|
t.Errorf("Field = %q, want %q (%v)", e.Field, tc.field, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMissingSections(t *testing.T) {
|
|
for _, tc := range []struct{ name, text, field string }{
|
|
{"no hosts", "listen = \"127.0.0.1:7\"\n[routes.r]\nhosts = [\"a\"]\n", "hosts"},
|
|
{"no routes", "listen = \"127.0.0.1:7\"\n[hosts.a]\nbase_url = \"http://a:1\"\nmodels = { \"m\" = { } }\n", "routes"},
|
|
{"host without models", "listen = \"127.0.0.1:7\"\n[hosts.a]\nbase_url = \"http://a:1\"\n[routes.r]\nhosts = [\"a\"]\n", "hosts.a.models"},
|
|
{"route without hosts", "listen = \"127.0.0.1:7\"\n[hosts.a]\nbase_url = \"http://a:1\"\nmodels = { \"m\" = { } }\n[routes.r]\n", "routes.r.hosts"},
|
|
{"host twice", "listen = \"127.0.0.1:7\"\n[hosts.a]\nbase_url = \"http://a:1\"\nmodels = { \"m\" = { } }\n[routes.r]\nhosts = [\"a\", \"a\"]\n", "routes.r.hosts"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := config.Parse(strings.NewReader(tc.text))
|
|
e, ok := config.IsError(err)
|
|
if !ok {
|
|
t.Fatalf("want *config.Error, got %v", err)
|
|
}
|
|
if e.Field != tc.field {
|
|
t.Errorf("Field = %q, want %q", e.Field, tc.field)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNotTOML(t *testing.T) {
|
|
_, err := config.Parse(strings.NewReader("listen = [unterminated"))
|
|
if err == nil {
|
|
t.Fatal("want error")
|
|
}
|
|
if _, ok := config.IsError(err); ok {
|
|
t.Errorf("a syntax error is not a validation Error")
|
|
}
|
|
if !strings.HasPrefix(err.Error(), "config: ") {
|
|
t.Errorf("Error() = %q", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestMissingFile(t *testing.T) {
|
|
if _, err := config.Load(filepath.Join("testdata", "does-not-exist.toml")); err == nil {
|
|
t.Fatal("want error")
|
|
}
|
|
}
|