create(); $this->actingAs($user); expect(TenantResource::canCreate())->toBeFalse(); }); it('cannot be created via CRUD (onboarding wizard is the only path)', function () { [$user] = createUserWithTenant(role: 'manager'); $this->actingAs($user); expect(TenantResource::canCreate())->toBeFalse(); }); it('can be edited by managers (TENANT_MANAGE)', function () { [$user, $tenant] = createUserWithTenant(role: 'manager'); $this->actingAs($user); expect(TenantResource::canEdit($tenant))->toBeTrue(); }); it('cannot be deleted by managers (TENANT_DELETE)', function () { [$user, $tenant] = createUserWithTenant(role: 'manager'); $this->actingAs($user); expect(TenantResource::canDelete($tenant))->toBeFalse(); }); it('can be deleted by owners (TENANT_DELETE)', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); expect(TenantResource::canDelete($tenant))->toBeTrue(); }); it('cannot edit tenants it cannot access', function () { [$user] = createUserWithTenant(role: 'manager'); $otherTenant = Tenant::factory()->create(); $this->actingAs($user); expect(TenantResource::canEdit($otherTenant))->toBeFalse(); }); it('does not grant lifecycle mutation abilities for inaccessible tenants regardless of lifecycle state', function (\Closure $tenantFactory): void { [$user] = createUserWithTenant(role: 'owner', ensureDefaultMicrosoftProviderConnection: false); $otherTenant = $tenantFactory(); $this->actingAs($user); expect(TenantResource::canEdit($otherTenant))->toBeFalse() ->and(TenantResource::canDelete($otherTenant))->toBeFalse(); })->with([ 'draft' => [fn (): Tenant => Tenant::factory()->draft()->create()], 'onboarding' => [fn (): Tenant => Tenant::factory()->onboarding()->create()], 'active' => [fn (): Tenant => Tenant::factory()->active()->create()], 'archived' => [fn (): Tenant => Tenant::factory()->archived()->create()], ]); it('keeps onboarding and archived tenants manageable when the actor is entitled', function () { $onboardingTenant = Tenant::factory()->onboarding()->create(); [$user, $onboardingTenant] = createUserWithTenant( tenant: $onboardingTenant, role: 'manager', ensureDefaultMicrosoftProviderConnection: false, ); $archivedTenant = Tenant::factory()->archived()->create([ 'workspace_id' => (int) $onboardingTenant->workspace_id, ]); createUserWithTenant( tenant: $archivedTenant, user: $user, role: 'manager', workspaceRole: 'manager', ensureDefaultMicrosoftProviderConnection: false, ); $archivedTenant = Tenant::withTrashed()->findOrFail((int) $archivedTenant->getKey()); $this->actingAs($user); expect(TenantResource::canEdit($onboardingTenant))->toBeTrue() ->and(TenantResource::canEdit($archivedTenant))->toBeTrue() ->and(TenantResource::canDelete($onboardingTenant))->toBeFalse() ->and(TenantResource::canDelete($archivedTenant))->toBeFalse(); }); it('keeps mutation capability checks separate from lifecycle-specific action visibility', function (): void { $onboardingTenant = Tenant::factory()->onboarding()->create(); [$user, $onboardingTenant] = createUserWithTenant( tenant: $onboardingTenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $archivedTenant = Tenant::factory()->archived()->create([ 'workspace_id' => (int) $onboardingTenant->workspace_id, ]); createUserWithTenant( tenant: $archivedTenant, user: $user, role: 'owner', workspaceRole: 'owner', ensureDefaultMicrosoftProviderConnection: false, ); $archivedTenant = Tenant::withTrashed()->findOrFail((int) $archivedTenant->getKey()); $this->actingAs($user); expect(TenantResource::canDelete($onboardingTenant))->toBeTrue() ->and(TenantResource::canDelete($archivedTenant))->toBeTrue(); }); it('keeps the tenant resource index usable with no selected tenant context', function () { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(TenantResource::getUrl(panel: 'admin')) ->assertSuccessful() ->assertSee((string) $tenant->name); }); it('resolves archive and verify actions as enabled for owners on active tenant surfaces', function (): void { $tenant = Tenant::factory()->active()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); Livewire::actingAs($user) ->test(ListTenants::class) ->assertTableActionVisible('archive', $tenant) ->assertTableActionEnabled('archive', $tenant) ->assertTableActionVisible('verify', $tenant) ->assertTableActionEnabled('verify', $tenant); Filament::setTenant(null, true); Livewire::actingAs($user) ->test(ViewTenant::class, ['record' => $tenant->getRouteKey()]) ->assertActionVisible('archive') ->assertActionEnabled('archive') ->assertActionVisible('verify') ->assertActionEnabled('verify'); }); it('keeps lifecycle mutation actions visible but disabled for managers without delete capability', function (): void { $tenant = Tenant::factory()->active()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'manager', ensureDefaultMicrosoftProviderConnection: false); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); Livewire::actingAs($user) ->test(ListTenants::class) ->assertTableActionVisible('archive', $tenant) ->assertTableActionDisabled('archive', $tenant); Filament::setTenant(null, true); Livewire::actingAs($user) ->test(ViewTenant::class, ['record' => $tenant->getRouteKey()]) ->assertActionVisible('archive') ->assertActionDisabled('archive'); }); });