actingAs($manager); Filament::setTenant($tenant, true); expect(TenantMembership::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('role', 'owner') ->count())->toBe(0); Livewire::test(TenantDiagnostics::class) ->assertSee('Missing owner') ->assertActionVisible('bootstrapOwner') ->assertActionEnabled('bootstrapOwner') ->mountAction('bootstrapOwner') ->callMountedAction() ->assertSuccessful(); expect(TenantMembership::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('role', 'owner') ->count())->toBe(1); expect(AuditLog::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('action', AuditActionId::TenantMembershipBootstrapRecover->value) ->exists())->toBeTrue(); }); it('shows repair actions as disabled for readonly members', function () { [$readonly, $tenant] = createUserWithTenant(role: 'readonly'); $this->actingAs($readonly); Filament::setTenant($tenant, true); // Force missing-owner state. TenantMembership::query() ->where('tenant_id', (int) $tenant->getKey()) ->update(['role' => 'readonly']); Livewire::test(TenantDiagnostics::class) ->assertActionVisible('bootstrapOwner') ->assertActionDisabled('bootstrapOwner') ->assertActionExists('bootstrapOwner', function (Action $action): bool { return $action->getTooltip() === UiTooltips::INSUFFICIENT_PERMISSION; }); }); it('merges duplicate memberships for the current user (diagnostics repair)', function () { [$owner, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($owner); Filament::setTenant($tenant, true); // Intentionally create a broken state by temporarily dropping the unique uniqueness enforcement. // Tests typically run on SQLite, which uses a unique index rather than a named table constraint. $driver = DB::getDriverName(); if ($driver === 'sqlite') { DB::statement('DROP INDEX tenant_memberships_tenant_id_user_id_unique'); } else { DB::statement('ALTER TABLE tenant_memberships DROP CONSTRAINT tenant_memberships_tenant_id_user_id_unique'); } TenantMembership::query()->create([ 'tenant_id' => (int) $tenant->getKey(), 'user_id' => (int) $owner->getKey(), 'role' => 'readonly', 'source' => 'manual', 'created_by_user_id' => (int) $owner->getKey(), ]); expect(TenantMembership::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('user_id', (int) $owner->getKey()) ->count())->toBeGreaterThan(1); Livewire::test(TenantDiagnostics::class) ->assertActionVisible('mergeDuplicateMemberships') ->assertActionEnabled('mergeDuplicateMemberships') ->mountAction('mergeDuplicateMemberships') ->callMountedAction() ->assertSuccessful(); expect(TenantMembership::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('user_id', (int) $owner->getKey()) ->count())->toBe(1); expect(AuditLog::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('action', AuditActionId::TenantMembershipDuplicatesMerged->value) ->exists())->toBeTrue(); }); });