resolve($resourceType); $first = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['id' => 'assignment-filter-1', 'displayName' => 'Old name'], sourceMetadata: $decision->sourceMetadata, ); $second = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['id' => 'assignment-filter-1', 'displayName' => 'New name'], sourceMetadata: $decision->sourceMetadata, ); expect($second->getKey())->toBe($first->getKey()) ->and($second->source_display_name)->toBe('New name') ->and(TenantConfigurationResource::query()->count())->toBe(1); }); it('Spec417 keeps duplicate display names with different stable ids as separate resources', function (): void { [, $tenant] = createMinimalUserWithTenant(role: 'owner'); $connection = spec417UpsertConnection($tenant); $resourceType = spec417UpsertResourceType('deviceAndAppManagementAssignmentFilter'); $decision = app(CoverageSourceContractResolver::class)->resolve($resourceType); foreach (['assignment-filter-1', 'assignment-filter-2'] as $sourceId) { app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['id' => $sourceId, 'displayName' => 'Same visible name'], sourceMetadata: $decision->sourceMetadata, ); } expect(TenantConfigurationResource::query()->count())->toBe(2) ->and(TenantConfigurationResource::query()->pluck('source_display_name')->unique()->values()->all())->toBe(['Same visible name']) ->and(TenantConfigurationResource::query()->pluck('canonical_resource_key')->unique())->toHaveCount(2); }); it('Spec417 never marks display-name-only resources stable', function (): void { [, $tenant] = createMinimalUserWithTenant(role: 'owner'); $connection = spec417UpsertConnection($tenant); $resourceType = spec417UpsertResourceType('deviceAndAppManagementAssignmentFilter'); $decision = app(CoverageSourceContractResolver::class)->resolve($resourceType); $resource = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['displayName' => 'Only a label'], sourceMetadata: $decision->sourceMetadata, ); expect($resource->latest_identity_state)->toBe(IdentityState::MissingExternalId) ->and($resource->canonical_resource_key)->not->toContain('Only a label'); }); it('Spec417 does not merge repeated display-name-only resources as identity truth', function (): void { [, $tenant] = createMinimalUserWithTenant(role: 'owner'); $connection = spec417UpsertConnection($tenant); $resourceType = spec417UpsertResourceType('deviceAndAppManagementAssignmentFilter'); $decision = app(CoverageSourceContractResolver::class)->resolve($resourceType); $first = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['displayName' => 'Only a label'], sourceMetadata: $decision->sourceMetadata, ); $second = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['displayName' => 'Only a label'], sourceMetadata: $decision->sourceMetadata, ); $first->refresh(); expect($second->getKey())->not->toBe($first->getKey()) ->and(TenantConfigurationResource::query()->count())->toBe(2) ->and(TenantConfigurationResource::query()->pluck('canonical_resource_key')->unique())->toHaveCount(2) ->and(TenantConfigurationResource::query()->pluck('latest_identity_state')->all())->each->toBe(IdentityState::IdentityConflict) ->and(TenantConfigurationResource::query()->pluck('latest_claim_state')->all())->each->toBe(ClaimState::ClaimBlocked); }); it('Spec417 does not merge unsupported identity observations by fallback candidate key', function (): void { [, $tenant] = createMinimalUserWithTenant(role: 'owner'); $connection = spec417UpsertConnection($tenant); $resourceType = TenantConfigurationResourceType::factory()->create([ 'canonical_type' => 'unsupportedIdentityType', 'default_claim_state' => ClaimState::ClaimAllowed->value, ]); $first = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['id' => 'unsupported-1', 'displayName' => 'Unsupported A'], ); $second = app(CoverageResourceUpserter::class)->upsert( tenant: $tenant, providerConnection: $connection, resourceType: $resourceType, payload: ['id' => 'unsupported-2', 'displayName' => 'Unsupported B'], ); $first->refresh(); expect($second->getKey())->not->toBe($first->getKey()) ->and(TenantConfigurationResource::query()->count())->toBe(2) ->and(TenantConfigurationResource::query()->pluck('canonical_resource_key')->unique())->toHaveCount(2) ->and(TenantConfigurationResource::query()->pluck('latest_identity_state')->all())->each->toBe(IdentityState::IdentityConflict) ->and(TenantConfigurationResource::query()->pluck('latest_claim_state')->all())->each->toBe(ClaimState::ClaimBlocked); }); function spec417UpsertResourceType(string $canonicalType): TenantConfigurationResourceType { app(ResourceTypeRegistry::class)->syncDefaults(); return TenantConfigurationResourceType::query() ->where('canonical_type', $canonicalType) ->firstOrFail(); } function spec417UpsertConnection($tenant): ProviderConnection { return ProviderConnection::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), ]); }