Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 3m58s
Applied diagnostic surface contract rules to Audit Log inspect modal and Support Diagnostics action context, consolidating raw diagnostic data into safe modals according to Spec 374.
328 lines
12 KiB
PHP
328 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Monitoring\AuditLog as AuditLogPage;
|
|
use App\Models\AuditLog as AuditLogModel;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Livewire\Features\SupportTesting\Testable;
|
|
use Livewire\Livewire;
|
|
|
|
function auditLogPageTestComponent(User $user, ?ManagedEnvironment $tenant = null): Testable
|
|
{
|
|
test()->actingAs($user);
|
|
Filament::setTenant($tenant, true);
|
|
|
|
return Livewire::withHeaders(['referer' => route('admin.monitoring.audit-log')])
|
|
->actingAs($user)
|
|
->test(AuditLogPage::class);
|
|
}
|
|
|
|
function auditLogPageTestRecord(?ManagedEnvironment $tenant, array $attributes = []): AuditLogModel
|
|
{
|
|
$workspaceId = array_key_exists('workspace_id', $attributes)
|
|
? (int) $attributes['workspace_id']
|
|
: (int) ($tenant?->workspace_id ?? Workspace::factory()->create()->getKey());
|
|
|
|
return AuditLogModel::query()->create(array_merge([
|
|
'workspace_id' => $workspaceId,
|
|
'managed_environment_id' => $tenant?->getKey(),
|
|
'actor_email' => 'auditor@example.com',
|
|
'actor_name' => 'Audit Operator',
|
|
'action' => 'operation.completed',
|
|
'status' => 'success',
|
|
'resource_type' => 'operation_run',
|
|
'resource_id' => '1',
|
|
'summary' => 'Operation completed',
|
|
'metadata' => [],
|
|
'recorded_at' => now(),
|
|
], $attributes));
|
|
}
|
|
|
|
it('renders the canonical audit route as an event-proof-first monitoring surface', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.monitoring.audit-log'))
|
|
->assertOk()
|
|
->assertSee('Which event proves what happened?')
|
|
->assertSee('Audit proof workbench')
|
|
->assertSee('Workspace-wide audit disclosure across authorized environment and workspace events.');
|
|
});
|
|
|
|
it('keeps preselected audit detail inside the event-proof-first route', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$audit = auditLogPageTestRecord($tenant, [
|
|
'summary' => 'Preselected audit detail',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.monitoring.audit-log', ['event' => (int) $audit->getKey()]))
|
|
->assertOk()
|
|
->assertSee('Which event proves what happened?')
|
|
->assertSee('Selected event proof')
|
|
->assertSee('Preselected audit detail')
|
|
->assertSee('data-testid="audit-focus-proof-cards"', false)
|
|
->assertSee('break-words', false)
|
|
->assertDontSee('Focused review lane');
|
|
});
|
|
|
|
it('loads the audit page with populated filter options', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
auditLogPageTestRecord($tenant, [
|
|
'action' => 'verification.completed',
|
|
'actor_name' => 'Ahmed Darrazi',
|
|
'resource_type' => 'tenant',
|
|
'resource_id' => (string) $tenant->getKey(),
|
|
'summary' => 'Verification completed',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.monitoring.audit-log'))
|
|
->assertOk()
|
|
->assertSee('Event type')
|
|
->assertSee('Actor')
|
|
->assertSee('Target type');
|
|
});
|
|
|
|
it('renders derived labels for audit rows that are missing evolved snapshot columns', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
DB::table('audit_logs')->insert([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'actor_id' => null,
|
|
'actor_email' => 'spo_admin@yptw2.onmicrosoft.com',
|
|
'actor_name' => 'Ahmed Darrazi',
|
|
'action' => 'baseline.capture.started',
|
|
'resource_type' => 'baseline_profile',
|
|
'resource_id' => '2',
|
|
'status' => 'success',
|
|
'metadata' => json_encode([
|
|
'baseline_profile_id' => 2,
|
|
'baseline_profile_name' => 'Device Compliance',
|
|
], JSON_THROW_ON_ERROR),
|
|
'recorded_at' => now(),
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.monitoring.audit-log'))
|
|
->assertOk()
|
|
->assertSee('Baseline capture started for Baseline profile #2')
|
|
->assertSee('Ahmed Darrazi')
|
|
->assertSee('Baseline profile #2')
|
|
->assertSee('Success');
|
|
});
|
|
|
|
it('renders workspace setting targets with readable labels', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
auditLogPageTestRecord($tenant, [
|
|
'action' => 'workspace_setting.updated',
|
|
'summary' => 'Workspace setting updated for backup.retention_keep_last_default',
|
|
'resource_type' => 'workspace_setting',
|
|
'resource_id' => 'backup.retention_keep_last_default',
|
|
'target_label' => 'backup.retention_keep_last_default',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.monitoring.audit-log'))
|
|
->assertOk()
|
|
->assertSee('Workspace setting updated for Backup Retention Keep Last Default')
|
|
->assertSee('Backup Retention Keep Last Default');
|
|
});
|
|
|
|
it('shows reverse-chronological audit rows and supports summary search', function (): void {
|
|
[$user, $tenantA] = createUserWithTenant(role: 'owner');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
]);
|
|
|
|
createUserWithTenant($tenantB, $user, role: 'owner');
|
|
|
|
$oldest = auditLogPageTestRecord($tenantA, [
|
|
'resource_id' => '101',
|
|
'summary' => 'Older baseline capture completed',
|
|
'action' => 'baseline.capture.completed',
|
|
'recorded_at' => now()->subMinutes(15),
|
|
]);
|
|
|
|
$middle = auditLogPageTestRecord($tenantB, [
|
|
'resource_id' => '102',
|
|
'summary' => 'Rotate backup schedule credentials',
|
|
'action' => 'backup.updated',
|
|
'recorded_at' => now()->subMinutes(5),
|
|
]);
|
|
|
|
$newest = auditLogPageTestRecord($tenantA, [
|
|
'resource_id' => '103',
|
|
'summary' => 'Newest restore failed',
|
|
'action' => 'restore.failed',
|
|
'status' => 'failed',
|
|
'recorded_at' => now()->subMinute(),
|
|
]);
|
|
|
|
auditLogPageTestComponent($user)
|
|
->assertOk()
|
|
->assertCanSeeTableRecords([$newest, $middle, $oldest], inOrder: true)
|
|
->searchTable('Rotate')
|
|
->assertCanSeeTableRecords([$middle])
|
|
->assertCanNotSeeTableRecords([$newest, $oldest]);
|
|
});
|
|
|
|
it('keeps the audit log unfiltered when an active tenant context exists', function (): void {
|
|
[$user, $tenantA] = createUserWithTenant(role: 'owner');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
]);
|
|
|
|
createUserWithTenant($tenantB, $user, role: 'owner');
|
|
|
|
$tenantARecord = auditLogPageTestRecord($tenantA, [
|
|
'resource_id' => '201',
|
|
'summary' => 'ManagedEnvironment A verification completed',
|
|
'action' => 'verification.completed',
|
|
]);
|
|
|
|
$tenantBRecord = auditLogPageTestRecord($tenantB, [
|
|
'resource_id' => '202',
|
|
'summary' => 'ManagedEnvironment B verification completed',
|
|
'action' => 'verification.completed',
|
|
]);
|
|
|
|
auditLogPageTestComponent($user, $tenantA)
|
|
->assertSet('tableFilters.managed_environment_id.value', null)
|
|
->assertCanSeeTableRecords([$tenantARecord, $tenantBRecord]);
|
|
});
|
|
|
|
it('keeps the audit log unfiltered when only a remembered environment context exists', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create([
|
|
'name' => 'Phoenicon',
|
|
'environment' => 'dev',
|
|
]);
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'YPTW2',
|
|
'environment' => 'dev',
|
|
]);
|
|
|
|
createUserWithTenant($tenantB, $user, role: 'owner');
|
|
|
|
$tenantARecord = auditLogPageTestRecord($tenantA, [
|
|
'resource_id' => '301',
|
|
'summary' => 'Phoenicon verification completed',
|
|
'action' => 'verification.completed',
|
|
]);
|
|
|
|
$tenantBRecord = auditLogPageTestRecord($tenantB, [
|
|
'resource_id' => '302',
|
|
'summary' => 'YPTW2 verification completed',
|
|
'action' => 'verification.completed',
|
|
]);
|
|
|
|
$this->actingAs($user);
|
|
Filament::setTenant(null, true);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
|
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
|
(string) $tenantA->workspace_id => (int) $tenantA->getKey(),
|
|
]);
|
|
|
|
Livewire::withHeaders(['referer' => route('admin.monitoring.audit-log')])
|
|
->actingAs($user)
|
|
->test(AuditLogPage::class)
|
|
->assertSet('tableFilters.managed_environment_id.value', null)
|
|
->assertCanSeeTableRecords([$tenantARecord, $tenantBRecord]);
|
|
});
|
|
|
|
it('clears a stale persisted audit tenant filter when the workspace shell is tenantless', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create([
|
|
'name' => 'YPTW2',
|
|
'environment' => 'dev',
|
|
]);
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'name' => 'Phoenicon',
|
|
'environment' => 'dev',
|
|
]);
|
|
|
|
createUserWithTenant($tenantB, $user, role: 'owner');
|
|
|
|
$tenantARecord = auditLogPageTestRecord($tenantA, [
|
|
'resource_id' => '401',
|
|
'summary' => 'YPTW2 verification completed',
|
|
]);
|
|
|
|
$tenantBRecord = auditLogPageTestRecord($tenantB, [
|
|
'resource_id' => '402',
|
|
'summary' => 'Phoenicon verification completed',
|
|
]);
|
|
|
|
$this->actingAs($user);
|
|
Filament::setTenant(null, true);
|
|
|
|
$workspaceId = (int) $tenantA->workspace_id;
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
|
|
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
|
(string) $workspaceId => (int) $tenantA->getKey(),
|
|
]);
|
|
|
|
$component = Livewire::withHeaders(['referer' => route('admin.monitoring.audit-log')])
|
|
->actingAs($user)
|
|
->test(AuditLogPage::class)
|
|
->assertSet('tableFilters.managed_environment_id.value', null)
|
|
->assertCanSeeTableRecords([$tenantARecord, $tenantBRecord]);
|
|
|
|
expect(data_get(session()->get($component->instance()->getTableFiltersSessionKey()), 'managed_environment_id.value'))
|
|
->toBeNull();
|
|
|
|
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
|
(string) $workspaceId => (int) $tenantB->getKey(),
|
|
]);
|
|
|
|
Livewire::withHeaders(['referer' => route('admin.monitoring.audit-log')])
|
|
->actingAs($user)
|
|
->test(AuditLogPage::class)
|
|
->assertSet('tableFilters.managed_environment_id.value', null)
|
|
->assertCanSeeTableRecords([$tenantARecord, $tenantBRecord]);
|
|
});
|
|
|
|
it('shows a clear-filters empty state when no audit rows match the current view', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$record = auditLogPageTestRecord($tenant, [
|
|
'summary' => 'Baseline compare completed',
|
|
'action' => 'baseline.compare.completed',
|
|
]);
|
|
|
|
auditLogPageTestComponent($user)
|
|
->assertTableEmptyStateActionsExistInOrder(['clear_filters'])
|
|
->searchTable('no-such-audit-event')
|
|
->assertCountTableRecords(0)
|
|
->assertSee('No audit events match this view')
|
|
->assertSee('Clear filters')
|
|
->searchTable(null)
|
|
->assertCanSeeTableRecords([$record]);
|
|
});
|