createToken(TokenKind::Admin, Role::Viewer); $resp = $this->request('GET', '/api/v1/admin/audit-log', ['Authorization' => 'Bearer ' . $token]); self::assertSame(200, $resp->getStatusCode()); $body = $this->decode($resp); self::assertSame([], $body['items']); self::assertSame(0, $body['total']); } public function testListWithFilters(): void { $now = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s'); $this->seedAudit('user', '7', 'manual_block.created', 'manual_block', '1', '{"ip":"1.1.1.1"}', $now); $this->seedAudit('admin-token', '3', 'category.created', 'category', '2', '{"slug":"x"}', $now); $this->seedAudit('user', '7', 'allowlist.created', 'allowlist', '5', '{"ip":"2.2.2.2"}', $now); $token = $this->createToken(TokenKind::Admin, Role::Viewer); // Filter by actor_kind=user $resp = $this->request('GET', '/api/v1/admin/audit-log?actor_kind=user', ['Authorization' => 'Bearer ' . $token]); $body = $this->decode($resp); self::assertSame(2, $body['total']); foreach ($body['items'] as $item) { self::assertSame('user', $item['actor_kind']); } // Filter by action $resp = $this->request('GET', '/api/v1/admin/audit-log?action=category.created', ['Authorization' => 'Bearer ' . $token]); $body = $this->decode($resp); self::assertSame(1, $body['total']); self::assertSame('category.created', $body['items'][0]['action']); self::assertSame(['slug' => 'x'], $body['items'][0]['details']); // Filter by entity_type=manual_block $resp = $this->request('GET', '/api/v1/admin/audit-log?entity_type=manual_block', ['Authorization' => 'Bearer ' . $token]); $body = $this->decode($resp); self::assertSame(1, $body['total']); self::assertSame('manual_block', $body['items'][0]['entity_type']); } public function testInvalidActorKindReturns400(): void { $token = $this->createToken(TokenKind::Admin, Role::Viewer); $resp = $this->request('GET', '/api/v1/admin/audit-log?actor_kind=potato', ['Authorization' => 'Bearer ' . $token]); self::assertSame(400, $resp->getStatusCode()); } public function testRequiresViewer(): void { $resp = $this->request('GET', '/api/v1/admin/audit-log'); self::assertSame(401, $resp->getStatusCode()); } private function seedAudit(string $kind, ?string $actorId, string $action, string $type, string $id, string $details, string $when): void { $this->db->insert('audit_log', [ 'actor_kind' => $kind, 'actor_id' => $actorId, 'action' => $action, 'target_type' => $type, 'target_id' => $id, 'details_json' => $details, 'ip_address' => null, 'created_at' => $when, ]); } }