create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); session()->forget(WorkspaceContext::SESSION_KEY); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => null, 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, ]); $this->actingAs($user) ->get("/admin/operations/{$run->getKey()}") ->assertSuccessful(); expect(session()->get(WorkspaceContext::SESSION_KEY))->toBeNull(); }); it('returns 404 for non-members when viewing an operation run without a selected workspace', function (): void { $workspace = Workspace::factory()->create(); $user = User::factory()->create(); session()->forget(WorkspaceContext::SESSION_KEY); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => null, 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, ]); $this->actingAs($user) ->get("/admin/operations/{$run->getKey()}") ->assertNotFound(); }); it('returns 403 for members missing the required capability for the operation type', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); $tenant->users()->attach((int) $user->getKey(), [ 'role' => TenantRole::Readonly->value, 'source' => 'manual', 'source_ref' => null, 'created_by_user_id' => null, ]); session()->forget(WorkspaceContext::SESSION_KEY); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'type' => 'inventory_sync', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, ]); $this->actingAs($user) ->get("/admin/operations/{$run->getKey()}") ->assertForbidden(); }); it('keeps a canonical run viewer accessible when the remembered tenant differs from the run tenant', function (): void { $workspace = Workspace::factory()->create(); $tenantA = Tenant::factory()->for($workspace)->create(); $tenantB = Tenant::factory()->for($workspace)->create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); foreach ([$tenantA, $tenantB] as $tenant) { $tenant->users()->attach((int) $user->getKey(), [ 'role' => TenantRole::Owner->value, 'source' => 'manual', 'source_ref' => null, 'created_by_user_id' => null, ]); } $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenantA->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'type' => 'inventory_sync', 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, ]); Filament::setTenant($tenantB, true); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ (string) $workspace->getKey() => (int) $tenantB->getKey(), ], ]) ->get("/admin/operations/{$run->getKey()}") ->assertSuccessful() ->assertSee('Operation run') ->assertSee('Back to Operations'); }); it('renders stored target scope and failure details for a completed run', function (): void { $workspace = Workspace::factory()->create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); session()->forget(WorkspaceContext::SESSION_KEY); $entraTenantId = '11111111-1111-1111-1111-111111111111'; $failureMessage = 'Missing required Graph permissions.'; $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => null, 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Failed->value, 'context' => [ 'target_scope' => [ 'entra_tenant_id' => $entraTenantId, 'entra_tenant_name' => 'Contoso', ], ], 'failure_summary' => [ [ 'code' => 'provider.connection.check.failed', 'reason_code' => 'permission_denied', 'message' => $failureMessage, ], ], ]); $this->actingAs($user) ->get("/admin/operations/{$run->getKey()}") ->assertSuccessful() ->assertSee($entraTenantId) ->assertSee('permission_denied') ->assertSee($failureMessage); }); it('renders explicit back-link lineage when opened from a canonical source context', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); $tenant->users()->attach((int) $user->getKey(), [ 'role' => TenantRole::Owner->value, 'source' => 'manual', 'source_ref' => null, 'created_by_user_id' => null, ]); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'type' => 'backup_set.add_policies', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, ]); $context = new CanonicalNavigationContext( sourceSurface: 'backup_set.detail_section', canonicalRouteName: 'admin.operations.view', tenantId: (int) $tenant->getKey(), backLinkLabel: 'Back to backup set', backLinkUrl: '/admin/tenant/backup-sets/1', ); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get(OperationRunLinks::tenantlessView($run, $context)) ->assertSuccessful() ->assertSee('Back to backup set') ->assertSee('/admin/tenant/backup-sets/1', false); }); it('keeps the explicit back-link action after Livewire hydration', function (): void { $workspace = Workspace::factory()->create(); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); $tenant->users()->attach((int) $user->getKey(), [ 'role' => TenantRole::Owner->value, 'source' => 'manual', 'source_ref' => null, 'created_by_user_id' => null, ]); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'type' => 'baseline_compare', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, ]); $this->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]); session([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]); Livewire::withQueryParams([ 'nav' => [ 'source_surface' => 'finding.detail_section', 'canonical_route_name' => 'admin.operations.view', 'tenant_id' => (int) $tenant->getKey(), 'back_label' => 'Back to finding', 'back_url' => '/admin/findings/42?tenant='.$tenant->external_id, ], ]) ->actingAs($user) ->test(\App\Filament\Pages\Operations\TenantlessOperationRunViewer::class, ['run' => $run]) ->assertActionVisible('operate_hub_back_to_origin_run_detail'); }); it('renders shared polling markup for active tenantless runs', function (string $status, int $ageSeconds): void { $workspace = Workspace::factory()->create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); session()->forget(WorkspaceContext::SESSION_KEY); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => null, 'type' => 'provider.connection.check', 'status' => $status, 'outcome' => OperationRunOutcome::Pending->value, 'created_at' => now()->subSeconds($ageSeconds), ]); $expectedInterval = RunDetailPolling::interval($run); expect($expectedInterval)->not->toBeNull(); $this->actingAs($user) ->get("/admin/operations/{$run->getKey()}") ->assertSuccessful() ->assertSee("wire:poll.{$expectedInterval}", escape: false); })->with([ 'queued runs poll every second at startup' => [ OperationRunStatus::Queued->value, 5, ], 'running runs slow to five seconds after startup' => [ OperationRunStatus::Running->value, 30, ], ]); it('does not render polling markup for terminal tenantless runs', function (string $outcome): void { $workspace = Workspace::factory()->create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); session()->forget(WorkspaceContext::SESSION_KEY); $run = OperationRun::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => null, 'type' => 'provider.connection.check', 'status' => OperationRunStatus::Completed->value, 'outcome' => $outcome, 'created_at' => now()->subMinutes(2), ]); $this->actingAs($user) ->get("/admin/operations/{$run->getKey()}") ->assertSuccessful() ->assertDontSee('wire:poll.1s', escape: false) ->assertDontSee('wire:poll.5s', escape: false) ->assertDontSee('wire:poll.10s', escape: false); })->with([ 'succeeded runs stay stable' => OperationRunOutcome::Succeeded->value, 'failed runs stay stable' => OperationRunOutcome::Failed->value, ]);