// @vitest-environment node import { describe, expect, it, vi } from "vitest"; import { TenantUserType } from "~/application/enums/tenants/TenantUserType"; type Deferred = { promise: Promise; resolve: (value: T) => void; }; function deferred(): Deferred { let resolve!: (value: T) => void; const promise = new Promise((done) => { resolve = done; }); return { promise, resolve }; } const mocks = vi.hoisted(() => { const postDetails = { tenantUser: deferred<{ type: TenantUserType }>(), tenantTypes: deferred(), credits: deferred(), productAccess: deferred<{ allowed: boolean }>(), }; return { postDetails }; }); vi.mock("~/utils/services/.server/urlService", () => ({ getTenantIdFromUrl: vi.fn(async () => "tenant_1") })); vi.mock("~/utils/session.server", () => ({ getUserInfo: vi.fn(async () => ({ userId: "user_1", lng: "en" })) })); vi.mock("@toolctrl/db/auth-boundary.server", () => ({ getAuthUserWithoutPasswordById: vi.fn(async () => ({ id: "user_1", admin: false, locale: "en" })), getTenantsForUser: vi.fn(async () => [{ id: "tenant_1", slug: "acme", name: "Acme" }]), })); vi.mock("~/utils/db/tenants.db.server", () => ({ adminGetAllActiveTenantSimple: vi.fn(async () => []), getTenantSimple: vi.fn(async () => ({ id: "tenant_1", slug: "acme", name: "Acme", deactivatedReason: null })), getTenantUserType: vi.fn(() => mocks.postDetails.tenantUser.promise), })); vi.mock("@toolctrl/config", () => ({ loadRequiredWebConfig: vi.fn(() => ({ routing: {} })) })); vi.mock("~/utils/db/entities/entities.db.server", () => ({ getAllEntities: vi.fn(async () => []) })); vi.mock("~/utils/db/entities/entityGroups.db.server", () => ({ getAllEntityGroups: vi.fn(async () => []) })); vi.mock("~/utils/db/permissions/groups.db.server", () => ({ getMyGroups: vi.fn(async () => []) })); vi.mock("~/utils/db/permissions/roles.db.server", () => ({ getAllRolesWithoutPermissions: vi.fn(async () => []) })); vi.mock("~/utils/db/permissions/userRoles.db.server", () => ({ getPermissionsByUser: vi.fn(async () => []), getUserRoleInAdmin: vi.fn(async () => null), getUserRoleInTenant: vi.fn(async () => null), })); vi.mock("~/utils/db/tenants/tenantTypes.db.server", () => ({ getAllTenantTypes: vi.fn(() => mocks.postDetails.tenantTypes.promise) })); vi.mock("~/utils/services/.server/subscriptionService", () => ({ getActiveTenantSubscriptions: vi.fn(async () => ({ products: [] })), getPlanFeatureUsage: vi.fn(() => mocks.postDetails.credits.promise), })); vi.mock("~/modules/onboarding/services/OnboardingService", () => ({ default: { getUserActiveOnboarding: vi.fn(async () => null) } })); vi.mock("~/utils/entitlements/.server/productAccess.server", () => ({ resolveTenantProductAccess: vi.fn(() => mocks.postDetails.productAccess.promise), })); import { getTenantUserType } from "~/utils/db/tenants.db.server"; import { getAllTenantTypes } from "~/utils/db/tenants/tenantTypes.db.server"; import { getPlanFeatureUsage } from "~/utils/services/.server/subscriptionService"; import { resolveTenantProductAccess } from "~/utils/entitlements/.server/productAccess.server"; import { loadAppData } from "./appData"; async function flushAsyncWork() { await new Promise((resolve) => setImmediate(resolve)); } describe("loadAppData performance", () => { it("starts post-detail shell lookups in parallel", async () => { const result = loadAppData({ request: new Request("https://app.example.test/acme/dashboard"), params: { tenant: "acme" }, t: ((key: string) => key) as never, }); await flushAsyncWork(); expect(getTenantUserType).toHaveBeenCalledWith("user_1", "tenant_1"); expect(getAllTenantTypes).toHaveBeenCalled(); expect(getPlanFeatureUsage).toHaveBeenCalledWith("tenant_1", "ai-credits"); expect(resolveTenantProductAccess).toHaveBeenCalled(); mocks.postDetails.tenantUser.resolve({ type: TenantUserType.MEMBER }); mocks.postDetails.tenantTypes.resolve([]); mocks.postDetails.credits.resolve(undefined); mocks.postDetails.productAccess.resolve({ allowed: true }); await expect(result).resolves.toMatchObject({ currentRole: TenantUserType.MEMBER, tenantTypes: [], productAccess: expect.any(Object), }); }); });