Merge origin/master into v2: sticky-growth test fixed

This commit is contained in:
2026-09-25 09:55:15 -07:00
2 changed files with 19 additions and 7 deletions
+6
View File
@@ -61,3 +61,9 @@ reason.
procedure. Owner's fault (T19 once more: a new task changed what an earlier given file procedure. Owner's fault (T19 once more: a new task changed what an earlier given file
measures, and the pre-handover walk missed it). `helpers_test.go` is now a v2 given file that measures, and the pre-handover walk missed it). `helpers_test.go` is now a v2 given file that
answers `/props` without counting it; resumed. answers `/props` without counting it; resumed.
- 2026-09-25, task 02: my `TestStickyLeaseSurvivesGrowthUntilItDoesNotFit` "grew" the conversation
by enlarging the *first user message*, which by the fingerprint spec makes it a different
conversation — so the test demanded `reused` for a new key. Ornith diagnosed it exactly ("turn
2's fp differs from turn 1's, yet the test expects reuse") and the session ended on a
malformed tool call. Test fault (mine): later turns are now appended after the first user
message. Resumed from the working tree.
@@ -110,6 +110,13 @@ func TestUnknownContextNeverBlocks(t *testing.T) {
} }
} }
// grow appends later turns to a conversation body without touching its system prompt or first
// user message, so the fingerprint — and therefore the lease — stays the same.
func grow(body string, words int) string {
turn := `,{"role":"assistant","content":"ok"},{"role":"user","content":"` + strings.Repeat("x ", words) + `"}`
return strings.Replace(body, `]}`, turn+`]}`, 1)
}
func TestStickyLeaseSurvivesGrowthUntilItDoesNotFit(t *testing.T) { func TestStickyLeaseSurvivesGrowthUntilItDoesNotFit(t *testing.T) {
small := ctxUpstream(t, "small", 8192, 2) small := ctxUpstream(t, "small", 8192, 2)
big := ctxUpstream(t, "big", 131072, 1) big := ctxUpstream(t, "big", 131072, 1)
@@ -120,19 +127,18 @@ func TestStickyLeaseSurvivesGrowthUntilItDoesNotFit(t *testing.T) {
if resp.Header.Get(proxy.HostHeader) != "small" { if resp.Header.Get(proxy.HostHeader) != "small" {
t.Fatal("setup: first turn must be on small") t.Fatal("setup: first turn must be on small")
} }
// Same conversation (same first user message), later turn well under 4096: stays. // Same conversation, a later turn well under 4096 tokens: stays.
longer := strings.Replace(body, `"content":"`, `"content":"`+strings.Repeat("x ", 500), 1) resp = r.post("/r/v1/chat/completions", grow(body, 500))
resp = r.post("/r/v1/chat/completions", longer)
drain(resp) drain(resp)
if resp.Header.Get(proxy.HostHeader) != "small" || resp.Header.Get(proxy.LeaseHeader) != "reused" { if resp.Header.Get(proxy.HostHeader) != "small" || resp.Header.Get(proxy.LeaseHeader) != "reused" {
t.Errorf("turn 2: %q %q, want small reused", resp.Header.Get(proxy.HostHeader), resp.Header.Get(proxy.LeaseHeader)) t.Errorf("turn 2: %q %q, want small reused", resp.Header.Get(proxy.HostHeader), resp.Header.Get(proxy.LeaseHeader))
} }
// A turn that outgrows the slot moves the lease — once — and the move is recorded as an event. // A turn that outgrows the slot moves the lease — once — and the move is visible in the header.
huge := strings.Replace(body, `"content":"`, `"content":"`+strings.Repeat("x ", 30000), 1) huge := grow(body, 30000)
resp = r.post("/r/v1/chat/completions", huge) resp = r.post("/r/v1/chat/completions", huge)
drain(resp) drain(resp)
if resp.StatusCode != 200 || resp.Header.Get(proxy.HostHeader) != "big" { if resp.StatusCode != 200 || resp.Header.Get(proxy.HostHeader) != "big" || !strings.HasPrefix(resp.Header.Get(proxy.CtxHeader), "moved") {
t.Fatalf("outgrown turn: %d %q, want 200 from big", resp.StatusCode, resp.Header.Get(proxy.HostHeader)) t.Fatalf("outgrown turn: %d %q ctx=%q, want 200 from big with a moved header", resp.StatusCode, resp.Header.Get(proxy.HostHeader), resp.Header.Get(proxy.CtxHeader))
} }
resp = r.post("/r/v1/chat/completions", huge) resp = r.post("/r/v1/chat/completions", huge)
drain(resp) drain(resp)