Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/connector/tenantConnector.server.test.ts
T

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:");
});
});