TenantAtlas/tests/Feature/Inventory/InventorySyncStartSurfaceTest.php
ahmido 7620144ab6 Spec 116: Baseline drift engine v1 (meta fidelity + coverage guard) (#141)
Implements Spec 116 baseline drift engine v1 (meta fidelity) with coverage guard, stable finding identity, and Filament UI surfaces.

Highlights
- Baseline capture/compare jobs and supporting services (meta contract hashing via InventoryMetaContract + DriftHasher)
- Coverage proof parsing + compare partial outcome behavior
- Filament pages/resources/widgets for baseline compare + drift landing improvements
- Pest tests for capture/compare/coverage guard and UI start surfaces
- Research report: docs/research/golden-master-baseline-drift-deep-analysis.md

Validation
- `vendor/bin/sail bin pint --dirty`
- `vendor/bin/sail artisan test --compact --filter="Baseline"`

Notes
- No destructive user actions added; compare/capture remain queued jobs.
- Provider registration unchanged (Laravel 11+/12 uses bootstrap/providers.php for panel providers; not touched here).

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #141
2026-03-02 22:02:58 +00:00

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);
});