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); $idempotencyKey = RunIdempotency::buildKey( tenantId: (int) $tenant->getKey(), operationType: 'drift.generate', targetId: $scopeKey, context: [ 'scope_key' => $scopeKey, 'baseline_run_id' => (int) $baseline->getKey(), 'current_run_id' => (int) $current->getKey(), ], ); $bulkRun = BulkOperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('idempotency_key', $idempotencyKey) ->latest('id') ->first(); expect($bulkRun)->not->toBeNull(); expect($bulkRun->resource)->toBe('drift'); expect($bulkRun->action)->toBe('generate'); expect($bulkRun->status)->toBe('pending'); Queue::assertPushed(GenerateDriftFindingsJob::class, function (GenerateDriftFindingsJob $job) use ($tenant, $user, $baseline, $current, $scopeKey, $bulkRun): 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->bulkOperationRunId === (int) $bulkRun->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); $idempotencyKey = RunIdempotency::buildKey( tenantId: (int) $tenant->getKey(), operationType: 'drift.generate', targetId: $scopeKey, context: [ 'scope_key' => $scopeKey, 'baseline_run_id' => (int) $baseline->getKey(), 'current_run_id' => (int) $current->getKey(), ], ); expect(BulkOperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('idempotency_key', $idempotencyKey) ->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(BulkOperationRun::query()->where('tenant_id', $tenant->getKey())->count())->toBe(0); });