1
0

AuditLogControllerTest.php 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Tests\Integration\Admin;
  4. use App\Domain\Auth\Role;
  5. use App\Domain\Auth\TokenKind;
  6. use App\Tests\Integration\Support\AppTestCase;
  7. /**
  8. * `/api/v1/admin/audit-log` end-to-end. Seeds rows directly so the
  9. * filter coverage doesn't depend on emission timing.
  10. */
  11. final class AuditLogControllerTest extends AppTestCase
  12. {
  13. public function testListEmpty(): void
  14. {
  15. $token = $this->createToken(TokenKind::Admin, Role::Viewer);
  16. $resp = $this->request('GET', '/api/v1/admin/audit-log', ['Authorization' => 'Bearer ' . $token]);
  17. self::assertSame(200, $resp->getStatusCode());
  18. $body = $this->decode($resp);
  19. self::assertSame([], $body['items']);
  20. self::assertSame(0, $body['total']);
  21. }
  22. public function testListWithFilters(): void
  23. {
  24. $now = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
  25. $this->seedAudit('user', '7', 'manual_block.created', 'manual_block', '1', '{"ip":"1.1.1.1"}', $now);
  26. $this->seedAudit('admin-token', '3', 'category.created', 'category', '2', '{"slug":"x"}', $now);
  27. $this->seedAudit('user', '7', 'allowlist.created', 'allowlist', '5', '{"ip":"2.2.2.2"}', $now);
  28. $token = $this->createToken(TokenKind::Admin, Role::Viewer);
  29. // Filter by actor_kind=user
  30. $resp = $this->request('GET', '/api/v1/admin/audit-log?actor_kind=user', ['Authorization' => 'Bearer ' . $token]);
  31. $body = $this->decode($resp);
  32. self::assertSame(2, $body['total']);
  33. foreach ($body['items'] as $item) {
  34. self::assertSame('user', $item['actor_kind']);
  35. }
  36. // Filter by action
  37. $resp = $this->request('GET', '/api/v1/admin/audit-log?action=category.created', ['Authorization' => 'Bearer ' . $token]);
  38. $body = $this->decode($resp);
  39. self::assertSame(1, $body['total']);
  40. self::assertSame('category.created', $body['items'][0]['action']);
  41. self::assertSame(['slug' => 'x'], $body['items'][0]['details']);
  42. // Filter by entity_type=manual_block
  43. $resp = $this->request('GET', '/api/v1/admin/audit-log?entity_type=manual_block', ['Authorization' => 'Bearer ' . $token]);
  44. $body = $this->decode($resp);
  45. self::assertSame(1, $body['total']);
  46. self::assertSame('manual_block', $body['items'][0]['entity_type']);
  47. }
  48. public function testInvalidActorKindReturns400(): void
  49. {
  50. $token = $this->createToken(TokenKind::Admin, Role::Viewer);
  51. $resp = $this->request('GET', '/api/v1/admin/audit-log?actor_kind=potato', ['Authorization' => 'Bearer ' . $token]);
  52. self::assertSame(400, $resp->getStatusCode());
  53. }
  54. public function testRequiresViewer(): void
  55. {
  56. $resp = $this->request('GET', '/api/v1/admin/audit-log');
  57. self::assertSame(401, $resp->getStatusCode());
  58. }
  59. private function seedAudit(string $kind, ?string $actorId, string $action, string $type, string $id, string $details, string $when): void
  60. {
  61. $this->db->insert('audit_log', [
  62. 'actor_kind' => $kind,
  63. 'actor_id' => $actorId,
  64. 'action' => $action,
  65. 'target_type' => $type,
  66. 'target_id' => $id,
  67. 'details_json' => $details,
  68. 'ip_address' => null,
  69. 'created_at' => $when,
  70. ]);
  71. }
  72. }