mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-05 13:20:03 -06:00
160 lines
5.2 KiB
TypeScript
160 lines
5.2 KiB
TypeScript
import KeyvRedis from "@keyv/redis";
|
|
import { createCache } from "cache-manager";
|
|
import { Keyv } from "keyv";
|
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
import { logger } from "@formbricks/logger";
|
|
|
|
// Mock dependencies
|
|
vi.mock("keyv");
|
|
vi.mock("@keyv/redis");
|
|
vi.mock("cache-manager");
|
|
vi.mock("@formbricks/logger");
|
|
|
|
const mockCacheInstance = {
|
|
set: vi.fn(),
|
|
get: vi.fn(),
|
|
del: vi.fn(),
|
|
};
|
|
|
|
describe("Cache Service", () => {
|
|
let originalRedisUrl: string | undefined;
|
|
let originalNextRuntime: string | undefined;
|
|
|
|
beforeEach(() => {
|
|
originalRedisUrl = process.env.REDIS_URL;
|
|
originalNextRuntime = process.env.NEXT_RUNTIME;
|
|
|
|
// Ensure we're in runtime mode (not build time)
|
|
process.env.NEXT_RUNTIME = "nodejs";
|
|
|
|
vi.resetAllMocks();
|
|
vi.resetModules();
|
|
|
|
// Setup default mock implementations
|
|
vi.mocked(createCache).mockReturnValue(mockCacheInstance as any);
|
|
vi.mocked(Keyv).mockClear();
|
|
vi.mocked(KeyvRedis).mockClear();
|
|
vi.mocked(logger.warn).mockClear();
|
|
vi.mocked(logger.error).mockClear();
|
|
vi.mocked(logger.info).mockClear();
|
|
|
|
// Mock successful cache operations for Redis connection test
|
|
mockCacheInstance.set.mockResolvedValue(undefined);
|
|
mockCacheInstance.get.mockResolvedValue({ test: true });
|
|
mockCacheInstance.del.mockResolvedValue(undefined);
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env.REDIS_URL = originalRedisUrl;
|
|
process.env.NEXT_RUNTIME = originalNextRuntime;
|
|
});
|
|
|
|
describe("Initialization and getCache", () => {
|
|
test("should use Redis store and return it via getCache if REDIS_URL is set", async () => {
|
|
process.env.REDIS_URL = "redis://localhost:6379";
|
|
const { getCache } = await import("./service");
|
|
|
|
const cache = await getCache();
|
|
|
|
expect(KeyvRedis).toHaveBeenCalledWith("redis://localhost:6379");
|
|
expect(Keyv).toHaveBeenCalledWith({
|
|
store: expect.any(KeyvRedis),
|
|
});
|
|
expect(createCache).toHaveBeenCalledWith({
|
|
stores: [expect.any(Keyv)],
|
|
});
|
|
expect(logger.info).toHaveBeenCalledWith("Cache initialized with Redis");
|
|
expect(cache).toBe(mockCacheInstance);
|
|
});
|
|
|
|
test("should fall back to memory store if Redis connection fails", async () => {
|
|
process.env.REDIS_URL = "redis://localhost:6379";
|
|
const mockError = new Error("Connection refused");
|
|
|
|
// Mock cache operations to fail for Redis connection test
|
|
mockCacheInstance.get.mockRejectedValueOnce(mockError);
|
|
|
|
const { getCache } = await import("./service");
|
|
|
|
const cache = await getCache();
|
|
|
|
expect(KeyvRedis).toHaveBeenCalledWith("redis://localhost:6379");
|
|
expect(logger.warn).toHaveBeenCalledWith("Redis connection failed, using memory cache", {
|
|
error: mockError,
|
|
});
|
|
expect(cache).toBe(mockCacheInstance);
|
|
});
|
|
|
|
test("should use memory store and return it via getCache if REDIS_URL is not set", async () => {
|
|
delete process.env.REDIS_URL;
|
|
const { getCache } = await import("./service");
|
|
|
|
const cache = await getCache();
|
|
|
|
expect(KeyvRedis).not.toHaveBeenCalled();
|
|
expect(Keyv).toHaveBeenCalledWith();
|
|
expect(createCache).toHaveBeenCalledWith({
|
|
stores: [expect.any(Keyv)],
|
|
});
|
|
expect(cache).toBe(mockCacheInstance);
|
|
});
|
|
|
|
test("should use memory store and return it via getCache if REDIS_URL is an empty string", async () => {
|
|
process.env.REDIS_URL = "";
|
|
const { getCache } = await import("./service");
|
|
|
|
const cache = await getCache();
|
|
|
|
expect(KeyvRedis).not.toHaveBeenCalled();
|
|
expect(Keyv).toHaveBeenCalledWith();
|
|
expect(createCache).toHaveBeenCalledWith({
|
|
stores: [expect.any(Keyv)],
|
|
});
|
|
expect(cache).toBe(mockCacheInstance);
|
|
});
|
|
|
|
test("should return same instance on multiple calls to getCache", async () => {
|
|
process.env.REDIS_URL = "redis://localhost:6379";
|
|
const { getCache } = await import("./service");
|
|
|
|
const cache1 = await getCache();
|
|
const cache2 = await getCache();
|
|
|
|
expect(cache1).toBe(cache2);
|
|
expect(cache1).toBe(mockCacheInstance);
|
|
// Should only initialize once
|
|
expect(createCache).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
test("should use memory cache during build time", async () => {
|
|
process.env.REDIS_URL = "redis://localhost:6379";
|
|
delete process.env.NEXT_RUNTIME; // Simulate build time
|
|
|
|
const { getCache } = await import("./service");
|
|
|
|
const cache = await getCache();
|
|
|
|
expect(KeyvRedis).not.toHaveBeenCalled();
|
|
expect(Keyv).toHaveBeenCalledWith();
|
|
expect(cache).toBe(mockCacheInstance);
|
|
});
|
|
|
|
test("should provide cache health information", async () => {
|
|
process.env.REDIS_URL = "redis://localhost:6379";
|
|
const { getCache, getCacheHealth } = await import("./service");
|
|
|
|
// Before initialization
|
|
let health = getCacheHealth();
|
|
expect(health.isInitialized).toBe(false);
|
|
expect(health.hasInstance).toBe(false);
|
|
|
|
// After initialization
|
|
await getCache();
|
|
health = getCacheHealth();
|
|
expect(health.isInitialized).toBe(true);
|
|
expect(health.hasInstance).toBe(true);
|
|
expect(health.isRedisConnected).toBe(true);
|
|
});
|
|
});
|
|
});
|