TenantAtlas/apps/platform/tests/Feature/TenantConfiguration/Spec426ExchangeTeamsStableIdentityReadinessTest.php
ahmido f7d06621a0 feat: implement Exchange Teams evidence identity readiness (#493)
Automated PR for spec 426 exchange teams core evidence identity readiness. Includes service changes and coverage/requirement/spec updates from commit fb4dc20c.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #493
2026-07-03 11:43:11 +00:00

113 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\TenantConfigurationResource;
use App\Models\TenantConfigurationResourceEvidence;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\TenantConfiguration\GenericContentEvidenceCaptureService;
use App\Services\TenantConfiguration\ResourceTypeRegistry;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\TenantConfiguration\CaptureOutcome;
it('Spec426 does not create stable identity readiness rows from blocked Exchange and Teams source types', function (): void {
app(ResourceTypeRegistry::class)->syncDefaults();
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
$connection = ProviderConnection::factory()->withCredential()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
]);
$graph = spec426IdentityReadinessGraphClient();
app()->instance(GraphClientInterface::class, $graph);
$run = OperationRun::factory()->withUser($user)->forTenant($environment)->create([
'type' => OperationRunType::TenantConfigurationCapture->value,
'status' => OperationRunStatus::Queued->value,
'outcome' => OperationRunOutcome::Pending->value,
'context' => [
'target_scope' => [
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'provider_connection_id' => (int) $connection->getKey(),
],
'resource_types' => ['appPermissionPolicy'],
'required_capability' => 'evidence.manage',
],
]);
$result = app(GenericContentEvidenceCaptureService::class)->capture(
tenant: $environment,
providerConnection: $connection,
operationRun: $run,
canonicalTypes: ['appPermissionPolicy'],
);
expect($graph->calls)->toBe([])
->and($result['summary_counts'])->toMatchArray([
'total' => 1,
'processed' => 1,
'succeeded' => 0,
'skipped' => 1,
'failed' => 0,
])
->and($result['run_outcome'])->toBe(OperationRunOutcome::Blocked->value)
->and($result['outcomes'][0]['outcome'])->toBe(CaptureOutcome::BlockedMissingContract->value)
->and($result['outcomes'][0]['reason_code'])->toBe('missing_source_contract_mapping')
->and(TenantConfigurationResource::query()->count())->toBe(0)
->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0);
});
function spec426IdentityReadinessGraphClient(): GraphClientInterface
{
return new class implements GraphClientInterface
{
/**
* @var list<array{policy_type: string, options: array<string, mixed>}>
*/
public array $calls = [];
public function listPolicies(string $policyType, array $options = []): GraphResponse
{
$this->calls[] = ['policy_type' => $policyType, 'options' => $options];
return new GraphResponse(true, [[
'DisplayName' => 'Display-only policy',
'Name' => 'Display-only policy',
'Identity' => 'Global',
'GlobalCatalogAppsType' => 'AllowAllApps',
]]);
}
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
{
return new GraphResponse(false, [], 501);
}
public function getOrganization(array $options = []): GraphResponse
{
return new GraphResponse(false, [], 501);
}
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
{
return new GraphResponse(false, [], 501);
}
public function getServicePrincipalPermissions(array $options = []): GraphResponse
{
return new GraphResponse(false, [], 501);
}
public function request(string $method, string $path, array $options = []): GraphResponse
{
return new GraphResponse(false, [], 501);
}
};
}