Refactor health check DTOs around a common CheckResult contract and remove the\nlegacy HealthChecks aggregate object.\n\nAdd a dedicated /api/v1/health/oracle endpoint with response mapping, OpenAPI\nschema updates, and Bruno request coverage.\n\nAlign feature, unit, integration, architecture and OpenAPI tests with the new\nset-based checks representation and make Oracle health tests deterministic in CI.\n\nUpdate command documentation to the uwil:* command namespace. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
111 lines
4 KiB
PHP
111 lines
4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Console\Commands;
|
|
|
|
use Illuminate\Support\Facades\File;
|
|
use Infra\Database\Models\ClientAuditLog;
|
|
use Presentation\Console\Commands\ExportSecurityAuditLogsCommand;
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
covers(ExportSecurityAuditLogsCommand::class);
|
|
|
|
describe('ExportSecurityAuditLogsCommand', function (): void {
|
|
beforeEach(function (): void {
|
|
ClientAuditLog::query()->delete();
|
|
File::deleteDirectory(storage_path('app/audit'));
|
|
});
|
|
|
|
it('displays security audit logs in console table format', function (): void {
|
|
// GIVEN
|
|
/** @var \Tests\TestCase $this */
|
|
$clientId = Uuid::uuid7()->toString();
|
|
$clientIdentifier = 'arcadia_id_' . str_repeat('g', 32);
|
|
$logId = Uuid::uuid7(new \DateTimeImmutable('2026-08-20 10:00:00 UTC'));
|
|
ClientAuditLog::query()->create([
|
|
'id' => $logId->toString(),
|
|
'client_id' => $clientId,
|
|
'client_identifier' => $clientIdentifier,
|
|
'event_type' => 'client_secret_generated',
|
|
'details' => null,
|
|
]);
|
|
|
|
// WHEN
|
|
$this->artisan('uwil:arcadia:security:audit-logs', [
|
|
'--since' => '2026-08-01',
|
|
'--to' => '2026-08-21 23:59:59',
|
|
])
|
|
// THEN
|
|
->expectsTable(
|
|
['Date (UTC)', 'Event', 'Client Identifier', 'Client ID (UUID)', 'Details'],
|
|
[
|
|
[
|
|
'2026-08-20 10:00:00',
|
|
'client_secret_generated',
|
|
$clientIdentifier,
|
|
$clientId,
|
|
'',
|
|
],
|
|
]
|
|
)
|
|
->expectsOutput('Total: 1 event(s) found.')
|
|
->assertSuccessful();
|
|
});
|
|
|
|
it('displays notice when no logs are found in the specified range', function (): void {
|
|
// GIVEN
|
|
/** @var \Tests\TestCase $this */
|
|
|
|
// WHEN
|
|
$this->artisan('uwil:arcadia:security:audit-logs', [
|
|
'--since' => '2026-08-01',
|
|
'--to' => '2026-08-02',
|
|
])
|
|
// THEN
|
|
->expectsOutputToContain('No security audit logs found')
|
|
->assertSuccessful();
|
|
});
|
|
|
|
it('exports logs to CSV in storage directory when --csv is specified', function (): void {
|
|
// GIVEN
|
|
/** @var \Tests\TestCase $this */
|
|
$clientId = Uuid::uuid7()->toString();
|
|
$clientIdentifier = 'arcadia_id_' . str_repeat('e', 32);
|
|
$logId = Uuid::uuid7(new \DateTimeImmutable('2026-08-18 15:30:00 UTC'));
|
|
ClientAuditLog::query()->create([
|
|
'id' => $logId->toString(),
|
|
'client_id' => $clientId,
|
|
'client_identifier' => $clientIdentifier,
|
|
'event_type' => 'client_status_toggled',
|
|
'details' => ['is_active' => 'false'],
|
|
]);
|
|
|
|
// WHEN
|
|
$this->artisan('uwil:arcadia:security:audit-logs', [
|
|
'--since' => '2026-08-01',
|
|
'--csv' => true,
|
|
])
|
|
// THEN
|
|
->expectsOutputToContain('Exported 1 security audit log(s) to')
|
|
->assertSuccessful();
|
|
|
|
$files = File::files(storage_path('app/audit'));
|
|
expect($files)->toHaveCount(1);
|
|
|
|
$csvContent = File::get($files[0]->getPathname());
|
|
expect($csvContent)->toContain($clientIdentifier)
|
|
->and($csvContent)->toContain('client_status_toggled')
|
|
->and($csvContent)->toContain('is_active');
|
|
});
|
|
|
|
it('fails when an invalid date format is provided for --since', function (): void {
|
|
// GIVEN & WHEN & THEN
|
|
/** @var \Tests\TestCase $this */
|
|
$this->artisan('uwil:arcadia:security:audit-logs', [
|
|
'--since' => 'invalid-date-string',
|
|
])
|
|
->expectsOutputToContain("Invalid date format for --since: 'invalid-date-string'.")
|
|
->assertFailed();
|
|
});
|
|
});
|