instance(GraphClientInterface::class, new class implements GraphClientInterface { public function listPolicies(string $policyType, array $options = []): GraphResponse { return new GraphResponse(true); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(true, data: ['id' => 'org-id', 'displayName' => 'Contoso']); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true); } }); [$user, $tenant] = createUserWithTenant(role: 'operator'); $connection = ProviderConnection::factory()->create([ 'tenant_id' => $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => fake()->uuid(), 'status' => 'needs_consent', 'health_status' => 'unknown', ]); ProviderCredential::factory()->create([ 'provider_connection_id' => $connection->getKey(), 'payload' => [ 'client_id' => 'client-id', 'client_secret' => 'client-secret', ], ]); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'provider.connection.check', 'status' => 'running', 'outcome' => 'pending', 'context' => [ 'provider' => 'microsoft', 'module' => 'health_check', 'provider_connection_id' => (int) $connection->getKey(), 'target_scope' => [ 'entra_tenant_id' => $connection->entra_tenant_id, ], ], ]); $job = new ProviderConnectionHealthCheckJob( tenantId: (int) $tenant->getKey(), userId: (int) $user->getKey(), providerConnectionId: (int) $connection->getKey(), operationRun: $run, ); $job->handle(app(\App\Services\Providers\MicrosoftProviderHealthCheck::class), app(OperationRunService::class)); $connection->refresh(); $run->refresh(); expect($connection->status)->toBe('connected'); expect($connection->health_status)->toBe('ok'); expect($connection->last_health_check_at)->not->toBeNull(); expect($connection->last_error_reason_code)->toBeNull(); expect($connection->last_error_message)->toBeNull(); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('succeeded'); expect($run->context)->toMatchArray([ 'target_scope' => [ 'entra_tenant_id' => $connection->entra_tenant_id, 'entra_tenant_name' => 'Contoso', ], ]); expect($connection->metadata)->toMatchArray([ 'entra_tenant_name' => 'Contoso', ]); }); it('categorizes auth failures and stores sanitized reason codes and messages', function (): void { app()->instance(GraphClientInterface::class, new class implements GraphClientInterface { public function listPolicies(string $policyType, array $options = []): GraphResponse { return new GraphResponse(true); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse( success: false, data: [], status: 401, errors: ['invalid_client Authorization: Bearer super-secret-token client_secret=ghi'], ); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true); } }); [$user, $tenant] = createUserWithTenant(role: 'operator'); $connection = ProviderConnection::factory()->create([ 'tenant_id' => $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => fake()->uuid(), 'status' => 'needs_consent', 'health_status' => 'unknown', ]); ProviderCredential::factory()->create([ 'provider_connection_id' => $connection->getKey(), 'payload' => [ 'client_id' => 'client-id', 'client_secret' => 'client-secret', ], ]); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'provider.connection.check', 'status' => 'running', 'outcome' => 'pending', 'context' => [ 'provider' => 'microsoft', 'module' => 'health_check', 'provider_connection_id' => (int) $connection->getKey(), 'target_scope' => [ 'entra_tenant_id' => $connection->entra_tenant_id, ], ], ]); $job = new ProviderConnectionHealthCheckJob( tenantId: (int) $tenant->getKey(), userId: (int) $user->getKey(), providerConnectionId: (int) $connection->getKey(), operationRun: $run, ); $job->handle(app(\App\Services\Providers\MicrosoftProviderHealthCheck::class), app(OperationRunService::class)); $connection->refresh(); $run->refresh(); expect($connection->status)->toBe('needs_consent'); expect($connection->health_status)->toBe('down'); expect($connection->last_error_reason_code)->toBe('provider_auth_failed'); expect((string) $connection->last_error_message) ->not->toContain('Authorization') ->not->toContain('Bearer ') ->not->toContain('client_secret'); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('failed'); $failures = $run->failure_summary; expect($failures)->toBeArray()->not->toBeEmpty(); $message = (string) ($failures[0]['message'] ?? ''); expect($failures[0]['reason_code'] ?? null)->toBe('provider_auth_failed'); expect($message) ->not->toContain('Authorization') ->not->toContain('Bearer ') ->not->toContain('client_secret'); });