em->getConnection()->insert('app_log', [ 'level' => $level, 'message' => $message, 'channel' => 'app', 'path' => '/test', 'created_at' => time(), ]); } public function testNonAdminForbidden(): void { $user = $this->createUser(['ROLE_USER']); $this->authJson('GET', '/api/v1/admin/logs', $user); self::assertSame(403, $this->responseCode()); } public function testAdminSeesLogsFilteredByLevel(): void { $marker = 'ADMINLOGTEST_' . bin2hex(random_bytes(5)); $this->seedLog('error', $marker . '_err'); $this->seedLog('warning', $marker . '_warn'); $admin = $this->createUser(['ROLE_ADMIN']); $body = $this->authJson('GET', '/api/v1/admin/logs?level=error&search=' . $marker, $admin); self::assertSame(200, $this->responseCode()); self::assertTrue($body['success']); self::assertCount(1, $body['data']); self::assertSame('error', $body['data'][0]['level']); self::assertSame($marker . '_err', $body['data'][0]['message']); self::assertArrayHasKey('totalRecords', $body['meta']); $this->em->getConnection()->executeStatement('DELETE FROM app_log WHERE message LIKE ?', [$marker . '%']); } public function testExportNonAdminForbidden(): void { $user = $this->createUser(['ROLE_USER']); $this->authJson('GET', '/api/v1/admin/logs/export', $user); self::assertSame(403, $this->responseCode()); } public function testAdminExportsCsvFilteredByLevel(): void { $marker = 'ADMINLOGEXPORT_' . bin2hex(random_bytes(5)); $this->seedLog('error', $marker . '_err'); $this->seedLog('warning', $marker . '_warn'); $admin = $this->createUser(['ROLE_ADMIN']); $this->client->request( 'GET', '/api/v1/admin/logs/export?level=error&search=' . $marker, server: ['HTTP_AUTHORIZATION' => 'Bearer ' . $this->jwtFor($admin)], ); $response = $this->client->getResponse(); self::assertSame(200, $response->getStatusCode()); self::assertStringContainsString('text/csv', (string) $response->headers->get('Content-Type')); self::assertStringContainsString('attachment', (string) $response->headers->get('Content-Disposition')); // StreamedResponse body is captured by BrowserKit into the internal response. $csv = $this->client->getInternalResponse()->getContent(); self::assertStringContainsString('id,level,message', $csv); self::assertStringContainsString($marker . '_err', $csv); self::assertStringNotContainsString($marker . '_warn', $csv); $this->em->getConnection()->executeStatement('DELETE FROM app_log WHERE message LIKE ?', [$marker . '%']); } }