TenantAtlas/tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php
2026-02-04 00:57:26 +01:00

166 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
use App\Jobs\ProviderConnectionHealthCheckJob;
use App\Models\AuditLog;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\OperationRunService;
use App\Services\Providers\MicrosoftProviderHealthCheck;
use App\Support\Audit\AuditActionId;
use App\Support\Verification\VerificationReportSchema;
it('writes a sanitized verification report for failed provider connection checks', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'operator');
$connection = ProviderConnection::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
'payload' => [
'tenant_id' => (string) $connection->entra_tenant_id,
'client_id' => fake()->uuid(),
'client_secret' => fake()->sha1(),
],
]);
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'user_id' => (int) $user->getKey(),
'type' => 'provider.connection.check',
'status' => 'running',
'outcome' => 'pending',
'context' => [
'provider_connection_id' => (int) $connection->getKey(),
],
]);
$this->mock(GraphClientInterface::class, function ($mock): void {
$mock->shouldReceive('getOrganization')
->once()
->andReturn(new GraphResponse(false, [], 401, ['Bearer super-secret-token']));
});
$job = new ProviderConnectionHealthCheckJob(
tenantId: (int) $tenant->getKey(),
userId: (int) $user->getKey(),
providerConnectionId: (int) $connection->getKey(),
operationRun: $run,
);
$job->handle(
healthCheck: app(MicrosoftProviderHealthCheck::class),
runs: app(OperationRunService::class),
);
$run = $run->fresh();
expect($run)->not->toBeNull();
expect($run->status)->toBe('completed');
expect($run->outcome)->toBe('failed');
$context = is_array($run->context) ? $run->context : [];
$report = $context['verification_report'] ?? null;
expect($report)->toBeArray();
expect(VerificationReportSchema::isValidReport($report))->toBeTrue();
expect(json_encode($report))->not->toContain('Bearer ');
expect($report['checks'][0]['reason_code'] ?? null)->toBe('authentication_failed');
foreach (($report['checks'] ?? []) as $check) {
expect($check)->toBeArray();
foreach (($check['evidence'] ?? []) as $pointer) {
expect($pointer)->toBeArray();
expect(array_keys($pointer))->toEqualCanonicalizing(['kind', 'value']);
}
}
$audit = AuditLog::query()
->where('workspace_id', (int) $tenant->workspace_id)
->where('action', AuditActionId::VerificationCompleted->value)
->latest('id')
->first();
expect($audit)->not->toBeNull();
expect($audit?->metadata)->toMatchArray([
'operation_run_id' => (int) $run->getKey(),
]);
});
it('writes a verification report for successful provider connection checks', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'operator');
$connection = ProviderConnection::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
'payload' => [
'tenant_id' => (string) $connection->entra_tenant_id,
'client_id' => fake()->uuid(),
'client_secret' => fake()->sha1(),
],
]);
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'user_id' => (int) $user->getKey(),
'type' => 'provider.connection.check',
'status' => 'running',
'outcome' => 'pending',
'context' => [
'provider_connection_id' => (int) $connection->getKey(),
],
]);
$this->mock(GraphClientInterface::class, function ($mock): void {
$mock->shouldReceive('getOrganization')
->once()
->andReturn(new GraphResponse(true, [
'id' => 'org_123',
'displayName' => 'Org 123',
], 200));
});
$job = new ProviderConnectionHealthCheckJob(
tenantId: (int) $tenant->getKey(),
userId: (int) $user->getKey(),
providerConnectionId: (int) $connection->getKey(),
operationRun: $run,
);
$job->handle(
healthCheck: app(MicrosoftProviderHealthCheck::class),
runs: app(OperationRunService::class),
);
$run = $run->fresh();
expect($run)->not->toBeNull();
expect($run->status)->toBe('completed');
expect($run->outcome)->toBe('succeeded');
$context = is_array($run->context) ? $run->context : [];
$report = $context['verification_report'] ?? null;
expect($report)->toBeArray();
expect(VerificationReportSchema::isValidReport($report))->toBeTrue();
expect($report['summary']['counts'] ?? [])->toMatchArray([
'total' => 1,
'pass' => 1,
'fail' => 0,
]);
});