makeCrossTenantCompareFixture(); $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Promotable Policy', snapshot: ['settings' => [['key' => 'wifi', 'value' => 1]]], ); $blocked = $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Blocked Policy', snapshot: ['settings' => [['key' => 'blocked', 'value' => 1]]], ); $blocked['version']->delete(); $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Manual Policy', snapshot: ['settings' => [['key' => 'manual', 'value' => 1]]], ); $this->createPortfolioCompareSubject( tenant: $fixture['targetTenant'], displayName: 'Manual Policy', snapshot: ['settings' => [['key' => 'manual', 'value' => 1]]], ); $this->createPortfolioCompareSubject( tenant: $fixture['targetTenant'], displayName: 'Manual Policy', snapshot: ['settings' => [['key' => 'manual', 'value' => 2]]], ); $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); $query = [ 'source_tenant_id' => (int) $fixture['sourceTenant']->getKey(), 'target_tenant_id' => (int) $fixture['targetTenant']->getKey(), 'policy_type' => ['deviceConfiguration'], ]; $component = Livewire::withQueryParams($query) ->actingAs($fixture['user']) ->test(CrossTenantComparePage::class) ->call('executePromotion') ->assertNotified('Promotion execution unavailable'); expect(OperationRun::query()->count())->toBe(0); $component ->call('generatePromotionPreflight') ->assertActionVisible('executePromotion') ->mountAction('executePromotion') ->callMountedAction() ->assertHasNoActionErrors(); $run = OperationRun::query()->latest('id')->first(); expect($run) ->not->toBeNull() ->and($run?->type)->toBe('promotion.execute') ->and(data_get($run?->context, 'promotion_execution.plan.summary.ready'))->toBe(1) ->and(data_get($run?->context, 'promotion_execution.plan.summary.excluded'))->toBe(2) ->and(data_get($run?->context, 'promotion_execution.plan.items.0.display_name'))->toBe('Promotable Policy') ->and(data_get($run?->context, 'promotion_execution.plan.items.0.execution_action'))->toBe('create_missing') ->and(collect(data_get($run?->context, 'promotion_execution.plan.excluded', []))->pluck('excluded_reason')->all()) ->toEqualCanonicalizing(['blocked', 'manual_mapping_required']); Queue::assertPushed(CrossTenantPromotionExecutionJob::class, function (CrossTenantPromotionExecutionJob $job) use ($run): bool { return $job->getOperationRun()?->is($run); }); }); it('does not queue a promotion run when the current preflight has no ready governed subjects', function (): void { Queue::fake(); $fixture = $this->makeCrossTenantCompareFixture(); $blocked = $this->createPortfolioCompareSubject( tenant: $fixture['sourceTenant'], displayName: 'Blocked Policy', snapshot: ['settings' => [['key' => 'blocked', 'value' => 1]]], ); $blocked['version']->delete(); $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']); 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') ->assertActionVisible('executePromotion') ->assertActionDisabled('executePromotion') ->assertActionExists('executePromotion', fn (Action $action): bool => $action->getTooltip() === 'Current promotion preflight has no ready governed subjects to execute.') ->call('executePromotion') ->assertNotified('Promotion execution unavailable'); expect(OperationRun::query()->count())->toBe(0); Queue::assertNothingPushed(); });