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(); }); }); test('opening Drift dispatches generation when findings are missing', function () { Queue::fake(); [$user, $tenant] = createUserWithTenant(role: 'manager'); $this->actingAs($user); Filament::setTenant($tenant, true); $scopeKey = hash('sha256', 'scope-dispatch'); $baseline = InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDays(2), ]); $current = InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDay(), ]); Livewire::test(DriftLanding::class); $opRun = OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('type', 'drift.generate') ->latest('id') ->first(); expect($opRun)->not->toBeNull(); expect($opRun?->status)->toBe('queued'); $notifications = session('filament.notifications', []); expect($notifications)->not->toBeEmpty(); expect(collect($notifications)->last()['actions'][0]['url'] ?? null) ->toBe(OperationRunLinks::view($opRun, $tenant)); Queue::assertPushed(GenerateDriftFindingsJob::class, function (GenerateDriftFindingsJob $job) use ($tenant, $user, $baseline, $current, $scopeKey, $opRun): bool { return $job->tenantId === (int) $tenant->getKey() && $job->userId === (int) $user->getKey() && $job->baselineRunId === (int) $baseline->getKey() && $job->currentRunId === (int) $current->getKey() && $job->scopeKey === $scopeKey && $job->operationRun instanceof OperationRun && (int) $job->operationRun->getKey() === (int) $opRun?->getKey(); }); }); test('opening Drift is idempotent while a run is pending', function () { Queue::fake(); [$user, $tenant] = createUserWithTenant(role: 'manager'); $this->actingAs($user); Filament::setTenant($tenant, true); $scopeKey = hash('sha256', 'scope-idempotent'); $baseline = InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDays(2), ]); $current = InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDay(), ]); Livewire::test(DriftLanding::class); Livewire::test(DriftLanding::class); Queue::assertPushed(GenerateDriftFindingsJob::class, 1); expect(OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('type', 'drift.generate') ->count())->toBe(1); }); test('opening Drift does not dispatch generation when fewer than two successful runs exist', function () { Queue::fake(); [$user, $tenant] = createUserWithTenant(role: 'manager'); $this->actingAs($user); Filament::setTenant($tenant, true); $scopeKey = hash('sha256', 'scope-blocked'); InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDay(), ]); Livewire::test(DriftLanding::class); Queue::assertNothingPushed(); expect(OperationRun::query()->where('tenant_id', $tenant->getKey())->count())->toBe(0); }); test('opening Drift does not dispatch generation for readonly users', function () { Queue::fake(); [$user, $tenant] = createUserWithTenant(role: 'readonly'); $this->actingAs($user); Filament::setTenant($tenant, true); $scopeKey = hash('sha256', 'scope-readonly-blocked'); InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDays(2), ]); InventorySyncRun::factory()->for($tenant)->create([ 'selection_hash' => $scopeKey, 'status' => InventorySyncRun::STATUS_SUCCESS, 'finished_at' => now()->subDay(), ]); Livewire::test(DriftLanding::class); Queue::assertNothingPushed(); expect(OperationRun::query()->where('tenant_id', $tenant->getKey())->count())->toBe(0); });