TenantAtlas/apps/platform/tests/Feature/Jobs/PolicySyncProviderMissingSemanticsTest.php
Ahmed Darrazi 91f327a7c2
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m40s
feat(specs/261): add provider missing policy visibility
2026-05-01 22:17:29 +02:00

158 lines
5.2 KiB
PHP

<?php
use App\Models\AuditLog;
use App\Models\Policy;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Models\Tenant;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphLogger;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\PolicySyncService;
use App\Support\Audit\AuditActionId;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\mock;
uses(RefreshDatabase::class);
function tenantWithDefaultMicrosoftConnectionForProviderMissing(array $attributes = []): Tenant
{
$tenant = Tenant::factory()->create($attributes + [
'status' => 'active',
'app_client_id' => null,
'app_client_secret' => null,
]);
$connection = ProviderConnection::factory()->consentGranted()->create([
'tenant_id' => (int) $tenant->getKey(),
'provider' => 'microsoft',
'is_default' => true,
'consent_status' => 'granted',
'entra_tenant_id' => (string) ($tenant->tenant_id ?: 'tenant-'.$tenant->getKey()),
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
'type' => 'client_secret',
'payload' => [
'client_id' => 'provider-client-'.$tenant->getKey(),
'client_secret' => 'provider-secret-'.$tenant->getKey(),
],
]);
return $tenant;
}
it('marks previously observed policies missing when provider list omits them', function (): void {
$tenant = tenantWithDefaultMicrosoftConnectionForProviderMissing();
$present = Policy::factory()->create([
'tenant_id' => $tenant->id,
'external_id' => 'policy-present',
'policy_type' => 'deviceConfiguration',
'display_name' => 'Old present',
'ignored_at' => null,
'missing_from_provider_at' => null,
]);
$missing = Policy::factory()->create([
'tenant_id' => $tenant->id,
'external_id' => 'policy-missing',
'policy_type' => 'deviceConfiguration',
'display_name' => 'Missing from provider',
'ignored_at' => null,
'missing_from_provider_at' => null,
]);
mock(GraphLogger::class)
->shouldReceive('logRequest', 'logResponse')
->zeroOrMoreTimes()
->andReturnNull();
mock(GraphClientInterface::class)
->shouldReceive('listPolicies')
->once()
->with('deviceConfiguration', mockery::type('array'))
->andReturn(new GraphResponse(
success: true,
data: [
[
'id' => 'policy-present',
'displayName' => 'Provider present',
'platform' => 'windows',
],
],
));
app(PolicySyncService::class)->syncPolicies($tenant, [
['type' => 'deviceConfiguration', 'platform' => 'windows'],
]);
$present->refresh();
$missing->refresh();
expect($present->display_name)->toBe('Provider present')
->and($present->ignored_at)->toBeNull()
->and($present->missing_from_provider_at)->toBeNull()
->and($missing->ignored_at)->toBeNull()
->and($missing->missing_from_provider_at)->not->toBeNull()
->and($missing->visibilityState())->toBe(Policy::VISIBILITY_PROVIDER_MISSING);
expect(AuditLog::query()
->where('tenant_id', $tenant->id)
->where('action', AuditActionId::PolicyProviderMissingDetected->value)
->where('resource_id', (string) $missing->getKey())
->exists())->toBeTrue();
});
it('clears provider missing on reappearance without clearing local ignore', function (): void {
$tenant = tenantWithDefaultMicrosoftConnectionForProviderMissing();
$policy = Policy::factory()->create([
'tenant_id' => $tenant->id,
'external_id' => 'policy-returned',
'policy_type' => 'deviceConfiguration',
'display_name' => 'Returned policy',
'ignored_at' => now()->subDay(),
'missing_from_provider_at' => now()->subDay(),
]);
mock(GraphLogger::class)
->shouldReceive('logRequest', 'logResponse')
->zeroOrMoreTimes()
->andReturnNull();
mock(GraphClientInterface::class)
->shouldReceive('listPolicies')
->once()
->with('deviceConfiguration', mockery::type('array'))
->andReturn(new GraphResponse(
success: true,
data: [
[
'id' => 'policy-returned',
'displayName' => 'Returned from provider',
'platform' => 'windows',
],
],
));
app(PolicySyncService::class)->syncPolicies($tenant, [
['type' => 'deviceConfiguration', 'platform' => 'windows'],
]);
$policy->refresh();
expect($policy->display_name)->toBe('Returned from provider')
->and($policy->ignored_at)->not->toBeNull()
->and($policy->missing_from_provider_at)->toBeNull()
->and($policy->visibilityState())->toBe(Policy::VISIBILITY_IGNORED_LOCALLY);
expect(AuditLog::query()
->where('tenant_id', $tenant->id)
->where('action', AuditActionId::PolicyProviderMissingCleared->value)
->where('resource_id', (string) $policy->getKey())
->exists())->toBeTrue();
});