import { describe, expect, it } from "@suaveplan/testing/runner"; import type { Tenant } from "../types/tenant.js"; import { createTenantContext, NullTenantContext } from "./context.js"; function makeTenant(over: Partial = {}): Tenant { return { id: "t_1", name: "Acme", slug: "acme", status: "active", settings: {}, enabledPlugins: ["payment"], domains: [], createdAt: new Date(0), updatedAt: new Date(0), ...over, }; } describe("createTenantContext", () => { it("exposes tenant and tenantId", { timeout: 5000 }, () => { const ctx = createTenantContext(makeTenant()); expect(ctx.tenant.slug).toBe("acme"); expect(ctx.tenantId).toBe("t_1"); }); it("isPluginEnabled honours enabledPlugins", { timeout: 5000 }, () => { const ctx = createTenantContext(makeTenant()); expect(ctx.isPluginEnabled("payment")).toBe(true); expect(ctx.isPluginEnabled("ghost")).toBe(false); }); it("getSetting walks dot-paths and returns default", { timeout: 5000, }, () => { const ctx = createTenantContext( makeTenant({ settings: { custom: { nested: { value: 42 } }, timezone: "UTC", }, }) ); expect(ctx.getSetting("custom.nested.value")).toBe(42); expect(ctx.getSetting("timezone")).toBe("UTC"); expect(ctx.getSetting("missing.path", 7)).toBe(7); expect(ctx.getSetting("missing.path")).toBeUndefined(); }); it("getSetting returns default when leaf is nullish", { timeout: 5000, }, () => { const ctx = createTenantContext( makeTenant({ settings: { custom: { explicitNull: null, zero: 0, } as Record, }, }) ); expect(ctx.getSetting("custom.explicitNull", 42)).toBe(42); // Leaf 0 is non-nullish; default ignored. expect(ctx.getSetting("custom.zero", 9)).toBe(0); }); it("getSetting returns default when intermediate is null", { timeout: 5000, }, () => { const ctx = createTenantContext( makeTenant({ settings: { custom: { nullSlot: null } as Record, }, }) ); expect(ctx.getSetting("custom.nullSlot.x", 9)).toBe(9); }); it("isFeatureEnabled reads features boolean", { timeout: 5000 }, () => { const ctx = createTenantContext( makeTenant({ settings: { features: { x: true, y: false } } }) ); expect(ctx.isFeatureEnabled("x")).toBe(true); expect(ctx.isFeatureEnabled("y")).toBe(false); expect(ctx.isFeatureEnabled("missing")).toBe(false); }); it("isFeatureEnabled is false when no features map", { timeout: 5000, }, () => { const ctx = createTenantContext(makeTenant()); expect(ctx.isFeatureEnabled("x")).toBe(false); }); it("getLimit reads limits map", { timeout: 5000 }, () => { const ctx = createTenantContext( makeTenant({ settings: { limits: { maxUsers: 10, maxApiCalls: 1000 } }, }) ); expect(ctx.getLimit("maxUsers")).toBe(10); expect(ctx.getLimit("maxApiCalls")).toBe(1000); expect(ctx.getLimit("unknownKey")).toBeUndefined(); }); it("getLimit returns undefined when no limits", { timeout: 5000 }, () => { const ctx = createTenantContext(makeTenant()); expect(ctx.getLimit("maxUsers")).toBeUndefined(); }); }); describe("NullTenantContext", () => { it("NullTenantContext fail-closes plugin check", { timeout: 5000 }, () => { expect(NullTenantContext.isPluginEnabled("payment")).toBe(false); expect(NullTenantContext.isPluginEnabled("anything")).toBe(false); }); it("NullTenantContext fail-closes feature check", { timeout: 5000, }, () => { expect(NullTenantContext.isFeatureEnabled("x")).toBe(false); }); it("getSetting returns supplied default", { timeout: 5000 }, () => { expect(NullTenantContext.getSetting("any.key", 7)).toBe(7); expect(NullTenantContext.getSetting("any.key")).toBeUndefined(); }); it("getLimit returns undefined", { timeout: 5000 }, () => { expect(NullTenantContext.getLimit("maxUsers")).toBeUndefined(); }); it("tenantId is empty string", { timeout: 5000 }, () => { expect(NullTenantContext.tenantId).toBe(""); }); it("is frozen", { timeout: 5000 }, () => { expect(Object.isFrozen(NullTenantContext)).toBe(true); }); });