browser()->timeout(60_000); it('Spec362 smokes selected-family reconciled truth across the existing operations hub and detail pages', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); spec362AuthenticateBrowser($this, $user, $environment); [$inventoryRun, $baselineRun, $backupRun] = spec362BrowserSeedSelectedFamilyRuns($environment, $user); visit(OperationRunLinks::index($environment)) ->resize(1440, 1100) ->waitForText('Operations Hub') ->assertSee('Inventory sync') ->assertSee('Baseline capture') ->assertSee('Backup schedule run') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); visit(OperationRunLinks::tenantlessView($inventoryRun)) ->waitForText('Monitoring detail') ->assertSee('Inventory sync') ->assertSee('Completed with follow-up') ->assertSee('Automatically reconciled') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); visit(OperationRunLinks::tenantlessView($baselineRun)) ->waitForText('Monitoring detail') ->assertSee('Baseline capture') ->assertSee('Completed successfully') ->assertSee('Automatically reconciled') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); visit(OperationRunLinks::tenantlessView($backupRun)) ->waitForText('Monitoring detail') ->assertSee('Backup schedule run') ->assertSee('Completed with follow-up') ->assertSee('Automatically reconciled') ->assertNoJavaScriptErrors() ->assertNoConsoleLogs(); }); function spec362AuthenticateBrowser(mixed $test, User $user, ManagedEnvironment $environment): void { $workspaceId = (int) $environment->workspace_id; $test->actingAs($user)->withSession([ WorkspaceContext::SESSION_KEY => $workspaceId, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $workspaceId => (int) $environment->getKey(), ], ]); session()->put(WorkspaceContext::SESSION_KEY, $workspaceId); session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [ (string) $workspaceId => (int) $environment->getKey(), ]); setAdminPanelContext($environment); } /** * @return array{0: OperationRun, 1: OperationRun, 2: OperationRun} */ function spec362BrowserSeedSelectedFamilyRuns(ManagedEnvironment $environment, User $user): array { $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $environment->workspace_id, ]); $snapshot = BaselineSnapshot::factory()->complete()->create([ 'workspace_id' => (int) $environment->workspace_id, 'baseline_profile_id' => (int) $profile->getKey(), 'summary_jsonb' => ['total_items' => 2], ]); $backupSet = BackupSet::factory()->for($environment)->create([ 'status' => 'partial', 'item_count' => 2, 'metadata' => [ 'failures' => [ ['policy_id' => 'policy-1'], ], ], ]); $inventoryRun = OperationRun::factory()->forTenant($environment)->create([ 'user_id' => (int) $user->getKey(), 'initiator_name' => $user->name, 'type' => 'inventory.sync', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'policy_types' => ['conditionalAccessPolicy', 'deviceConfiguration'], 'inventory' => [ 'coverage' => InventoryCoverage::buildPayload([ 'conditionalAccessPolicy' => 'succeeded', 'deviceConfiguration' => 'skipped', ], []), ], ], ]); $baselineRun = OperationRun::factory()->forTenant($environment)->create([ 'user_id' => (int) $user->getKey(), 'initiator_name' => $user->name, 'type' => 'baseline.capture', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'baseline_profile_id' => (int) $profile->getKey(), 'baseline_snapshot_id' => (int) $snapshot->getKey(), ], ]); $backupRun = OperationRun::factory()->forTenant($environment)->create([ 'user_id' => (int) $user->getKey(), 'initiator_name' => $user->name, 'type' => 'backup.schedule.execute', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'backup_set_id' => (int) $backupSet->getKey(), ], ]); $service = app(OperationRunService::class); $inventoryRun = $service->updateRunWithReconciliation( run: $inventoryRun, status: OperationRunStatus::Completed->value, outcome: OperationRunOutcome::PartiallySucceeded->value, summaryCounts: [ 'total' => 2, 'processed' => 2, 'succeeded' => 1, 'failed' => 0, 'skipped' => 1, ], failures: [[ 'code' => 'inventory.partial', 'reason_code' => 'run.adapter_out_of_sync', 'message' => 'The inventory sync recorded usable coverage, but some selected policy types did not complete cleanly.', ]], reasonCode: 'run.adapter_out_of_sync', reasonMessage: 'The inventory sync recorded usable coverage, but some selected policy types did not complete cleanly.', source: 'adapter_reconciler', evidence: ['adapter' => 'inventory_sync'], adapter: 'inventory_sync', decision: 'reconciled_partially_succeeded', )->fresh(); $baselineRun = $service->updateRunWithReconciliation( run: $baselineRun, status: OperationRunStatus::Completed->value, outcome: OperationRunOutcome::Succeeded->value, summaryCounts: [ 'total' => 2, 'processed' => 2, 'succeeded' => 2, 'failed' => 0, ], failures: [], reasonCode: 'run.adapter_out_of_sync', reasonMessage: 'The baseline capture already produced a complete snapshot before the run finished updating.', source: 'adapter_reconciler', evidence: ['adapter' => 'baseline_capture'], adapter: 'baseline_capture', decision: 'reconciled_succeeded', related: [ 'type' => 'baseline_snapshot', 'id' => (int) $snapshot->getKey(), 'lifecycle_state' => 'complete', ], )->fresh(); $backupRun = $service->updateRunWithReconciliation( run: $backupRun, status: OperationRunStatus::Completed->value, outcome: OperationRunOutcome::PartiallySucceeded->value, summaryCounts: [ 'total' => 3, 'processed' => 3, 'succeeded' => 2, 'failed' => 1, 'created' => 1, 'updated' => 2, 'items' => 3, ], failures: [[ 'code' => 'backup_schedule.partial', 'reason_code' => 'run.adapter_out_of_sync', 'message' => 'The backup schedule produced a usable backup set, but some captures did not finish cleanly.', ]], reasonCode: 'run.adapter_out_of_sync', reasonMessage: 'The backup schedule produced a usable backup set, but some captures did not finish cleanly.', source: 'adapter_reconciler', evidence: ['adapter' => 'backup_schedule_execution'], adapter: 'backup_schedule_execution', decision: 'reconciled_partially_succeeded', related: [ 'type' => 'backup_set', 'id' => (int) $backupSet->getKey(), 'status' => 'partial', ], )->fresh(); return [$inventoryRun, $baselineRun, $backupRun]; }