Skip to content

Commit f61c1f3

Browse files
peppescgclaude
andauthored
fix: make assistant E2E tests model-agnostic and upgrade to qwen3 (#498)
* fix: make assistant E2E tests model-agnostic and upgrade to qwen3 The assistant E2E tests were flaky because they asserted on specific LLM output patterns (regex for sequential numbers, greeting with username). This breaks with models that have thinking mode (qwen3) or different output formatting. Changes: - Replace two flaky tests with a single deterministic arithmetic test ("What is 1 + 1?" → check response contains "2") - Increase beforeAll warmup timeout from 30s to 120s — qwen3 with thinking mode takes ~40s for first inference on CI runners - Upgrade E2E model from qwen2.5:1.5b to qwen3:1.7b in workflow and all source file defaults Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use testInfo.setTimeout and scope assertion to sidebar - Use testInfo.setTimeout in beforeAll with biome-ignore for the required empty destructuring (Playwright 1.58 does not support the { timeout } option on beforeAll) - Scope the /\b2\b/ assertion to the assistant sidebar (data-side="right") to avoid false positives from unrelated page content like pagination or counts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 79bc352 commit f61c1f3

4 files changed

Lines changed: 20 additions & 56 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
BETTER_AUTH_URL: http://localhost:3000
2727
BETTER_AUTH_SECRET: test-only-not-a-real-better-auth-secret
2828
USE_E2E_MODEL: "true"
29-
E2E_MODEL_NAME: qwen2.5:1.5b
29+
E2E_MODEL_NAME: qwen3:1.7b
3030
OLLAMA_BASE_URL: http://localhost:11434
3131
steps:
3232
- name: Checkout
@@ -36,7 +36,7 @@ jobs:
3636
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
3737
with:
3838
path: /home/runner/.ollama/models
39-
key: ollama-qwen2.5-1.5b-v3
39+
key: ollama-qwen3-1.7b-v1
4040

4141
- name: Start Ollama via Docker
4242
run: |
@@ -53,7 +53,7 @@ jobs:
5353
done
5454
5555
- name: Pull Ollama model
56-
run: docker exec ollama ollama pull qwen2.5:1.5b
56+
run: docker exec ollama ollama pull qwen3:1.7b
5757

5858
- name: Setup
5959
uses: ./.github/actions/setup

playwright.config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default defineConfig({
5454
BETTER_AUTH_RATE_LIMIT: "100",
5555
// Always use testing model for E2E tests to avoid needing OpenRouter API keys
5656
USE_E2E_MODEL: "true",
57-
E2E_MODEL_NAME: process.env.E2E_MODEL_NAME ?? "qwen2.5:1.5b",
57+
E2E_MODEL_NAME: process.env.E2E_MODEL_NAME ?? "qwen3:1.7b",
5858
OLLAMA_BASE_URL:
5959
process.env.OLLAMA_BASE_URL ?? "http://localhost:11434",
6060
},

src/app/api/chat/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from "@/lib/mcp/client";
1818
import { SYSTEM_PROMPT } from "./system-prompt";
1919

20-
const E2E_MODEL_NAME = process.env.E2E_MODEL_NAME ?? "qwen2.5:1.5b";
20+
const E2E_MODEL_NAME = process.env.E2E_MODEL_NAME ?? "qwen3:1.7b";
2121

2222
export const maxDuration = 60;
2323

tests/e2e/assistant.spec.ts

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect, test } from "./fixtures";
22

33
const OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL ?? "http://localhost:11434";
4-
const E2E_MODEL_NAME = process.env.E2E_MODEL_NAME ?? "qwen2.5:1.5b";
4+
const E2E_MODEL_NAME = process.env.E2E_MODEL_NAME ?? "qwen3:1.7b";
55

66
/**
77
* Warms up the testing model (Ollama) by making a simple generation request.
@@ -13,7 +13,7 @@ async function warmupTestingModel(): Promise<void> {
1313
headers: { "Content-Type": "application/json" },
1414
body: JSON.stringify({
1515
model: E2E_MODEL_NAME,
16-
prompt: "Say hello",
16+
prompt: "What is 1 + 1?",
1717
stream: false,
1818
}),
1919
});
@@ -34,8 +34,12 @@ test.describe("Assistant chat", () => {
3434
// Triple all timeouts for this describe block since LLM operations are slow
3535
test.slow();
3636

37-
// Warmup testing model before running any tests in this describe block
38-
test.beforeAll(async () => {
37+
// Warmup testing model before running any tests in this describe block.
38+
// qwen3 with thinking mode needs >30s for first inference on CI runners,
39+
// so we set a generous timeout (120s) for model loading + first generation.
40+
// biome-ignore lint/correctness/noEmptyPattern: Playwright requires destructured first arg to access testInfo
41+
test.beforeAll(async ({}, testInfo) => {
42+
testInfo.setTimeout(120_000);
3943
console.log("Warming up E2E testing model...");
4044
const startTime = Date.now();
4145

@@ -48,12 +52,9 @@ test.describe("Assistant chat", () => {
4852
}
4953
});
5054

51-
test("responds to user message with expected content", async ({
55+
test("responds to a simple arithmetic question", async ({
5256
authenticatedPage,
5357
}) => {
54-
// Use a unique identifier that we expect to appear in the response
55-
const testUsername = `testuser_${Date.now()}`;
56-
5758
// Navigate to catalog page which has the assistant sidebar
5859
await authenticatedPage.goto("/catalog");
5960

@@ -67,53 +68,16 @@ test.describe("Assistant chat", () => {
6768
authenticatedPage.getByPlaceholder(/type your message/i),
6869
).toBeVisible({ timeout: 10_000 });
6970

70-
// Type a message that includes the unique identifier
7171
const textarea = authenticatedPage.getByPlaceholder(/type your message/i);
72-
await textarea.fill(
73-
`Reply with a short greeting for the user named '${testUsername}'. Include their exact username in your response.`,
74-
);
72+
await textarea.fill("What is 1 + 1? Reply with just the number.");
7573

76-
// Submit the message
7774
await authenticatedPage.keyboard.press("Enter");
7875

79-
// Wait for the assistant's response to appear
80-
// The response should contain our unique username in a greeting
81-
// Using a generous timeout since model inference can take time
82-
// The regex matches any greeting pattern followed by the username
83-
await expect(
84-
authenticatedPage.getByText(
85-
new RegExp(`(hello|hi|hey|greetings).*${testUsername}`, "i"),
86-
),
87-
).toBeVisible({
88-
timeout: 60_000,
89-
});
90-
});
91-
92-
test("displays streaming response", async ({ authenticatedPage }) => {
93-
// Navigate to catalog page which has the assistant sidebar
94-
await authenticatedPage.goto("/catalog");
95-
96-
// Open the assistant sidebar
97-
await authenticatedPage
98-
.getByRole("button", { name: "Toggle Assistant" })
99-
.click();
100-
101-
// Wait for the sidebar to open and chat input to be visible
102-
await expect(
103-
authenticatedPage.getByPlaceholder(/type your message/i),
104-
).toBeVisible({ timeout: 10_000 });
105-
106-
const textarea = authenticatedPage.getByPlaceholder(/type your message/i);
107-
await textarea.fill("Count from 1 to 5, one number per line.");
108-
109-
await authenticatedPage.keyboard.press("Enter");
110-
111-
// Wait for the assistant's response containing numbers
112-
// Look for a pattern that indicates the assistant counted - digits on separate lines
113-
// This won't match the user's message "1 to 5" or model name "4.5"
114-
await expect(
115-
authenticatedPage.getByText(/\b1\s+2\s+3\b/), // Sequential numbers separated by whitespace
116-
).toBeVisible({
76+
// Wait for the assistant's response containing "2".
77+
// Scope to the assistant sidebar (data-side="right") to avoid matching
78+
// unrelated "2" text elsewhere on the page (pagination, counts, etc.).
79+
const sidebar = authenticatedPage.locator('[data-side="right"]');
80+
await expect(sidebar.getByText(/\b2\b/)).toBeVisible({
11781
timeout: 60_000,
11882
});
11983
});

0 commit comments

Comments
 (0)