116 lines
4.4 KiB
Go
116 lines
4.4 KiB
Go
package config_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.wntrmute.dev/kyle/crossbar/internal/config"
|
|
)
|
|
|
|
const templateBase = `
|
|
listen = "127.0.0.1:1"
|
|
[hosts.a]
|
|
base_url = "http://a:1"
|
|
models = { "m" = { }, "n" = { } }
|
|
[routes."opencode-*"]
|
|
hosts = ["a"]
|
|
default_model = "m"
|
|
[routes."opencode-rust-*"]
|
|
hosts = ["a"]
|
|
default_model = "n"
|
|
[routes.opencode-fixed]
|
|
hosts = ["a"]
|
|
[routes.paper]
|
|
hosts = ["a"]
|
|
`
|
|
|
|
// A route whose name ends in "-*" is a template: any request route that starts with the part
|
|
// before the star, with something after it, uses that route's config. An exact name wins over a
|
|
// template; the longest matching template wins over shorter ones.
|
|
func TestRouteTemplatesResolve(t *testing.T) {
|
|
c, err := config.Parse(strings.NewReader(templateBase))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, tc := range []struct {
|
|
name, wantKey, wantModel string
|
|
ok bool
|
|
}{
|
|
{"paper", "paper", "", true},
|
|
{"opencode-fixed", "opencode-fixed", "", true}, // exact beats template
|
|
{"opencode-projecta-4242", "opencode-*", "m", true}, // template
|
|
{"opencode-rust-a-7", "opencode-rust-*", "n", true}, // longest template wins
|
|
{"opencode-", "", "", false}, // nothing after the prefix
|
|
{"opencode", "", "", false}, // the dash is part of the prefix
|
|
{"opencodex", "", "", false}, // not a prefix match
|
|
{"opencode-*", "", "", false}, // a literal star is never a request route
|
|
{"Opencode-A", "", "", false}, // not a valid route name
|
|
{"nope", "", "", false},
|
|
} {
|
|
r, key, ok := c.Route(tc.name)
|
|
if ok != tc.ok || key != tc.wantKey || (ok && r.DefaultModel != tc.wantModel) {
|
|
t.Errorf("Route(%q) = (%+v, %q, %v), want key %q model %q ok %v", tc.name, r, key, ok, tc.wantKey, tc.wantModel, tc.ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRouteTemplateNamesAreValidated(t *testing.T) {
|
|
for name, tc := range map[string]struct {
|
|
route string
|
|
wantErr string
|
|
}{
|
|
"star in the middle": {`"open*code"`, "routes.open*code"},
|
|
"star without dash": {`"opencode*"`, "routes.opencode*"},
|
|
"bare star": {`"*"`, "routes.*"},
|
|
"double star": {`"opencode-**"`, "routes.opencode-**"},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
text := "listen = \"127.0.0.1:1\"\n[hosts.a]\nbase_url = \"http://a:1\"\nmodels = { \"m\" = { } }\n[routes." + tc.route + "]\nhosts = [\"a\"]\n"
|
|
_, err := config.Parse(strings.NewReader(text))
|
|
ce, ok := err.(*config.Error)
|
|
if !ok || ce.Field != tc.wantErr {
|
|
t.Fatalf("err = %v, want *config.Error on %q", err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
// A template alone satisfies "at least one route".
|
|
if _, err := config.Parse(strings.NewReader("listen = \"127.0.0.1:1\"\n[hosts.a]\nbase_url = \"http://a:1\"\nmodels = { \"m\" = { } }\n[routes.\"x-*\"]\nhosts = [\"a\"]\n")); err != nil {
|
|
t.Errorf("a template-only config must parse: %v", err)
|
|
}
|
|
}
|
|
|
|
// broadcasts: a wake target may name several broadcast addresses (a host that roams between two
|
|
// Wi-Fi networks). `broadcast` (one) and `broadcasts` (a list) are alternatives: exactly one.
|
|
func TestWakeBroadcasts(t *testing.T) {
|
|
head := "listen = \"127.0.0.1:1\"\n[hosts.a]\nbase_url = \"http://a:1\"\nmodels = { \"m\" = { } }\n[hosts.a.wake]\nmac = \"aa:bb:cc:dd:ee:ff\"\n"
|
|
tail := "\n[routes.r]\nhosts = [\"a\"]\n"
|
|
c, err := config.Parse(strings.NewReader(head + `broadcasts = ["192.168.88.255:9", "192.168.1.255:9"]` + tail))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := c.Hosts["a"].Wake.Addresses(); len(got) != 2 || got[0] != "192.168.88.255:9" || got[1] != "192.168.1.255:9" {
|
|
t.Errorf("Addresses() = %v, want both, in order", got)
|
|
}
|
|
c, err = config.Parse(strings.NewReader(head + `broadcast = "192.168.88.255:9"` + tail))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := c.Hosts["a"].Wake.Addresses(); len(got) != 1 || got[0] != "192.168.88.255:9" {
|
|
t.Errorf("Addresses() = %v, want the single broadcast", got)
|
|
}
|
|
for name, body := range map[string]string{
|
|
"both": "broadcast = \"192.168.88.255:9\"\nbroadcasts = [\"192.168.1.255:9\"]",
|
|
"neither": "wait = \"30s\"",
|
|
"empty list": "broadcasts = []",
|
|
"bad entry": "broadcasts = [\"192.168.1.255\"]", // no port
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
_, err := config.Parse(strings.NewReader(head + body + tail))
|
|
ce, ok := err.(*config.Error)
|
|
if !ok || !strings.HasPrefix(ce.Field, "hosts.a.wake") {
|
|
t.Fatalf("err = %v, want *config.Error under hosts.a.wake", err)
|
|
}
|
|
})
|
|
}
|
|
}
|