mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-07 02:07:47 +08:00
On the server's own credentials a provider 403 now gets its own code, server_key_forbidden, with a hint in all four languages: today's free quota is used up, it resets tomorrow, and users can add their own key in model settings. The generic "The provider returned an error." line is left out for this code. A daily spend cap that blocks a shared key makes every call return 403. The old hint said the key may lack access to the model or region, which points users at a setting they cannot change. A 403 on the user's own key keeps the old hint and the provider's text.
105 lines
3.6 KiB
TypeScript
105 lines
3.6 KiB
TypeScript
import { expect, type Page, test } from "@playwright/test"
|
|
import { getIframe, sendMessage } from "./lib/fixtures"
|
|
|
|
/** Answer the chat request with the given response and send a message */
|
|
async function chatWith(
|
|
page: Page,
|
|
response: { status: number; contentType: string; body: string },
|
|
) {
|
|
await page.route("**/api/chat", (route) => route.fulfill(response))
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
|
await sendMessage(page, "Draw a box")
|
|
}
|
|
|
|
test("a rejected key shows a hint, the provider's words and a settings button", async ({
|
|
page,
|
|
}) => {
|
|
await chatWith(page, {
|
|
status: 401,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
type: "provider",
|
|
code: "invalid_api_key",
|
|
message: "Authentication Fails, Your api key is invalid",
|
|
}),
|
|
})
|
|
await expect(
|
|
page.getByText("The provider rejected the API key"),
|
|
).toBeVisible({
|
|
timeout: 15000,
|
|
})
|
|
// First match: the Next.js dev overlay at the end also lists the error
|
|
await expect(page.getByText("Authentication Fails").first()).toBeVisible()
|
|
await page.getByRole("button", { name: "Open model settings" }).click()
|
|
await expect(
|
|
page.getByRole("dialog", { name: "AI Model Configuration" }),
|
|
).toBeVisible()
|
|
})
|
|
|
|
test("an error before the stream shows its text, not raw JSON", async ({
|
|
page,
|
|
}) => {
|
|
await chatWith(page, {
|
|
status: 400,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
error: 'Model "x" is not available on the server',
|
|
}),
|
|
})
|
|
// The whole message, not the JSON around it (the Next.js dev overlay
|
|
// also lists the raw console error; it is not part of the app)
|
|
await expect(
|
|
page.getByText('Model "x" is not available on the server', {
|
|
exact: true,
|
|
}),
|
|
).toBeVisible({ timeout: 15000 })
|
|
})
|
|
|
|
test("a provider rate limit is not shown as this site's quota", async ({
|
|
page,
|
|
}) => {
|
|
const errorText = JSON.stringify({
|
|
type: "provider",
|
|
code: "rate_limited",
|
|
message: "Rate limit exceeded for your organization",
|
|
})
|
|
await chatWith(page, {
|
|
status: 200,
|
|
contentType: "text/event-stream",
|
|
body: `data: {"type":"start"}\n\ndata: ${JSON.stringify({ type: "error", errorText })}\n\ndata: [DONE]\n\n`,
|
|
})
|
|
await expect(
|
|
page.getByText("The provider is limiting requests"),
|
|
).toBeVisible({ timeout: 15000 })
|
|
// The site's own tokens-per-minute toast
|
|
await expect(page.getByText("Rate limit reached")).toHaveCount(0)
|
|
})
|
|
|
|
test("a refused server key shows only the quota hint and a settings button", async ({
|
|
page,
|
|
}) => {
|
|
// What the chat route streams when the server's key gets a 403, e.g.
|
|
// after a daily spend cap blocked it
|
|
const errorText = JSON.stringify({
|
|
type: "provider",
|
|
code: "server_key_forbidden",
|
|
message: "",
|
|
})
|
|
await chatWith(page, {
|
|
status: 200,
|
|
contentType: "text/event-stream",
|
|
body: `data: {"type":"start"}\n\ndata: ${JSON.stringify({ type: "error", errorText })}\n\ndata: [DONE]\n\n`,
|
|
})
|
|
await expect(
|
|
page.getByText("Today's free quota is used up", { exact: false }),
|
|
).toBeVisible({ timeout: 15000 })
|
|
await expect(page.getByText("The provider returned an error")).toHaveCount(
|
|
0,
|
|
)
|
|
await page.getByRole("button", { name: "Open model settings" }).click()
|
|
await expect(
|
|
page.getByRole("dialog", { name: "AI Model Configuration" }),
|
|
).toBeVisible()
|
|
})
|