$role, 'guard_name' => 'web']); } } private function createUser(string $role): User { $user = User::factory()->create(); $user->assignRole($role); return $user; } // ── STATISTIK ──────────────────────────────────────────────── public function test_admin_can_get_stats(): void { $admin = $this->createUser('administrator'); $this->actingAs($admin); ActivityLog::record('login', 'Test'); $res = $this->getJson('/api/admin/stats')->assertOk(); $res->assertJsonStructure(['logins_today', 'logins_week', 'logins_month', 'actions_today', 'by_type', 'top_users', 'daily']); $this->assertGreaterThanOrEqual(1, $res->json('actions_today')); } public function test_non_admin_cannot_get_stats(): void { $this->actingAs($this->createUser('petugas_pendataan')); $this->getJson('/api/admin/stats')->assertStatus(403); } // ── NOTIFIKASI ──────────────────────────────────────────────── public function test_authenticated_user_can_poll_notifications(): void { $admin = $this->createUser('administrator'); $this->actingAs($admin); RumahIbadah::create(['user_id' => $admin->id, 'nama' => 'Test', 'latitude' => -7, 'longitude' => 110, 'radius' => 500]); $since = now()->subMinutes(5)->toISOString(); $res = $this->getJson('/api/notifications?since=' . urlencode($since))->assertOk(); $res->assertJsonStructure(['new_ibadah', 'new_miskin', 'latest_ibadah', 'latest_miskin']); $this->assertEquals(1, $res->json('new_ibadah')); } public function test_unauthenticated_cannot_poll_notifications(): void { $this->get('/api/notifications')->assertRedirect('/login'); } // ── BACKUP LIST ─────────────────────────────────────────────── public function test_admin_can_list_backups(): void { $this->actingAs($this->createUser('administrator')); $this->getJson('/api/admin/backup/list')->assertOk()->assertJsonIsArray(); } public function test_non_admin_cannot_list_backups(): void { $this->actingAs($this->createUser('pengurus_ibadah')); $this->getJson('/api/admin/backup/list')->assertStatus(403); } // ── RESTORE ─────────────────────────────────────────────────── public function test_restore_requires_confirm_word(): void { $this->actingAs($this->createUser('administrator')); $file = UploadedFile::fake()->createWithContent('backup.sql', '-- test sql'); $this->post('/api/admin/restore', ['file' => $file, 'confirm' => 'WRONG'])->assertStatus(302); } public function test_non_admin_cannot_restore(): void { $this->actingAs($this->createUser('petugas_pendataan')); $file = UploadedFile::fake()->createWithContent('backup.sql', '-- test'); $this->post('/api/admin/restore', ['file' => $file, 'confirm' => 'RESTORE'])->assertStatus(403); } public function test_unauthenticated_cannot_restore(): void { $file = UploadedFile::fake()->createWithContent('backup.sql', '-- test'); $this->post('/api/admin/restore', ['file' => $file, 'confirm' => 'RESTORE'])->assertRedirect('/login'); } // ── BACKUP COMMAND ──────────────────────────────────────────── public function test_backup_database_command_runs_successfully(): void { $this->artisan('backup:database')->assertSuccessful(); $files = glob(storage_path('app/backups/backup-webgis-*.sql')); $this->assertNotEmpty($files); foreach ($files as $f) unlink($f); } }