// @vitest-environment node import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("@toolctrl/config", async () => { const actual = await vi.importActual("@toolctrl/config"); return { ...actual, loadRequiredWebConfig: () => ({ nodeEnv: "production" }), }; }); vi.mock("./db/tenants.db.server", () => ({ getTenantUserType: vi.fn(), })); vi.mock("./services/.server/urlService", async () => { const actual = await vi.importActual("./services/.server/urlService"); return { ...actual, getTenantIdFromUrl: vi.fn(), }; }); vi.mock("./session.server", () => ({ getUserInfo: vi.fn(), })); vi.mock("./db.server", () => ({ db: { adminUser: { findUnique: vi.fn(), }, }, })); vi.mock("./db/permissions/userRoles.db.server", () => ({ getUserRoleInTenant: vi.fn(), })); vi.mock("./cache.server", () => ({ cachified: vi.fn(), clearCacheKey: vi.fn(), })); import { getTenantUserType } from "./db/tenants.db.server"; import { db } from "./db.server"; import { getUserRoleInTenant } from "./db/permissions/userRoles.db.server"; import { getTenantIdFromUrl } from "./services/.server/urlService"; import { getUserInfo } from "./session.server"; import { requireAuth } from "./loaders.middleware"; const env = { APP_BASE_DOMAIN: "localhost:3000", WEB_APP_HOST: "app.localhost:3000", ADMIN_APP_HOST: "admin.localhost:3000", API_HOST: "api.localhost:3000", APP_PROTOCOL: "http", }; const userSession = { userId: "user-1", lightOrDarkMode: "light", lng: "en", cookies: [] }; function makeRequest(host: string, pathname: string) { return new Request(`http://localhost:3000${pathname}`, { headers: { host, }, }); } async function expectUnauthorized(promise: Promise) { try { await promise; throw new Error("Expected unauthorized response"); } catch (error) { expect(error).toBeInstanceOf(Response); expect((error as Response).status).toBe(401); } } describe("requireAuth tenant-source routing", () => { beforeEach(() => { vi.stubEnv("APP_BASE_DOMAIN", env.APP_BASE_DOMAIN); vi.stubEnv("WEB_APP_HOST", env.WEB_APP_HOST); vi.stubEnv("ADMIN_APP_HOST", env.ADMIN_APP_HOST); vi.stubEnv("API_HOST", env.API_HOST); vi.stubEnv("APP_PROTOCOL", env.APP_PROTOCOL); vi.clearAllMocks(); vi.mocked(getUserInfo).mockResolvedValue(userSession); vi.mocked(getTenantIdFromUrl).mockResolvedValue("tenant-1"); vi.mocked(getTenantUserType).mockResolvedValue({ status: 2, type: 1 }); vi.mocked(db.adminUser.findUnique).mockResolvedValue(null); vi.mocked(getUserRoleInTenant).mockResolvedValue(null); }); it("requires tenant membership for app-host tenant route params", async () => { await expect(requireAuth({ request: makeRequest(env.WEB_APP_HOST, "/acme/dashboard"), params: { tenant: "acme" } })).resolves.toEqual( userSession, ); expect(getTenantIdFromUrl).toHaveBeenCalledWith({ tenant: "acme" }); expect(getTenantUserType).toHaveBeenCalledWith("user-1", "tenant-1"); }); it("rejects non-member app-host tenant requests", async () => { vi.mocked(getTenantUserType).mockResolvedValueOnce(null); await expectUnauthorized(requireAuth({ request: makeRequest(env.WEB_APP_HOST, "/acme/dashboard"), params: { tenant: "acme" } })); expect(getTenantUserType).toHaveBeenCalledWith("user-1", "tenant-1"); expect(db.adminUser.findUnique).toHaveBeenCalledWith({ where: { userId: "user-1" } }); }); it("allows operator admins to inspect app-host tenant requests without membership", async () => { vi.mocked(getTenantUserType).mockResolvedValueOnce(null); vi.mocked(db.adminUser.findUnique).mockResolvedValueOnce({ userId: "user-1" }); await expect(requireAuth({ request: makeRequest(env.WEB_APP_HOST, "/acme/dashboard"), params: { tenant: "acme" } })).resolves.toEqual(userSession); expect(getTenantUserType).toHaveBeenCalledWith("user-1", "tenant-1"); expect(db.adminUser.findUnique).toHaveBeenCalledWith({ where: { userId: "user-1" } }); }); it("keeps admin-host paths on the super-admin branch", async () => { vi.mocked(db.adminUser.findUnique).mockResolvedValueOnce({ userId: "user-1" }); await expect(requireAuth({ request: makeRequest(env.ADMIN_APP_HOST, "/settings"), params: {} })).resolves.toEqual({ ...userSession, isSuperAdmin: { userId: "user-1" }, }); expect(db.adminUser.findUnique).toHaveBeenCalledWith({ where: { userId: "user-1" } }); expect(getTenantUserType).not.toHaveBeenCalled(); }); it("does not classify admin-prefixed non-admin paths as admin host routes", async () => { await expect(requireAuth({ request: makeRequest(env.WEB_APP_HOST, "/adminish/dashboard"), params: { tenant: "adminish" } })).resolves.toEqual( userSession, ); expect(getTenantIdFromUrl).toHaveBeenCalledWith({ tenant: "adminish" }); expect(getTenantUserType).toHaveBeenCalledWith("user-1", "tenant-1"); expect(db.adminUser.findUnique).not.toHaveBeenCalled(); }); });