102 lines
3.7 KiB
TypeScript
102 lines
3.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
// Cache-Abstraktion: getFreshValue direkt durchreichen (deterministisch, kein Cache-State zwischen Tests).
|
|
vi.mock("~/utils/cache.server", () => ({
|
|
cachified: vi.fn(async (opts: { getFreshValue: () => Promise<unknown> }) => opts.getFreshValue()),
|
|
clearCacheKeyPrefix: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("~/utils/entitlements/.server/productAccess.server", () => ({
|
|
resolveTenantProductAccess: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("~/utils/services/.server/subscriptionService", () => ({
|
|
getActiveTenantSubscriptions: vi.fn(async () => null),
|
|
}));
|
|
|
|
vi.mock("~/utils/services/.server/featureFlagEvaluator.server", () => ({
|
|
evaluateFlagForContext: vi.fn(),
|
|
}));
|
|
|
|
import { clearCacheKeyPrefix } from "~/utils/cache.server";
|
|
import { resolveTenantProductAccess } from "~/utils/entitlements/.server/productAccess.server";
|
|
import { getActiveTenantSubscriptions } from "~/utils/services/.server/subscriptionService";
|
|
import { evaluateFlagForContext } from "~/utils/services/.server/featureFlagEvaluator.server";
|
|
import { invalidateTenantModuleAccessCache, tenantHasFlag, tenantHasModule } from "./tenantConnector.server";
|
|
|
|
const resolveMock = vi.mocked(resolveTenantProductAccess);
|
|
const subscriptionMock = vi.mocked(getActiveTenantSubscriptions);
|
|
const flagMock = vi.mocked(evaluateFlagForContext);
|
|
const clearPrefixMock = vi.mocked(clearCacheKeyPrefix);
|
|
|
|
describe("tenantHasModule", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
subscriptionMock.mockResolvedValue(null as never);
|
|
});
|
|
|
|
it("returns true when the product-access decision allows the module", async () => {
|
|
resolveMock.mockResolvedValue({ allowed: true } as never);
|
|
|
|
const allowed = await tenantHasModule({ tenantId: "t1", userId: "u1", moduleKey: "crm-basic" });
|
|
|
|
expect(allowed).toBe(true);
|
|
expect(subscriptionMock).toHaveBeenCalledWith("t1");
|
|
expect(resolveMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({ tenantId: "t1", userId: "u1", featureKey: "crm-basic" })
|
|
);
|
|
});
|
|
|
|
it("returns false when the product-access decision denies the module", async () => {
|
|
resolveMock.mockResolvedValue({ allowed: false } as never);
|
|
|
|
const allowed = await tenantHasModule({ tenantId: "t1", userId: "u1", moduleKey: "kwp" });
|
|
|
|
expect(allowed).toBe(false);
|
|
});
|
|
|
|
it("forwards the bypassTenantRolePermission flag to the resolver", async () => {
|
|
resolveMock.mockResolvedValue({ allowed: true } as never);
|
|
|
|
await tenantHasModule({ tenantId: "t1", userId: "u1", moduleKey: "crm-basic", bypassTenantRolePermission: true });
|
|
|
|
expect(resolveMock).toHaveBeenCalledWith(expect.objectContaining({ bypassTenantRolePermission: true }));
|
|
});
|
|
});
|
|
|
|
describe("tenantHasFlag", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("returns the evaluator's enabled boolean", async () => {
|
|
flagMock.mockResolvedValue({ enabled: true } as never);
|
|
|
|
const enabled = await tenantHasFlag({ flagKey: "new-dashboard", tenantId: "t1", userId: "u1" });
|
|
|
|
expect(enabled).toBe(true);
|
|
expect(flagMock).toHaveBeenCalledWith("new-dashboard", { flagName: "new-dashboard", tenantId: "t1", userId: "u1" });
|
|
});
|
|
|
|
it("normalizes missing tenant/user context to null", async () => {
|
|
flagMock.mockResolvedValue({ enabled: false } as never);
|
|
|
|
const enabled = await tenantHasFlag({ flagKey: "beta" });
|
|
|
|
expect(enabled).toBe(false);
|
|
expect(flagMock).toHaveBeenCalledWith("beta", { flagName: "beta", tenantId: null, userId: null });
|
|
});
|
|
});
|
|
|
|
describe("invalidateTenantModuleAccessCache", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("clears every cached module decision for the tenant", () => {
|
|
invalidateTenantModuleAccessCache("t1");
|
|
|
|
expect(clearPrefixMock).toHaveBeenCalledWith("connector:module:t1:");
|
|
});
|
|
});
|