TenantAtlas/apps/platform/tests/Feature/TenantConfiguration/Spec420M365ProviderConnectionScopeTest.php
ahmido a73a8f5882 feat: complete m365 generic evidence coverage pack (#487)
Committing and publishing the current Spec 420 package changes.

Includes updated services, coverage tests, browser smoke coverage, and the spec/plan/tasks artifacts for the package.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #487
2026-06-27 12:24:00 +00:00

113 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Jobs\TenantConfiguration\CaptureTenantConfigurationEvidenceJob;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Services\Audit\AuditRecorder;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\OperationRunService;
use App\Services\TenantConfiguration\GenericContentEvidenceCaptureService;
use App\Services\TenantConfiguration\ResourceTypeRegistry;
use App\Services\TenantConfiguration\StartTenantConfigurationCapture;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use Illuminate\Support\Facades\Queue;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
it('Spec420 rejects cross-environment provider connections before run creation', function (): void {
Queue::fake();
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
[, $otherEnvironment] = createMinimalUserWithTenant(role: 'owner');
$foreignConnection = ProviderConnection::factory()->create([
'workspace_id' => (int) $otherEnvironment->workspace_id,
'managed_environment_id' => (int) $otherEnvironment->getKey(),
]);
expect(fn () => app(StartTenantConfigurationCapture::class)->start($environment, $foreignConnection, $user, [
'conditionalAccessPolicy',
]))->toThrow(NotFoundHttpException::class);
Queue::assertNothingPushed();
});
it('Spec420 revalidates provider connection scope in the queued job before graph work', function (): void {
app(ResourceTypeRegistry::class)->syncDefaults();
[$user, $environment] = createMinimalUserWithTenant(role: 'owner');
[, $otherEnvironment] = createMinimalUserWithTenant(role: 'owner');
$foreignConnection = ProviderConnection::factory()->withCredential()->create([
'workspace_id' => (int) $otherEnvironment->workspace_id,
'managed_environment_id' => (int) $otherEnvironment->getKey(),
]);
$graph = spec420ScopeGraphClient();
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) $foreignConnection->getKey(),
],
'resource_types' => ['conditionalAccessPolicy'],
'required_capability' => 'evidence.manage',
],
]);
expect(fn () => app(CaptureTenantConfigurationEvidenceJob::class, ['run' => $run])->handle(
app(GenericContentEvidenceCaptureService::class),
app(OperationRunService::class),
app(AuditRecorder::class),
))->toThrow(RuntimeException::class, 'same-scope provider connection');
expect($graph->calls)->toBe([]);
});
function spec420ScopeGraphClient(): GraphClientInterface
{
return new class implements GraphClientInterface
{
public array $calls = [];
public function listPolicies(string $policyType, array $options = []): GraphResponse
{
$this->calls[] = ['policy_type' => $policyType, 'options' => $options];
return new GraphResponse(true, []);
}
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);
}
};
}