actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::test(ListPolicies::class) ->mountAction('sync') ->callMountedAction() ->assertHasNoActionErrors(); $requestedTypes = array_map( static fn (array $typeConfig): string => (string) $typeConfig['type'], config('tenantpilot.supported_policy_types', []) ); sort($requestedTypes); $run = OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('type', 'policy.sync') ->latest('id') ->first(); expect($run)->not->toBeNull(); expect($run?->status)->toBe('queued'); expect($run?->context)->toMatchArray([ 'scope' => 'all', 'types' => $requestedTypes, ]); Queue::assertPushed(SyncPoliciesJob::class, function (SyncPoliciesJob $job) use ($tenant, $run, $requestedTypes): bool { return $job->tenantId === (int) $tenant->getKey() && $job->types === $requestedTypes && $job->operationRun instanceof OperationRun && (int) $job->operationRun->getKey() === (int) $run?->getKey(); }); }); it('reuses an active policy sync run and does not enqueue twice', function () { Queue::fake(); [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::test(ListPolicies::class) ->mountAction('sync') ->callMountedAction(); Livewire::test(ListPolicies::class) ->mountAction('sync') ->callMountedAction(); Queue::assertPushed(SyncPoliciesJob::class, 1); expect(OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('type', 'policy.sync') ->count())->toBe(1); }); it('queues row policy sync and creates a canonical operation run (no Graph calls in request)', function () { Queue::fake(); bindFailHardGraphClient(); [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); $policy = Policy::factory()->create([ 'tenant_id' => $tenant->getKey(), 'ignored_at' => null, ]); Livewire::test(ListPolicies::class) ->callTableAction('sync', $policy) ->assertHasNoTableActionErrors(); $run = OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('type', 'policy.sync_one') ->latest('id') ->first(); expect($run)->not->toBeNull(); expect($run?->status)->toBe('queued'); expect($run?->context)->toMatchArray([ 'scope' => 'one', 'policy_id' => (int) $policy->getKey(), ]); Queue::assertPushed(SyncPoliciesJob::class, function (SyncPoliciesJob $job) use ($tenant, $run, $policy): bool { return $job->tenantId === (int) $tenant->getKey() && $job->types === null && $job->policyIds === [(int) $policy->getKey()] && $job->operationRun instanceof OperationRun && (int) $job->operationRun->getKey() === (int) $run?->getKey(); }); }); it('queues bulk policy sync and creates a canonical operation run (no Graph calls in request)', function () { Queue::fake(); bindFailHardGraphClient(); [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); $policies = Policy::factory()->count(2)->create([ 'tenant_id' => $tenant->getKey(), 'ignored_at' => null, ]); Livewire::test(ListPolicies::class) ->callTableBulkAction('bulk_sync', $policies) ->assertHasNoTableBulkActionErrors(); $selectedIds = $policies ->pluck('id') ->map(static fn ($id): int => (int) $id) ->sort() ->values() ->all(); $run = OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->where('type', 'policy.sync') ->latest('id') ->first(); expect($run)->not->toBeNull(); expect($run?->status)->toBe('queued'); expect($run?->context)->toMatchArray([ 'scope' => 'subset', 'policy_ids' => $selectedIds, ]); Queue::assertPushed(SyncPoliciesJob::class, function (SyncPoliciesJob $job) use ($tenant, $run, $selectedIds): bool { return $job->tenantId === (int) $tenant->getKey() && $job->types === null && $job->policyIds === $selectedIds && $job->operationRun instanceof OperationRun && (int) $job->operationRun->getKey() === (int) $run?->getKey(); }); }); it('hides policy sync start action for readonly users', function () { Queue::fake(); [$user, $tenant] = createUserWithTenant(role: 'readonly'); $this->actingAs($user); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::test(ListPolicies::class) ->assertActionHidden('sync'); Queue::assertNothingPushed(); });