v2.3 plan: clients that manage their own slots (control calls, route affinity, queue = false, route listeners, llama-server ctx error); given tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 17:36:59 -07:00
co-authored by Claude Opus 5.5
parent 4c6415819e
commit 3518e84dd7
16 changed files with 1655 additions and 0 deletions
@@ -0,0 +1,75 @@
package config_test
// v2.3 task 02: the affinity and queue route keys.
import (
"strings"
"testing"
"git.wntrmute.dev/kyle/crossbar/internal/config"
)
const affinityBase = `
listen = "127.0.0.1:1"
[hosts.a]
base_url = "http://a:1"
models = { "m" = { } }
[routes.plain]
hosts = ["a"]
[routes.convo]
hosts = ["a"]
affinity = "conversation"
[routes.boxmaker]
hosts = ["a"]
affinity = "route"
queue = false
[routes."bm-*"]
hosts = ["a"]
affinity = "route"
queue = false
[routes.queued]
hosts = ["a"]
queue = true
`
func TestAffinityAndQueueKeys(t *testing.T) {
c, err := config.Parse(strings.NewReader(affinityBase))
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
route string
perRoute, queues bool
}{
{"plain", false, true}, // defaults: conversation affinity, queueing on
{"convo", false, true},
{"boxmaker", true, false},
{"bm-agent-1", true, false}, // a template's keys reach its concrete routes
{"queued", false, true},
} {
r, _, ok := c.Route(tc.route)
if !ok {
t.Fatalf("route %q not found", tc.route)
}
if r.PerRoute() != tc.perRoute || r.Queues() != tc.queues {
t.Errorf("%s: PerRoute %v Queues %v, want %v %v", tc.route, r.PerRoute(), r.Queues(), tc.perRoute, tc.queues)
}
}
}
func TestAffinityRejectsUnknownValues(t *testing.T) {
for _, bad := range []string{`"session"`, `"Route"`, `1`} {
text := strings.Replace(affinityBase, `affinity = "conversation"`, "affinity = "+bad, 1)
_, err := config.Parse(strings.NewReader(text))
if err == nil || !strings.Contains(err.Error(), "routes.convo.affinity") {
t.Errorf("affinity = %s: err %v, want one naming routes.convo.affinity", bad, err)
}
}
}
func TestQueueMustBeABool(t *testing.T) {
text := strings.Replace(affinityBase, "queue = true", `queue = "no"`, 1)
if _, err := config.Parse(strings.NewReader(text)); err == nil {
t.Error(`queue = "no" parsed; want an error`)
}
}
@@ -0,0 +1,54 @@
package config_test
// v2.3 task 03: a concrete route may own a dedicated listener. Every request that arrives on it is
// that route, with the upstream path unprefixed, for clients that cannot put a route in the path
// or a header (Boxmaker's inferproxy rewrites nothing).
import (
"strings"
"testing"
"git.wntrmute.dev/kyle/crossbar/internal/config"
)
const listenBase = `
listen = "127.0.0.1:7777"
[hosts.a]
base_url = "http://a:1"
models = { "m" = { } }
[routes.bm-a]
hosts = ["a"]
listen = "127.0.0.1:7801"
[routes.bm-b]
hosts = ["a"]
listen = "127.0.0.1:7802"
[routes.plain]
hosts = ["a"]
`
func TestRouteListen(t *testing.T) {
c, err := config.Parse(strings.NewReader(listenBase))
if err != nil {
t.Fatal(err)
}
for route, want := range map[string]string{"bm-a": "127.0.0.1:7801", "bm-b": "127.0.0.1:7802", "plain": ""} {
if got := c.Routes[route].Listen; got != want {
t.Errorf("%s listen = %q, want %q", route, got, want)
}
}
}
func TestRouteListenRejected(t *testing.T) {
for _, tc := range []struct{ name, text, want string }{
{"not host:port", strings.Replace(listenBase, `"127.0.0.1:7801"`, `"7801"`, 1), "routes.bm-a.listen"},
{"bad port", strings.Replace(listenBase, `"127.0.0.1:7801"`, `"127.0.0.1:http"`, 1), "routes.bm-a.listen"},
{"port zero", strings.Replace(listenBase, `"127.0.0.1:7801"`, `"127.0.0.1:0"`, 1), "routes.bm-a.listen"},
{"same as another route", strings.Replace(listenBase, `"127.0.0.1:7802"`, `"127.0.0.1:7801"`, 1), "listen"},
{"same as the main listener", strings.Replace(listenBase, `"127.0.0.1:7801"`, `"127.0.0.1:7777"`, 1), "routes.bm-a.listen"},
{"on a template", listenBase + "[routes.\"t-*\"]\nhosts = [\"a\"]\nlisten = \"127.0.0.1:7803\"\n", "t-*"},
} {
if _, err := config.Parse(strings.NewReader(tc.text)); err == nil || !strings.Contains(err.Error(), tc.want) {
t.Errorf("%s: err %v, want one containing %q", tc.name, err, tc.want)
}
}
}