94 lines
3.9 KiB
PHP
94 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\TenantConfigurationResource;
|
|
use App\Models\TenantConfigurationResourceType;
|
|
use App\Services\TenantConfiguration\CoverageResourceUpserter;
|
|
use App\Services\TenantConfiguration\CoverageSourceContractResolver;
|
|
use App\Services\TenantConfiguration\ResourceTypeRegistry;
|
|
use App\Support\TenantConfiguration\ClaimState;
|
|
use App\Support\TenantConfiguration\IdentityState;
|
|
|
|
it('Spec417 marks same-scope unsafe derived identity collisions as conflicts', function (): void {
|
|
[, $tenant] = createMinimalUserWithTenant(role: 'owner');
|
|
$connection = spec417ConflictConnection($tenant);
|
|
$resourceType = spec417ConflictResourceType('deviceAndAppManagementAssignmentFilter');
|
|
$decision = app(CoverageSourceContractResolver::class)->resolve($resourceType);
|
|
|
|
$basePayload = [
|
|
'platform' => 'windows10AndLater',
|
|
'assignmentFilterManagementType' => 'devices',
|
|
'rule' => '(device.osVersion -startsWith "10.")',
|
|
];
|
|
|
|
app(CoverageResourceUpserter::class)->upsert(
|
|
tenant: $tenant,
|
|
providerConnection: $connection,
|
|
resourceType: $resourceType,
|
|
payload: [...$basePayload, 'displayName' => 'Candidate A'],
|
|
sourceMetadata: $decision->sourceMetadata,
|
|
);
|
|
app(CoverageResourceUpserter::class)->upsert(
|
|
tenant: $tenant,
|
|
providerConnection: $connection,
|
|
resourceType: $resourceType,
|
|
payload: [...$basePayload, 'displayName' => 'Candidate B'],
|
|
sourceMetadata: $decision->sourceMetadata,
|
|
);
|
|
|
|
$resources = TenantConfigurationResource::query()->orderBy('id')->get();
|
|
|
|
expect($resources)->toHaveCount(2)
|
|
->and($resources->pluck('latest_identity_state')->all())->each->toBe(IdentityState::IdentityConflict)
|
|
->and($resources->pluck('latest_claim_state')->all())->each->toBe(ClaimState::ClaimBlocked)
|
|
->and($resources->last()->identity_diagnostics['reason_code'])->toBe('same_scope_derived_identity_collision');
|
|
});
|
|
|
|
it('Spec417 does not merge the same stable source key across workspace, environment, or provider scope', function (): void {
|
|
[, $tenant] = createMinimalUserWithTenant(role: 'owner');
|
|
[, $otherTenant] = createMinimalUserWithTenant(role: 'owner');
|
|
$resourceType = spec417ConflictResourceType('deviceAndAppManagementAssignmentFilter');
|
|
$decision = app(CoverageSourceContractResolver::class)->resolve($resourceType);
|
|
$connection = spec417ConflictConnection($tenant);
|
|
$otherConnection = spec417ConflictConnection($otherTenant);
|
|
$sameEnvironmentSecondProvider = spec417ConflictConnection($tenant);
|
|
|
|
foreach ([
|
|
[$tenant, $connection],
|
|
[$otherTenant, $otherConnection],
|
|
[$tenant, $sameEnvironmentSecondProvider],
|
|
] as [$scopedTenant, $scopedConnection]) {
|
|
app(CoverageResourceUpserter::class)->upsert(
|
|
tenant: $scopedTenant,
|
|
providerConnection: $scopedConnection,
|
|
resourceType: $resourceType,
|
|
payload: ['id' => 'same-provider-key', 'displayName' => 'Same resource name'],
|
|
sourceMetadata: $decision->sourceMetadata,
|
|
);
|
|
}
|
|
|
|
expect(TenantConfigurationResource::query()->count())->toBe(3)
|
|
->and(TenantConfigurationResource::query()->pluck('canonical_resource_key')->unique())->toHaveCount(1)
|
|
->and(TenantConfigurationResource::query()->pluck('provider_connection_id')->unique())->toHaveCount(3);
|
|
});
|
|
|
|
function spec417ConflictResourceType(string $canonicalType): TenantConfigurationResourceType
|
|
{
|
|
app(ResourceTypeRegistry::class)->syncDefaults();
|
|
|
|
return TenantConfigurationResourceType::query()
|
|
->where('canonical_type', $canonicalType)
|
|
->firstOrFail();
|
|
}
|
|
|
|
function spec417ConflictConnection($tenant): ProviderConnection
|
|
{
|
|
return ProviderConnection::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
]);
|
|
}
|
|
|