'tenant-1', 'name' => 'Contoso', ]); $registry = app(ProviderNextStepsRegistry::class); $steps = $registry->forReason($tenant, ProviderReasonCodes::ProviderConsentMissing); expect($steps)->toBeArray() ->and($steps)->not->toBeEmpty() ->and($steps[0]['label'])->toBe('Grant admin consent') ->and($steps[0]['url'])->toContain('learn.microsoft.com'); }); it('links to the real admin consent endpoint when provider credentials exist', function () { $tenant = Tenant::factory()->create([ 'app_client_id' => null, ]); $connection = ProviderConnection::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->graphTenantId(), 'is_default' => true, ]); ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'derived-client-id', 'client_secret' => 'derived-client-secret', ], ]); $registry = app(ProviderNextStepsRegistry::class); $steps = $registry->forReason($tenant, ProviderReasonCodes::ProviderConsentMissing, $connection); expect($steps)->toBeArray() ->and($steps)->not->toBeEmpty() ->and($steps[0]['label'])->toBe('Grant admin consent') ->and($steps[0]['url'])->toContain('login.microsoftonline.com') ->and($steps[0]['url'])->toContain('adminconsent'); }); it('surfaces review-required next steps on the provider connection detail path', function (): void { $tenant = Tenant::factory()->create([ 'app_client_id' => null, ]); $connection = ProviderConnection::factory()->dedicated()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->graphTenantId(), 'migration_review_required' => true, 'metadata' => [ 'effective_app' => [ 'app_id' => null, 'source' => 'review_required', ], ], ]); $registry = app(ProviderNextStepsRegistry::class); $steps = $registry->forReason($tenant, ProviderReasonCodes::ProviderConnectionReviewRequired, $connection); expect($steps)->toHaveCount(2) ->and($steps[0]['label'])->toBe('Review migration classification') ->and($steps[1]['label'])->toBe('Review effective app details') ->and($steps[0]['url'])->toContain('/provider-connections/') ->and($steps[1]['url'])->toContain('/edit'); });