TenantAtlas/tests/Unit/Support/Inventory/TenantCoverageTruthResolverTest.php
2026-04-05 14:18:37 +02:00

151 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\InventoryItem;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Support\Inventory\TenantCoverageTruthResolver;
use App\Support\Inventory\TenantCoverageTypeTruth;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('selects the latest completed coverage-bearing inventory sync run for tenant truth', function (): void {
$tenant = Tenant::factory()->create();
$olderBasis = createInventorySyncOperationRunWithCoverage($tenant, [
'deviceConfiguration' => ['status' => 'succeeded', 'item_count' => 1],
], attributes: [
'completed_at' => now()->subHours(2),
'outcome' => 'succeeded',
]);
createInventorySyncOperationRun($tenant, [
'status' => 'completed',
'outcome' => 'failed',
'completed_at' => now()->subMinute(),
]);
$latestCoverageBearing = createInventorySyncOperationRunWithCoverage($tenant, [
'deviceConfiguration' => ['status' => 'failed', 'item_count' => 2, 'error_code' => 'graph_forbidden'],
], attributes: [
'completed_at' => now()->subMinutes(5),
'outcome' => 'failed',
]);
expect(OperationRun::latestCompletedCoverageBearingInventorySyncForTenant((int) $tenant->getKey())?->is($latestCoverageBearing))
->toBeTrue()
->and(OperationRun::latestCompletedCoverageBearingInventorySyncForTenant((int) $tenant->getKey())?->is($olderBasis))
->toBeFalse();
$truth = app(TenantCoverageTruthResolver::class)->resolve($tenant);
expect($truth->basisRunId())->toBe((int) $latestCoverageBearing->getKey())
->and($truth->basisRunOutcome())->toBe('failed')
->and($truth->hasCurrentCoverageResult)->toBeTrue();
});
it('derives per-type coverage truth, observed counts, and deterministic follow-up order', function (): void {
$tenant = Tenant::factory()->create();
InventoryItem::factory()->count(3)->create([
'tenant_id' => (int) $tenant->getKey(),
'policy_type' => 'deviceCompliancePolicy',
]);
InventoryItem::factory()->count(2)->create([
'tenant_id' => (int) $tenant->getKey(),
'policy_type' => 'conditionalAccessPolicy',
]);
InventoryItem::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'policy_type' => 'deviceConfiguration',
]);
createInventorySyncOperationRunWithCoverage($tenant, [
'deviceCompliancePolicy' => [
'status' => 'failed',
'item_count' => 3,
'error_code' => 'graph_forbidden',
],
'deviceConfiguration' => [
'status' => 'succeeded',
'item_count' => 1,
],
'roleScopeTag' => [
'status' => 'skipped',
'item_count' => 0,
],
], foundationTypes: ['roleScopeTag'], attributes: [
'completed_at' => now(),
'outcome' => 'partially_succeeded',
]);
$truth = app(TenantCoverageTruthResolver::class)->resolve($tenant);
$rowsByType = collect($truth->rows)->keyBy(
static fn (TenantCoverageTypeTruth $row): string => $row->type,
);
expect($truth->supportedTypeCount)->toBeGreaterThanOrEqual(4)
->and($truth->succeededTypeCount)->toBe(1)
->and($truth->failedTypeCount)->toBe(1)
->and($truth->skippedTypeCount)->toBe(1)
->and($truth->unknownTypeCount)->toBe($truth->supportedTypeCount - 3)
->and($truth->followUpTypeCount)->toBe($truth->supportedTypeCount - 1)
->and($truth->observedItemTotal)->toBe(6);
expect($rowsByType['deviceCompliancePolicy'])
->coverageState->toBe('failed')
->basisErrorCode->toBe('graph_forbidden')
->basisItemCount->toBe(3)
->followUpGuidance->toBe('Review provider consent or permissions, then rerun inventory sync.');
expect($rowsByType['conditionalAccessPolicy'])
->coverageState->toBe('unknown')
->observedItemCount->toBe(2)
->followUpGuidance->toBe('No current basis result exists for this type. Run inventory sync to confirm coverage.');
expect($rowsByType['deviceConfiguration'])
->coverageState->toBe('succeeded')
->basisItemCount->toBe(1)
->followUpRequired->toBeFalse();
expect($rowsByType['roleScopeTag'])
->coverageState->toBe('skipped')
->segment->toBe('foundation')
->followUpGuidance->toBe('Run inventory sync again with the required types selected.');
$orderedTypes = array_map(
static fn (TenantCoverageTypeTruth $row): string => $row->type,
$truth->rows,
);
expect(array_search('deviceCompliancePolicy', $orderedTypes, true))->toBeLessThan(array_search('conditionalAccessPolicy', $orderedTypes, true))
->and(array_search('conditionalAccessPolicy', $orderedTypes, true))->toBeLessThan(array_search('roleScopeTag', $orderedTypes, true))
->and($truth->topPriorityFollowUpRow()?->type)->toBe('deviceCompliancePolicy');
});
it('returns unknown coverage rows when no basis run exists', function (): void {
$tenant = Tenant::factory()->create();
InventoryItem::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'policy_type' => 'deviceConfiguration',
]);
$truth = app(TenantCoverageTruthResolver::class)->resolve($tenant);
expect($truth->hasCurrentCoverageResult)->toBeFalse()
->and($truth->basisRunId())->toBeNull()
->and($truth->succeededTypeCount)->toBe(0)
->and($truth->failedTypeCount)->toBe(0)
->and($truth->skippedTypeCount)->toBe(0)
->and($truth->unknownTypeCount)->toBe($truth->supportedTypeCount)
->and($truth->followUpTypeCount)->toBe($truth->supportedTypeCount)
->and($truth->observedItemTotal)->toBe(1)
->and($truth->topPriorityFollowUpRow())->not->toBeNull();
});