test(e2e): orphan sweep at globalSetup
Wipes e2e-* apps and e2e* admin users before the suite starts so a prior crashed run doesn't accumulate state across runs (45 rows observed on 2026-05-28). Per-row try/catch keeps it best-effort; a sweep failure never blocks the suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ export default async function globalSetup(): Promise<void> {
|
||||
await assertBackendUp();
|
||||
await fs.mkdir(AUTH_DIR, { recursive: true });
|
||||
const token = await loginAsAdmin();
|
||||
await sweepOrphans(token);
|
||||
await persistAdminStorageState(token);
|
||||
}
|
||||
|
||||
@@ -71,6 +72,57 @@ async function loginAsAdmin(): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up apps + admin users left over from a previous crashed run.
|
||||
// The convention is that every e2e-created resource has a slug
|
||||
// starting with `e2e-` (apps) or a username starting with `e2e`
|
||||
// (admins) — see fixtures/ids.ts. Best-effort: a sweep failure must
|
||||
// not stop the suite from running.
|
||||
async function sweepOrphans(token: string): Promise<void> {
|
||||
const ctx = await request.newContext({
|
||||
baseURL: API_BASE,
|
||||
extraHTTPHeaders: { authorization: `Bearer ${token}` }
|
||||
});
|
||||
try {
|
||||
try {
|
||||
const res = await ctx.get('/api/v1/admin/apps');
|
||||
if (res.ok()) {
|
||||
const apps = (await res.json()) as Array<{ slug: string }>;
|
||||
for (const app of apps) {
|
||||
if (!app.slug.startsWith('e2e-')) continue;
|
||||
try {
|
||||
await ctx.delete(
|
||||
`/api/v1/admin/apps/${encodeURIComponent(app.slug)}?force=true`
|
||||
);
|
||||
} catch {
|
||||
// Individual delete failure is non-fatal — the per-test
|
||||
// cleanup will catch it on the next run.
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Listing failed; nothing to do but proceed.
|
||||
}
|
||||
try {
|
||||
const res = await ctx.get('/api/v1/admin/admins');
|
||||
if (res.ok()) {
|
||||
const admins = (await res.json()) as Array<{ id: string; username: string }>;
|
||||
for (const a of admins) {
|
||||
if (!/^e2e/.test(a.username)) continue;
|
||||
try {
|
||||
await ctx.delete(`/api/v1/admin/admins/${a.id}`);
|
||||
} catch {
|
||||
// Same per-row tolerance as above.
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Listing failed; same as above.
|
||||
}
|
||||
} finally {
|
||||
await ctx.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// The dashboard reads its session from localStorage under the key
|
||||
// `picloud.admin.token` (see src/lib/auth.ts). We can't write to
|
||||
// localStorage without a browser context, so launch a throwaway one,
|
||||
|
||||
Reference in New Issue
Block a user