makeCrossTenantCompareFixture(workspaceRole: 'owner', tenantRole: 'readonly'); $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Compare Only Policy', snapshot: ['settings' => [['key' => 'readonly', 'value' => 1]]], ); $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); $query = [ 'source_tenant_id' => (int) $fixture['sourceTenant']->getKey(), 'target_tenant_id' => (int) $fixture['targetTenant']->getKey(), 'policy_type' => ['deviceConfiguration'], ]; Livewire::withQueryParams($query) ->actingAs($fixture['user']) ->test(CrossTenantComparePage::class) ->call('generatePromotionPreflight') ->assertActionVisible('executePromotion') ->assertActionDisabled('executePromotion') ->assertActionExists('executePromotion', fn (Action $action): bool => $action->getTooltip() === 'You need target tenant manage access to execute promotion.') ->call('executePromotion') ->assertForbidden(); }); it('keeps execute promotion visible but disabled without workspace baseline manage access and forbids forced execution', function (): void { $fixture = $this->makeCrossTenantCompareFixture(workspaceRole: 'readonly', tenantRole: 'owner'); $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Workspace Gate Policy', snapshot: ['settings' => [['key' => 'workspace', 'value' => 1]]], ); $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); $selection = new CrossTenantCompareSelection( $fixture['sourceTenant'], $fixture['targetTenant'], ['deviceConfiguration'], ); $preview = app(CrossTenantComparePreviewBuilder::class)->build($selection); $preflight = app(CrossTenantPromotionPreflight::class)->build($preview); Livewire::withQueryParams([ 'source_tenant_id' => (int) $fixture['sourceTenant']->getKey(), 'target_tenant_id' => (int) $fixture['targetTenant']->getKey(), 'policy_type' => ['deviceConfiguration'], ]) ->actingAs($fixture['user']) ->test(CrossTenantComparePage::class) ->set('preview', $preview) ->set('preflight', $preflight) ->assertActionVisible('executePromotion') ->assertActionDisabled('executePromotion') ->assertActionExists('executePromotion', fn (Action $action): bool => $action->getTooltip() === 'You need workspace baseline manage access to execute promotion.') ->call('executePromotion') ->assertForbidden(); }); it('does not queue a promotion run when the current preflight is stale for the selected target tenant', function (): void { $fixture = $this->makeCrossTenantCompareFixture(); $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Stale Policy', snapshot: ['settings' => [['key' => 'stale', 'value' => 1]]], ); $staleTarget = Tenant::factory()->create([ 'workspace_id' => (int) $fixture['workspace']->getKey(), 'name' => 'Stale Target', ]); $fixture['user']->tenants()->syncWithoutDetaching([ (int) $staleTarget->getKey() => ['role' => 'owner'], ]); app(CapabilityResolver::class)->clearCache(); $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); $staleSelection = new CrossTenantCompareSelection( $fixture['sourceTenant'], $staleTarget, ['deviceConfiguration'], ); $currentSelection = new CrossTenantCompareSelection( $fixture['sourceTenant'], $fixture['targetTenant'], ['deviceConfiguration'], ); $currentPreview = app(CrossTenantComparePreviewBuilder::class)->build($currentSelection); $stalePreview = app(CrossTenantComparePreviewBuilder::class)->build($staleSelection); $stalePreflight = app(CrossTenantPromotionPreflight::class)->build($stalePreview); Livewire::withQueryParams([ 'source_tenant_id' => (int) $fixture['sourceTenant']->getKey(), 'target_tenant_id' => (int) $fixture['targetTenant']->getKey(), 'policy_type' => ['deviceConfiguration'], ]) ->actingAs($fixture['user']) ->test(CrossTenantComparePage::class) ->set('preview', $currentPreview) ->set('preflight', $stalePreflight) ->call('executePromotion') ->assertNotified('Promotion execution unavailable'); expect(OperationRun::query()->count())->toBe(0); }); it('does not queue a promotion run when promotion execution is paused by operational control', function (): void { $fixture = $this->makeCrossTenantCompareFixture(); $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Paused Policy', snapshot: ['settings' => [['key' => 'paused', 'value' => 1]]], ); $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); OperationalControlActivation::factory()->workspaceScoped()->create([ 'control_key' => 'promotion.execute', 'workspace_id' => (int) $fixture['workspace']->getKey(), 'reason_text' => 'Paused during promotion review.', ]); Livewire::withQueryParams([ 'source_tenant_id' => (int) $fixture['sourceTenant']->getKey(), 'target_tenant_id' => (int) $fixture['targetTenant']->getKey(), 'policy_type' => ['deviceConfiguration'], ]) ->actingAs($fixture['user']) ->test(CrossTenantComparePage::class) ->call('generatePromotionPreflight') ->call('executePromotion') ->assertNotified('Promotion execution paused'); expect(OperationRun::query()->count())->toBe(0); }); it('returns 404 and does not queue a promotion run when the requested target tenant is outside the actor scope', function (): void { $fixture = $this->makeCrossTenantCompareFixture(); $hiddenTarget = Tenant::factory()->create([ 'workspace_id' => (int) $fixture['workspace']->getKey(), 'name' => 'Hidden Promotion Target', ]); $session = $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); $this->withSession($session) ->get(CrossTenantComparePage::getUrl(parameters: [ 'source_tenant_id' => (int) $fixture['sourceTenant']->getKey(), 'target_tenant_id' => (int) $hiddenTarget->getKey(), 'policy_type' => ['deviceConfiguration'], ], panel: 'admin')) ->assertNotFound(); expect(OperationRun::query()->count())->toBe(0); });