Root cause:
Four apps named "تطبيق" (App Clean / App Force, ids 326, 328, 352, 354)
were leftover test fixtures from
artifacts/api-server/tests/delete-force-warnings.test.mjs. The "delete
clean" and "delete force" app tests inserted apps directly via SQL but
never pushed the new id to `createdAppIds` — they relied on the API
DELETE call itself to remove them. When the api-server was down (and
it was, due to a port conflict on 8080), those rows were never cleaned
up, the after() hook couldn't see them, and they appeared on every
user's home screen as 4 generic placeholder icons. Two failed test
runs at 06:29 and 06:51 on 2026-04-28 left exactly 4 rows behind.
T1 — One-shot DB cleanup (via executeSql):
- DELETE FROM app_opens WHERE app_id IN (326,328,352,354);
- DELETE FROM app_permissions WHERE app_id IN (326,328,352,354);
- DELETE FROM group_apps WHERE app_id IN (326,328,352,354);
- DELETE FROM user_app_orders WHERE app_id IN (326,328,352,354);
- DELETE FROM apps WHERE id IN (326,328,352,354);
Verified: SELECT count(*) FROM apps WHERE name_ar='تطبيق' returns 0.
GET /api/apps now returns only the 8 real apps.
T2 — Hardened the test so this can't recur:
1. Added `createdAppIds.push(id)` immediately after each app INSERT
in the "clean" and "force" cases (the "busy" case already had it).
2. Added cleanup of `user_app_orders` to the existing tracked-id
cleanup (the original `after()` was missing this child table).
3. Added a defensive sweep in the after() hook that, after the
tracked-id cleanup, deletes any apps whose slug matches the test
prefixes (clean_app_%, busy_app_%, force_app_%) plus their child
rows in app_opens, app_permissions, group_apps, user_app_orders.
This guards against any future failure that crashes a test
between INSERT and the push.
Verification:
- Re-ran the test file: name_ar='تطبيق' count = 0 after run, even
with several unrelated pre-existing test failures still present
(force-delete users/apps assertions broken by Task #93's audit
action rename, services duplicate-key issues — both filed
separately and explicitly out of scope here).
- Architect review: PASS (idempotent, safe re-runs, addresses real
missing FK cleanup path).
Out of scope (filed as follow-up):
- Restoring the broken force-delete audit-action assertions that
Task #93 renamed (user.force_delete → user.delete, app.force_delete
→ app.delete, group.force_delete → group.delete) and the missing
per-test unique suffix on services in the same file.