/** * US-12 — simulation delete (RBAC + cascade + confirm modal). * Covers AC-12.1 → AC-12.4. */ import { test, expect } from '@playwright/test'; import { adminToken, createEngagement, deleteEngagement, deleteUserByUsername, ensureUser, login, makeClient, } from '../fixtures/api'; import { seedTokenInStorage } from '../fixtures/auth'; const REDTEAM_USER = 'us12-redteam'; const SOC_USER = 'us12-soc'; const PASS = 'us12-pass-strong'; interface Simulation { id: number; engagement_id: number; name: string; status: string; [key: string]: unknown; } async function createSimulation( token: string, engagementId: number, name = 'US-12 sim', ): Promise { const client = makeClient(token); const r = await client.post(`/engagements/${engagementId}/simulations`, { name }); if (r.status !== 201) { throw new Error(`create simulation failed: ${r.status} ${JSON.stringify(r.data)}`); } return r.data as Simulation; } test.describe('US-12 — simulation delete', () => { let redteamToken: string; let socToken: string; let engagementId: number; test.beforeAll(async () => { await ensureUser(REDTEAM_USER, PASS, 'redteam'); await ensureUser(SOC_USER, PASS, 'soc'); redteamToken = (await login(REDTEAM_USER, PASS)).token; socToken = (await login(SOC_USER, PASS)).token; const eng = await createEngagement(redteamToken, { name: 'US-12 Test Engagement', start_date: '2026-01-01', }); engagementId = eng.id; }); test.afterAll(async () => { try { const tok = await adminToken(); await deleteEngagement(tok, engagementId); for (const u of [REDTEAM_USER, SOC_USER]) { await deleteUserByUsername(tok, u); } } catch { /* noop */ } }); test('AC-12.1 — DELETE /api/simulations/ (redteam) → 204, then 404', async () => { const sim = await createSimulation(redteamToken, engagementId, 'AC-12.1 to delete'); const client = makeClient(redteamToken); const rDel = await client.delete(`/simulations/${sim.id}`); expect(rDel.status).toBe(204); const rGet = await client.get(`/simulations/${sim.id}`); expect(rGet.status).toBe(404); }); test('AC-12.2 — soc → 403 on DELETE', async () => { const sim = await createSimulation(redteamToken, engagementId, 'AC-12.2 soc blocked'); const socClient = makeClient(socToken); const r = await socClient.delete(`/simulations/${sim.id}`); expect(r.status).toBe(403); // Clean up const rtClient = makeClient(redteamToken); await rtClient.delete(`/simulations/${sim.id}`); }); test('AC-12.3 — cascade: deleting engagement deletes its simulations', async () => { const adminTok = await adminToken(); const adminClient = makeClient(adminTok); // Create a fresh engagement with simulations const eng = await createEngagement(redteamToken, { name: 'US-12 cascade test', start_date: '2026-01-01', }); const s1 = await createSimulation(redteamToken, eng.id, 'cascade sim 1'); const s2 = await createSimulation(redteamToken, eng.id, 'cascade sim 2'); // Delete the engagement await deleteEngagement(redteamToken, eng.id); // Simulations must be gone const rS1 = await adminClient.get(`/simulations/${s1.id}`); expect(rS1.status).toBe(404); const rS2 = await adminClient.get(`/simulations/${s2.id}`); expect(rS2.status).toBe(404); }); test('AC-12.4 — delete button visible for redteam, confirmation modal, deletes and redirects', async ({ page, context, }) => { const sim = await createSimulation(redteamToken, engagementId, 'AC-12.4 UI delete'); await seedTokenInStorage(context, redteamToken); await page.goto(`/engagements/${engagementId}/simulations/${sim.id}/edit`); // Delete button is visible for redteam const deleteBtn = page.getByRole('button', { name: /supprimer/i }); await expect(deleteBtn).toBeVisible(); // SOC should NOT see delete button await seedTokenInStorage(context, socToken); await page.goto(`/engagements/${engagementId}/simulations/${sim.id}/edit`); await expect(page.getByRole('button', { name: /supprimer/i })).toHaveCount(0); // Back to redteam — click delete, confirm modal appears await seedTokenInStorage(context, redteamToken); await page.goto(`/engagements/${engagementId}/simulations/${sim.id}/edit`); await page.getByRole('button', { name: /supprimer/i }).click(); // Confirmation dialog must appear const dialog = page.getByRole('dialog'); await expect(dialog).toBeVisible(); await expect(dialog.getByText(/supprimer la simulation/i)).toBeVisible(); // Confirm deletion await dialog.getByRole('button', { name: /supprimer/i }).click(); // Should navigate back to engagement detail await page.waitForURL(new RegExp(`/engagements/${engagementId}$`)); // Simulation no longer in list await expect(page.getByRole('row', { name: /AC-12.4 UI delete/i })).toHaveCount(0); }); });