test: align stale integration tests surfaced post-v3.8.28 on main (#4129)

Two integration tests were red on `main` after the v3.8.28 release merged.
Integration tests do not gate PR->release, so these only surfaced on
push->main once the cycle's changes landed:

- v1-contracts-behavior count_tokens expected 3 (the old ceil(chars/4)
  heuristic). #4087 switched the estimator to real tiktoken (countTextTokens):
  "abcd" => 1, "12345678" => 3, total 4. Assertion updated to the verified
  tiktoken total.

- combo-routing-e2e round-robin tests assumed one-request-per-target rotation,
  but stickyRoundRobinLimit defaults to 3 and sticky batching has applied to
  combos since v3.8.26 (#3846). Both tests now drive enough requests to observe
  the batched rotation: round-robin cycles openai x3 -> claude x3 -> gemini x3;
  the strategy-update test proves round-robin took over by reaching claude on
  the 4th post-update request (a target priority would never select).

Test-only; no production behavior change. Verified locally: both files green.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-17 23:20:17 -03:00
committed by GitHub
parent cf716e97de
commit 9f14c1294a
2 changed files with 43 additions and 21 deletions

View File

@@ -99,14 +99,29 @@ test("round-robin combo cycles through three providers", async () => {
return buildGeminiResponse("Gemini round-robin");
};
const first = await handleChat(buildRequest({ body: buildOpenAIChatBody("router-rr") }));
const second = await handleChat(buildRequest({ body: buildOpenAIChatBody("router-rr") }));
const third = await handleChat(buildRequest({ body: buildOpenAIChatBody("router-rr") }));
// `stickyRoundRobinLimit` defaults to 3 (src/lib/db/settings.ts), so a combo using the
// round-robin strategy serves 3 consecutive requests per target before advancing to the
// next one — batched rotation, not one-request-per-target. Nine requests therefore cover
// a full cycle: openai x3 -> claude x3 -> gemini x3.
const responses = [];
for (let i = 0; i < 9; i++) {
responses.push(await handleChat(buildRequest({ body: buildOpenAIChatBody("router-rr") })));
}
assert.equal(first.status, 200);
assert.equal(second.status, 200);
assert.equal(third.status, 200);
assert.deepEqual(seenProviders, ["openai", "claude", "gemini"]);
for (const response of responses) {
assert.equal(response.status, 200);
}
assert.deepEqual(seenProviders, [
"openai",
"openai",
"openai",
"claude",
"claude",
"claude",
"gemini",
"gemini",
"gemini",
]);
});
test("priority combo sticks to the primary model while healthy", async () => {
@@ -382,19 +397,23 @@ test("strategy updates take effect for later requests on the same combo name", a
strategy: "round-robin",
config: { maxRetries: 0, retryDelayMs: 0 },
});
const second = await handleChat(
buildRequest({
body: buildOpenAIChatBody("router-dynamic", "Route dynamic second"),
})
);
const third = await handleChat(
buildRequest({
body: buildOpenAIChatBody("router-dynamic", "Route dynamic third"),
})
);
// After the strategy flips to round-robin the new behavior must take effect. With the
// default stickyRoundRobinLimit of 3 the round-robin batch serves openai three times
// before advancing, so the 4th post-update request rotates to claude — a provider the
// previous "priority" strategy would never have reached. That rotation is the proof the
// updated strategy is live.
const postUpdate = [];
for (let i = 0; i < 4; i++) {
postUpdate.push(
await handleChat(
buildRequest({ body: buildOpenAIChatBody("router-dynamic", `Route dynamic ${i}`) })
)
);
}
assert.equal(initial.status, 200);
assert.equal(second.status, 200);
assert.equal(third.status, 200);
assert.deepEqual(seenProviders, ["openai", "openai", "claude"]);
for (const response of postUpdate) {
assert.equal(response.status, 200);
}
assert.deepEqual(seenProviders, ["openai", "openai", "openai", "openai", "claude"]);
});

View File

@@ -169,5 +169,8 @@ test("contract: /api/v1/messages/count_tokens computes token estimate from text
assert.equal(response.status, 200);
const body = (await response.json()) as any;
assert.equal(body.input_tokens, 3);
// Real tiktoken count (countTextTokens): "abcd" => 1, "12345678" => 3 (digits split).
// The previous expectation (3) was the old ceil(chars/4) heuristic, replaced by the
// tiktoken-based estimator; the accurate total for this payload is 4.
assert.equal(body.input_tokens, 4);
});