TenantAtlas/apps/platform/tests/Browser/Spec198MonitoringPageStateSmokeTest.php
ahmido e02799b383 feat: implement spec 198 monitoring page state contract (#238)
## Summary
- implement Spec 198 monitoring page-state contracts across Operations, Audit Log, Finding Exceptions Queue, Evidence Overview, Baseline Compare Landing, and Baseline Compare Matrix
- align selected-record and draft/apply behavior with query/session restoration semantics, including canonical navigation and tenant-filter normalization helpers
- add Spec 198 feature and browser coverage, update closure/spec artifacts, and refresh affected regression tests that asserted pre-contract behavior

## Verification
- focused Spec 198 feature pack passed through Sail
- Spec 198 browser smoke passed through Sail
- existing Spec 190 and Spec 194 browser smokes passed through Sail
- targeted fallout tests were updated and rerun during full-suite triage

## Notes
- Livewire v4 / Filament v5 compliant only; no legacy API reintroduction
- no provider registration changes; Laravel 11+ provider registration remains in `bootstrap/providers.php`
- no global-search behavior changed for any resource
- destructive queue decision actions remain confirmation-gated and authorization-backed
- no new Filament assets were added; existing deploy step for `php artisan filament:assets` remains unchanged

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #238
2026-04-15 21:59:42 +00:00

176 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\BaselineCompareLanding;
use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
use App\Filament\Resources\BaselineProfileResource;
use App\Models\AuditLog;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
pest()->browser()->timeout(20_000);
it('smokes monitoring deeplinks for operations, audit log, finding exceptions queue, and evidence overview', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$secondTenant = Tenant::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Second Evidence Tenant',
]);
createUserWithTenant(tenant: $secondTenant, user: $user, role: 'owner', workspaceRole: 'manager');
$activeRun = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'type' => 'inventory_sync',
'status' => OperationRunStatus::Running->value,
'outcome' => OperationRunOutcome::Pending->value,
'created_at' => now()->subMinute(),
'started_at' => now()->subMinute(),
]);
$audit = AuditLog::query()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'actor_email' => 'owner@example.com',
'actor_name' => 'Owner',
'actor_type' => 'human',
'action' => 'workspace.selected',
'status' => 'success',
'resource_type' => 'workspace',
'resource_id' => (string) $tenant->workspace_id,
'target_label' => 'Workspace '.$tenant->workspace_id,
'summary' => 'Workspace selected for Workspace '.$tenant->workspace_id,
'recorded_at' => now(),
]);
$finding = Finding::factory()->for($tenant)->create();
$exception = FindingException::query()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'finding_id' => (int) $finding->getKey(),
'requested_by_user_id' => (int) $user->getKey(),
'owner_user_id' => (int) $user->getKey(),
'status' => FindingException::STATUS_PENDING,
'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
'request_reason' => 'Spec198 browser queue smoke.',
'requested_at' => now()->subDay(),
'review_due_at' => now()->addDay(),
'evidence_summary' => ['reference_count' => 0],
]);
foreach ([$tenant, $secondTenant] as $snapshotTenant) {
EvidenceSnapshot::query()->create([
'tenant_id' => (int) $snapshotTenant->getKey(),
'workspace_id' => (int) $snapshotTenant->workspace_id,
'status' => EvidenceSnapshotStatus::Active->value,
'completeness_state' => EvidenceCompletenessState::Complete->value,
'summary' => ['missing_dimensions' => 0, 'stale_dimensions' => 0],
'generated_at' => now(),
]);
}
$this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(),
],
]);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
visit(route('admin.operations.index', [
'tenant_id' => (int) $tenant->getKey(),
'activeTab' => 'active',
]))
->waitForText('Monitoring landing')
->assertSee('Tenant prefilters and the selected operations tab remain shareable through the URL.')
->assertSee('Open run detail')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
visit(route('admin.monitoring.audit-log', ['event' => (int) $audit->getKey()]))
->waitForText('Summary-first audit history')
->assertSee('Close details')
->assertSee('Readable context')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
visit(FindingExceptionsQueue::getUrl(panel: 'admin', parameters: ['exception' => (int) $exception->getKey()]))
->waitForText('Focused review lane')
->assertSee('Approve exception')
->assertSee('Reject exception')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
visit(route('admin.evidence.overview', [
'tenant_id' => (int) $tenant->getKey(),
'search' => $tenant->name,
]))
->waitForText('Tenant and search query seeds can reopen this overview in a specific monitoring slice.')
->assertSee($tenant->name)
->assertSee('Clear filters')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
});
it('smokes compare landing to compare matrix handoff with carried subject focus', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$subjectKey = 'wifi-corp-profile';
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec198 Matrix Profile',
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
$this->actingAs($user);
$tenant->makeCurrent();
$matrixUrl = BaselineProfileResource::compareMatrixUrl($profile).'?subject_key='.urlencode($subjectKey);
visit(BaselineCompareLanding::getUrl(
parameters: [
'baseline_profile_id' => (int) $profile->getKey(),
'subject_key' => $subjectKey,
],
panel: 'tenant',
tenant: $tenant,
))
->waitForText('Open compare matrix')
->assertSee('Launch the compare matrix with the currently known baseline profile and any carried subject focus from this tenant landing.');
visit($matrixUrl)
->waitForText('Focused subject')
->assertSee($subjectKey)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
});