Skip to main content
Glama

mcp-youtube

by kirbah
cache.service.test.ts7.87 kB
import { CacheService } from "../cache.service"; // Import the class import { getDb } from "../database.service"; // This is already mocked // These will hold the mock functions retrieved from the mocked getDb instance let actualMockUpdateOne: jest.Mock; let actualMockFindOne: jest.Mock; let actualMockDeleteOne: jest.Mock; jest.mock("../database.service", () => { // Create the mocks within the factory scope const factoryMockUpdateOne = jest.fn(); const factoryMockFindOne = jest.fn(); const factoryMockDeleteOne = jest.fn(); return { getDb: jest.fn().mockReturnValue({ collection: jest.fn().mockReturnValue({ updateOne: factoryMockUpdateOne, findOne: factoryMockFindOne, deleteOne: factoryMockDeleteOne, }), }), }; }); let cacheServiceInstance: CacheService; describe("CacheService", () => { beforeEach(() => { // We need to get the actual mock functions used by the mocked getDb // This ensures our tests assert against the correct mock instances. const mockDb = getDb(); // This is the mocked getDb const mockCollection = mockDb.collection("test_collection"); // Provide a collection name actualMockUpdateOne = mockCollection.updateOne as jest.Mock; actualMockFindOne = mockCollection.findOne as jest.Mock; actualMockDeleteOne = mockCollection.deleteOne as jest.Mock; // Clear all mock states before each test. // This is important because the mocks (factoryMockUpdateOne etc.) are created once // when the factory runs. Their state (calls, instances) persists across tests // unless cleared. actualMockUpdateOne.mockClear(); actualMockFindOne.mockClear(); actualMockDeleteOne.mockClear(); // Clear the getDb mock itself and the collection mock function // getDb (the imported function) is mocked by jest.mock (getDb as jest.Mock).mockClear(); // getDb().collection is also a mock function from our factory (mockDb.collection as jest.Mock).mockClear(); cacheServiceInstance = new CacheService(); }); describe("createOperationKey", () => { it("should create a consistent hash for the same operation and arguments", () => { const key1 = cacheServiceInstance.createOperationKey("testOp", { a: 1, b: "hello", }); const key2 = cacheServiceInstance.createOperationKey("testOp", { b: "hello", a: 1, }); const key3 = cacheServiceInstance.createOperationKey("anotherOp", { a: 1, b: "hello", }); expect(key1).toBe(key2); // Order of arguments should not matter expect(key1).not.toBe(key3); // Different operation name should result in different hash }); it("should exclude undefined values from the key", () => { const key1 = cacheServiceInstance.createOperationKey("testOp", { a: 1, b: undefined, }); const key2 = cacheServiceInstance.createOperationKey("testOp", { a: 1 }); expect(key1).toBe(key2); }); }); describe("getOrSet", () => { const key = "testKey"; const collectionName = "testCollection"; const ttlSeconds = 60; const freshData = { value: "fresh" }; const cachedData = { value: "cached" }; describe("when MDB_MCP_CONNECTION_STRING is set", () => { const originalEnv = process.env; beforeEach(() => { process.env = { ...originalEnv, MDB_MCP_CONNECTION_STRING: "mongodb://test", }; }); afterEach(() => { process.env = originalEnv; }); it("should return cached data if available and not expired", async () => { actualMockFindOne.mockResolvedValue({ _id: key, data: cachedData, expiresAt: new Date(Date.now() + 10000), // 10 seconds in the future }); const operation = jest.fn().mockResolvedValue(freshData); const result = await cacheServiceInstance.getOrSet( key, operation, ttlSeconds, collectionName ); expect(result).toEqual(cachedData); expect(operation).not.toHaveBeenCalled(); expect(actualMockFindOne).toHaveBeenCalledWith({ _id: key, expiresAt: { $gt: expect.any(Date) }, }); expect(actualMockUpdateOne).not.toHaveBeenCalled(); }); it("should execute operation, store, and return fresh data if cache is expired or not found", async () => { // Simulate no cache or expired cache actualMockFindOne.mockResolvedValue(null); const operation = jest.fn().mockResolvedValue(freshData); const result = await cacheServiceInstance.getOrSet( key, operation, ttlSeconds, collectionName ); expect(result).toEqual(freshData); expect(operation).toHaveBeenCalledTimes(1); expect(actualMockFindOne).toHaveBeenCalledWith({ _id: key, expiresAt: { $gt: expect.any(Date) }, }); expect(actualMockUpdateOne).toHaveBeenCalledTimes(1); const updateCall = actualMockUpdateOne.mock.calls[0]; expect(updateCall[0]).toEqual({ _id: key }); expect(updateCall[1].$set.data).toEqual(freshData); expect(updateCall[1].$set.expiresAt.getTime()).toBeGreaterThan( Date.now() ); expect(updateCall[2]).toEqual({ upsert: true }); }); it("should not cache if fresh data is null or undefined", async () => { actualMockFindOne.mockResolvedValue(null); const operationNull = jest.fn().mockResolvedValue(null); const resultNull = await cacheServiceInstance.getOrSet( key, operationNull, ttlSeconds, collectionName ); expect(resultNull).toBeNull(); expect(actualMockUpdateOne).not.toHaveBeenCalled(); // Should not cache null actualMockUpdateOne.mockClear(); // Clear for next assertion const operationUndefined = jest.fn().mockResolvedValue(undefined); const resultUndefined = await cacheServiceInstance.getOrSet( key, operationUndefined, ttlSeconds, collectionName ); expect(resultUndefined).toBeUndefined(); expect(actualMockUpdateOne).not.toHaveBeenCalled(); // Should not cache undefined }); it("should store params if provided", async () => { actualMockFindOne.mockResolvedValue(null); const params = { query: "test" }; const operation = jest.fn().mockResolvedValue(freshData); await cacheServiceInstance.getOrSet( key, operation, ttlSeconds, collectionName, params ); expect(actualMockUpdateOne).toHaveBeenCalledTimes(1); const updateCall = actualMockUpdateOne.mock.calls[0]; expect(updateCall[1].$set.params).toEqual(params); }); }); describe("when MDB_MCP_CONNECTION_STRING is not set", () => { const originalEnv = process.env; beforeEach(() => { jest.resetModules(); process.env = { ...originalEnv }; delete process.env.MDB_MCP_CONNECTION_STRING; }); afterEach(() => { process.env = originalEnv; }); it("should bypass caching and execute the operation directly", async () => { const operation = jest.fn().mockResolvedValue(freshData); const result = await cacheServiceInstance.getOrSet( key, operation, ttlSeconds, collectionName ); expect(result).toEqual(freshData); expect(operation).toHaveBeenCalledTimes(1); expect(getDb).not.toHaveBeenCalled(); expect(actualMockFindOne).not.toHaveBeenCalled(); expect(actualMockUpdateOne).not.toHaveBeenCalled(); }); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kirbah/mcp-youtube'

If you have feedback or need assistance with the MCP directory API, please join our Discord server