- Implement Spec 116 baseline capture/compare + coverage guard\n- Add UI surfaces and widgets for baseline compare\n- Add tests and research report
105 lines
3.9 KiB
PHP
105 lines
3.9 KiB
PHP
<?php
|
|
|
|
use App\Filament\Resources\InventoryItemResource\Pages\ListInventoryItems;
|
|
use App\Jobs\RunInventorySyncJob;
|
|
use App\Models\OperationRun;
|
|
use App\Services\Graph\GraphClientInterface;
|
|
use App\Services\Inventory\InventorySyncService;
|
|
use App\Support\OperationRunLinks;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Livewire\Livewire;
|
|
|
|
it('enqueues inventory sync and creates a canonical operation run without calling Graph in request', function () {
|
|
Queue::fake();
|
|
|
|
$this->mock(GraphClientInterface::class, function ($mock): void {
|
|
$mock->shouldReceive('listPolicies')->never();
|
|
$mock->shouldReceive('getPolicy')->never();
|
|
$mock->shouldReceive('getOrganization')->never();
|
|
$mock->shouldReceive('applyPolicy')->never();
|
|
$mock->shouldReceive('getServicePrincipalPermissions')->never();
|
|
$mock->shouldReceive('request')->never();
|
|
});
|
|
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$this->actingAs($user);
|
|
|
|
$tenant->makeCurrent();
|
|
Filament::setTenant($tenant, true);
|
|
|
|
$sync = app(InventorySyncService::class);
|
|
$policyTypes = array_slice($sync->defaultSelectionPayload()['policy_types'] ?? [], 0, 2);
|
|
|
|
Livewire::test(ListInventoryItems::class)
|
|
->callAction('run_inventory_sync', data: [
|
|
'policy_types' => $policyTypes,
|
|
'include_foundations' => false,
|
|
'include_dependencies' => false,
|
|
]);
|
|
|
|
$opRun = OperationRun::query()
|
|
->where('tenant_id', $tenant->getKey())
|
|
->where('type', 'inventory_sync')
|
|
->latest('id')
|
|
->first();
|
|
|
|
expect($opRun)->not->toBeNull();
|
|
expect($opRun?->status)->toBe('queued');
|
|
expect($opRun?->outcome)->toBe('pending');
|
|
|
|
$notifications = session('filament.notifications', []);
|
|
expect($notifications)->not->toBeEmpty();
|
|
expect(collect($notifications)->last()['actions'][0]['url'] ?? null)
|
|
->toBe(OperationRunLinks::view($opRun, $tenant));
|
|
|
|
$capturedJob = null;
|
|
|
|
Queue::assertPushed(RunInventorySyncJob::class, function (RunInventorySyncJob $job) use (&$capturedJob, $tenant, $user, $opRun): bool {
|
|
$capturedJob = $job;
|
|
|
|
return $job->tenantId === (int) $tenant->getKey()
|
|
&& $job->userId === (int) $user->getKey()
|
|
&& $job->operationRun instanceof OperationRun
|
|
&& (int) $job->operationRun->getKey() === (int) $opRun?->getKey();
|
|
});
|
|
|
|
expect($capturedJob)->toBeInstanceOf(RunInventorySyncJob::class);
|
|
|
|
$mockSync = \Mockery::mock(InventorySyncService::class);
|
|
$mockSync
|
|
->shouldReceive('executeSelection')
|
|
->once()
|
|
->andReturnUsing(function (OperationRun $operationRun, $tenant, array $selectionPayload, ?callable $onPolicyTypeProcessed): array {
|
|
$policyTypes = $selectionPayload['policy_types'] ?? [];
|
|
$policyTypes = is_array($policyTypes) ? array_values(array_filter(array_map('strval', $policyTypes))) : [];
|
|
|
|
foreach ($policyTypes as $policyType) {
|
|
$onPolicyTypeProcessed && $onPolicyTypeProcessed($policyType, true, null);
|
|
}
|
|
|
|
return [
|
|
'status' => 'success',
|
|
'had_errors' => false,
|
|
'error_codes' => [],
|
|
'error_context' => [],
|
|
'errors_count' => 0,
|
|
'items_observed_count' => 0,
|
|
'items_upserted_count' => 0,
|
|
'processed_policy_types' => $policyTypes,
|
|
'failed_policy_types' => [],
|
|
'skipped_policy_types' => [],
|
|
];
|
|
});
|
|
|
|
$capturedJob->handle($mockSync, app(\App\Services\Intune\AuditLogger::class), app(\App\Services\OperationRunService::class));
|
|
|
|
$opRun->refresh();
|
|
|
|
$context = is_array($opRun->context) ? $opRun->context : [];
|
|
$coverage = $context['inventory']['coverage']['policy_types'] ?? null;
|
|
|
|
expect($coverage)->toBeArray();
|
|
expect(array_keys($coverage))->toEqualCanonicalizing($policyTypes);
|
|
});
|