set('graph.client_id', 'platform-client-id'); config()->set('graph.client_secret', 'platform-client-secret'); $tenant = Tenant::factory()->create([ 'tenant_id' => 'platform-target-tenant-id', 'app_client_id' => 'legacy-tenant-client-id', ]); $connection = ProviderConnection::factory()->platform()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'provider' => 'microsoft', 'entra_tenant_id' => 'platform-target-tenant-id', ]); ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'dedicated-fallback-client-id', 'client_secret' => 'dedicated-fallback-client-secret', ], ]); $resolution = app(ProviderIdentityResolver::class)->resolve($connection); expect($resolution->resolved)->toBeTrue() ->and($resolution->connectionType)->toBe(ProviderConnectionType::Platform) ->and($resolution->effectiveClientId)->toBe('platform-client-id') ->and($resolution->effectiveClientId)->not->toBe('legacy-tenant-client-id') ->and($resolution->effectiveClientId)->not->toBe('dedicated-fallback-client-id') ->and($resolution->credentialSource)->toBe('platform_config'); }); it('resolves dedicated connections from dedicated credentials only', function (): void { $tenant = Tenant::factory()->create([ 'tenant_id' => 'dedicated-target-tenant-id', ]); $connection = ProviderConnection::factory()->dedicated()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'provider' => 'microsoft', 'entra_tenant_id' => 'dedicated-target-tenant-id', ]); ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'dedicated-client-id', 'client_secret' => 'dedicated-client-secret', ], ]); $resolution = app(ProviderIdentityResolver::class)->resolve($connection); expect($resolution->resolved)->toBeTrue() ->and($resolution->connectionType)->toBe(ProviderConnectionType::Dedicated) ->and($resolution->effectiveClientId)->toBe('dedicated-client-id') ->and($resolution->credentialSource)->toBe('dedicated_manual'); }); it('blocks dedicated connections when dedicated credentials are missing', function (): void { $connection = ProviderConnection::factory()->dedicated()->create([ 'entra_tenant_id' => 'dedicated-target-tenant-id', ]); $resolution = app(ProviderIdentityResolver::class)->resolve($connection); expect($resolution->resolved)->toBeFalse() ->and($resolution->connectionType)->toBe(ProviderConnectionType::Dedicated) ->and($resolution->effectiveReasonCode())->toBe(ProviderReasonCodes::DedicatedCredentialMissing); }); it('blocks connections that require migration review before runtime use', function (): void { $connection = ProviderConnection::factory()->platform()->create([ 'entra_tenant_id' => 'platform-target-tenant-id', 'migration_review_required' => true, ]); $resolution = app(ProviderIdentityResolver::class)->resolve($connection); expect($resolution->resolved)->toBeFalse() ->and($resolution->effectiveReasonCode())->toBe(ProviderReasonCodes::ProviderConnectionReviewRequired); });