TenantAtlas/apps/platform/tests/Feature/TenantConfiguration/Spec427ExchangeTeamsNoEvidencePromotionTest.php
ahmido bfb52b84d6 feat: implement spec 427 source contract enablement (#494)
Automated PR for spec 427 Exchange Teams verified source contract enablement.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #494
2026-07-03 23:12:45 +00:00

128 lines
4.7 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('Spec427 blocks Exchange and Teams source contracts without provider calls or evidence rows', 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(),
'scopes_granted' => ['Exchange.ManageAsApp', 'Policy.Read.All'],
]);
$graph = spec427NoEvidenceGraphClient();
app()->instance(GraphClientInterface::class, $graph);
$run = spec427NoEvidenceRun($user, $environment, $connection, spec427NoEvidenceTypes());
$result = app(GenericContentEvidenceCaptureService::class)->capture(
tenant: $environment,
providerConnection: $connection,
operationRun: $run,
canonicalTypes: spec427NoEvidenceTypes(),
);
expect($graph->calls)->toBe([])
->and($result['summary_counts'])->toMatchArray([
'total' => 4,
'processed' => 4,
'succeeded' => 0,
'skipped' => 4,
'failed' => 0,
'errors_recorded' => 0,
])
->and($result['run_outcome'])->toBe(OperationRunOutcome::Blocked->value)
->and(TenantConfigurationResource::query()->count())->toBe(0)
->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0);
foreach (collect($result['outcomes'])->keyBy('canonical_type') as $canonicalType => $outcome) {
expect(spec427NoEvidenceTypes())->toContain($canonicalType)
->and($outcome['outcome'])->toBe(CaptureOutcome::BlockedMissingContract->value)
->and($outcome['reason_code'])->toBe('contract_blocked_repo_adapter_missing')
->and($outcome['source_contract_key'])->toBeNull();
}
});
/**
* @return list<string>
*/
function spec427NoEvidenceTypes(): array
{
return ['acceptedDomain', 'appPermissionPolicy', 'meetingPolicy', 'transportRule'];
}
function spec427NoEvidenceRun($user, $environment, ProviderConnection $connection, array $resourceTypes): OperationRun
{
return 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' => $resourceTypes,
'required_capability' => 'evidence.manage',
],
]);
}
function spec427NoEvidenceGraphClient(): 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, [['id' => 'unexpected-provider-call']]);
}
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);
}
};
}