fix: final review pass — email collision, orphaned-run loop, null confidence guards
- changeEmail validates and refuses an address another account owns (users.email is UNIQUE; a collision was a 500 inside the settings form). - /app/[id] no longer redirects a 'running' row to itself; it renders what exists and says the run was interrupted. - ScoreBar / OptionRow / VerdictBlock survive null numbers from the model instead of throwing and losing the block (seen live: '[openui] runtime/render-error: Component ScoreBar render failed'). - drop the ad-hoc render-debug scripts now that Playwright covers the browser. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
07dd3d6bf8
commit
5160a00db5
@@ -0,0 +1,38 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
test.use({ storageState: ".auth/user.json" });
|
||||
const DB = ".playwright-data/qai.db";
|
||||
|
||||
// The app opens this file too (WAL), so writes here are just another connection.
|
||||
function withDb<T>(fn: (d: DatabaseSync) => T): T {
|
||||
const d = new DatabaseSync(DB);
|
||||
try { return fn(d); } finally { d.close(); }
|
||||
}
|
||||
|
||||
test("a row left running renders what exists instead of redirecting forever", async ({ page }) => {
|
||||
const id = withDb((d) => {
|
||||
const userId = d.prepare("SELECT id FROM users ORDER BY rowid DESC LIMIT 1").get() as { id: string };
|
||||
const aid = randomUUID();
|
||||
// Backdated so it never becomes the rail's first row: history.spec.ts depends on that row being a real run.
|
||||
d.prepare("INSERT INTO analyses (id, user_id, title, xml, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?)")
|
||||
.run(aid, userId.id, "Orphaned run", "<ShowPlanXML/>", "running", Date.now() - 3_600_000, Date.now() - 3_600_000);
|
||||
return aid;
|
||||
});
|
||||
await page.goto(`/app/${id}`);
|
||||
await expect(page.getByText(/This run was interrupted before it finished/)).toBeVisible({ timeout: 15_000 });
|
||||
expect(page.url()).toContain(`/app/${id}`); // the old redirect sent this to itself
|
||||
await expect(page.getByRole("button", { name: "Analyse" })).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("an email another account already has is refused inline", async ({ page }) => {
|
||||
withDb((d) => d.prepare("INSERT INTO users (id, email, pass, created_at) VALUES (?,?,?,?)")
|
||||
.run(randomUUID(), "taken@qai.test", "salt:hash", Date.now()));
|
||||
await page.goto("/settings");
|
||||
await page.fill("#email", "taken@qai.test");
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
await expect(page.locator("form p[role=alert]")).toContainText("That email already has an account", { timeout: 15_000 });
|
||||
const still = withDb((d) => (d.prepare("SELECT email FROM users WHERE email = ?").get("taken@qai.test") as { email: string }).email);
|
||||
expect(still).toBe("taken@qai.test");
|
||||
});
|
||||
Reference in New Issue
Block a user