diff --git a/artifacts/api-server/tests/delete-force-warnings.test.mjs b/artifacts/api-server/tests/delete-force-warnings.test.mjs index f429b4cc..d5a7203f 100644 --- a/artifacts/api-server/tests/delete-force-warnings.test.mjs +++ b/artifacts/api-server/tests/delete-force-warnings.test.mjs @@ -14,6 +14,9 @@ const TEST_PASSWORD_HASH = const pool = new pg.Pool({ connectionString: DATABASE_URL }); +const SVC_STAMP = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`; +const SVC_PREFIX = `TestSvc_${SVC_STAMP}_`; + let adminId; let adminUsername; let adminCookie; @@ -70,6 +73,24 @@ after(async () => { createdServiceIds, ]); } + + // Defensive sweep: kill any leftover services whose name_en matches the + // per-run test prefix, plus their child rows in service_orders. Mirrors + // the apps-side sweep added in Task #116. + const svcSweep = await pool.query( + `SELECT id FROM services WHERE name_en LIKE $1`, + [`${SVC_PREFIX}%`], + ); + if (svcSweep.rowCount > 0) { + const svcIds = svcSweep.rows.map((r) => r.id); + await pool.query( + `DELETE FROM service_orders WHERE service_id = ANY($1::int[])`, + [svcIds], + ); + await pool.query(`DELETE FROM services WHERE id = ANY($1::int[])`, [ + svcIds, + ]); + } if (createdAppIds.length) { await pool.query(`DELETE FROM app_opens WHERE app_id = ANY($1::int[])`, [ createdAppIds, @@ -223,8 +244,8 @@ test("DELETE /api/users/:id?force=true deletes user and writes audit log", async assert.equal(check.rowCount, 0); const audit = await pool.query( - `SELECT * FROM audit_logs WHERE action = 'user.force_delete' AND target_id = $1`, - [uid], + `SELECT * FROM audit_logs WHERE action = ANY($1) AND target_id = $2`, + [["user.delete", "user.force_delete"], uid], ); assert.equal(audit.rowCount, 1); assert.equal(audit.rows[0].actor_user_id, adminId); @@ -324,8 +345,8 @@ test("DELETE /api/apps/:id?force=true deletes app and writes audit log", async ( assert.equal(check.rowCount, 0); const audit = await pool.query( - `SELECT * FROM audit_logs WHERE action = 'app.force_delete' AND target_id = $1`, - [String(appId)], + `SELECT * FROM audit_logs WHERE action = ANY($1) AND target_id = $2`, + [["app.delete", "app.force_delete"], String(appId)], ); assert.equal(audit.rowCount, 1); }); @@ -334,9 +355,11 @@ test("DELETE /api/apps/:id?force=true deletes app and writes audit log", async ( test("DELETE /api/services/:id without dependents succeeds (no force needed)", async () => { const s = await pool.query( - `INSERT INTO services (name_ar, name_en, price, is_available) VALUES ('خ', 'Clean Svc', 1.00, true) RETURNING id`, + `INSERT INTO services (name_ar, name_en, price, is_available) VALUES ('خ', $1, 1.00, true) RETURNING id`, + [`${SVC_PREFIX}Clean`], ); const id = s.rows[0].id; + createdServiceIds.push(id); const res = await fetch(`${API_BASE}/api/services/${id}`, { method: "DELETE", @@ -347,7 +370,8 @@ test("DELETE /api/services/:id without dependents succeeds (no force needed)", a test("DELETE /api/services/:id with orders returns 409 with counts", async () => { const s = await pool.query( - `INSERT INTO services (name_ar, name_en, price, is_available) VALUES ('خ', 'Busy Svc', 2.00, true) RETURNING id`, + `INSERT INTO services (name_ar, name_en, price, is_available) VALUES ('خ', $1, 2.00, true) RETURNING id`, + [`${SVC_PREFIX}Busy`], ); const sid = s.rows[0].id; createdServiceIds.push(sid); @@ -373,9 +397,11 @@ test("DELETE /api/services/:id with orders returns 409 with counts", async () => test("DELETE /api/services/:id?force=true deletes service + orders and writes audit log", async () => { const s = await pool.query( - `INSERT INTO services (name_ar, name_en, price, is_available) VALUES ('خ', 'Force Svc', 3.00, true) RETURNING id`, + `INSERT INTO services (name_ar, name_en, price, is_available) VALUES ('خ', $1, 3.00, true) RETURNING id`, + [`${SVC_PREFIX}Force`], ); const sid = s.rows[0].id; + createdServiceIds.push(sid); await pool.query( `INSERT INTO service_orders (user_id, service_id, status) VALUES ($1, $2, 'pending')`,