Route templates: a route named x-* serves any request route x-<something>
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user