['auth', 'login', 'frontend'], 'identity-privacy' => ['password', 'verify', 'profile', 'me'], 'bulk-assignment' => ['assign', 'bulk', 'batch', 'class'], 'print-export-tamper' => ['print', 'export', 'download', 'certificate'], 'audit-tamper' => ['audit', 'log', 'activity'], 'finance-reconcile' => ['finance', 'payment', 'refund', 'ledger'], 'academic-rollback' => ['promotion', 'school-year', 'reopen'], 'kiosk-device' => ['attendance', 'scanner', 'badge'], 'notification-template' => ['notification', 'template', 'broadcast'], 'storage-signed-url' => ['file', 'media', 'signed', 'upload'], ]; foreach ($riskFamilies as $name => $needles) { $this->assertNotEmpty($this->apiRoutesContainingAny($needles), 'Batch 14 expected route coverage for ' . $name); } } public function test_batch14_high_risk_mutation_families_exist(): void { foreach (['users', 'students', 'attendance', 'finance', 'payment', 'roles', 'permissions', 'settings', 'messages', 'inventory'] as $family) { $mutations = array_filter($this->apiRoutesContainingAny([$family]), function ($route): bool { return in_array($this->primaryMethod($route), ['POST', 'PUT', 'PATCH', 'DELETE'], true); }); $this->assertNotEmpty($mutations, 'Batch 14 expected mutating route family: ' . $family); } } }