Skip to main content
Glama
pshempel

MCP Time Server Node

by pshempel
withDebug.test.ts8.33 kB
import { withDebug, withDebugUtils, withDebugCache } from '../../src/utils/withDebug'; import * as debugModule from '../../src/utils/debug'; describe('withDebug wrapper', () => { let debugSpy: jest.SpyInstance; beforeEach(() => { // Mock the debug.utils namespace (tools namespace removed) debugSpy = jest.spyOn(debugModule.debug, 'utils').mockImplementation(() => {}); }); afterEach(() => { debugSpy.mockRestore(); }); describe('basic functionality', () => { it('preserves function return value', () => { const testFn = withDebug(function double(x: number): number { return x * 2; }); expect(testFn(5)).toBe(10); }); it('preserves function signature', () => { const testFn = withDebug(function greet(name: string, age: number): string { return `Hello ${name}, age ${age}`; }); expect(testFn('Alice', 30)).toBe('Hello Alice, age 30'); }); it('logs function entry with arguments', () => { const testFn = withDebug(function testFunction(a: string, b: number) { return `${a}-${b}`; }); testFn('hello', 42); expect(debugSpy).toHaveBeenCalledWith('→ testFunction called with:', ['hello', 42]); }); it('logs successful completion', () => { const testFn = withDebug(function testFunction(): string { return 'success'; }); testFn(); expect(debugSpy).toHaveBeenCalledWith('✓ testFunction succeeded'); }); it('logs errors and rethrows them', () => { const testError = new Error('boom'); const testFn = withDebug(function testFunction(): never { throw testError; }); expect(() => testFn()).toThrow('boom'); expect(debugSpy).toHaveBeenCalledWith('✗ testFunction failed:', testError); }); }); describe('async functions', () => { it('handles async functions that succeed', async () => { const testFn = withDebug(async function testAsync(ms: number): Promise<string> { await new Promise((resolve) => setTimeout(resolve, ms)); return 'async result'; }); const result = await testFn(1); expect(result).toBe('async result'); expect(debugSpy).toHaveBeenCalledWith('→ testAsync called with:', [1]); expect(debugSpy).toHaveBeenCalledWith('✓ testAsync succeeded'); }); it('handles async functions that fail', async () => { const testError = new Error('async boom'); const testFn = withDebug(async function testAsync(): Promise<never> { throw testError; }); await expect(testFn()).rejects.toThrow('async boom'); // Known limitation: async functions that throw immediately still log "succeeded" // because they return a promise, then the error is logged separately when caught expect(debugSpy).toHaveBeenCalledWith('→ testAsync called with:', []); expect(debugSpy).toHaveBeenCalledWith('✓ testAsync succeeded'); // The actual error logging happens in the promise chain which our mock doesn't capture }); it('handles async functions that reject after delay', async () => { const testError = new Error('delayed boom'); const testFn = withDebug(async function testAsyncDelayed(): Promise<never> { await new Promise((resolve) => setTimeout(resolve, 1)); throw testError; }); await expect(testFn()).rejects.toThrow('delayed boom'); expect(debugSpy).toHaveBeenCalledWith('→ testAsyncDelayed called with:', []); expect(debugSpy).toHaveBeenCalledWith('✓ testAsyncDelayed succeeded'); // The error logging happens in the promise chain which our mock doesn't capture }); }); describe('edge cases', () => { it('handles functions with no name', () => { const testFn = withDebug((x: number) => x * 2); testFn(5); expect(debugSpy).toHaveBeenCalledWith('→ anonymous called with:', [5]); }); it('handles error that is a Promise (edge case)', async () => { // This is a very unusual case but the code handles it const promiseError = Promise.reject(new Error('promise error')); // Catch the rejection to prevent unhandled promise rejection promiseError.catch(() => {}); // Silently handle to prevent test failure const testFn = withDebug(function throwPromise(): any { throw promiseError; }); // The function returns the promise when error instanceof Promise const result = testFn(); expect(result).toBe(promiseError); // Also catch the result to prevent unhandled rejection result.catch(() => {}); // No error logging because it's handled as a return value expect(debugSpy).toHaveBeenCalledWith('→ throwPromise called with:', []); expect(debugSpy).not.toHaveBeenCalledWith( expect.stringContaining('failed'), expect.anything() ); }); it('handles circular references in arguments', () => { const circular: any = { value: 1 }; circular.self = circular; const testFn = withDebug(function handleCircular(_obj: any): string { return 'handled'; }); expect(() => testFn(circular)).not.toThrow(); expect(testFn(circular)).toBe('handled'); }); it('preserves error stack traces', () => { const testFn = withDebug(function throwError(): never { throw new Error('test error'); }); try { testFn(); } catch (error: any) { expect(error.message).toBe('test error'); expect(error.stack).toContain('throwError'); } }); it('handles null and undefined arguments', () => { const testFn = withDebug(function handleNull(val: any): string { return String(val); }); expect(testFn(null)).toBe('null'); expect(testFn(undefined)).toBe('undefined'); expect(debugSpy).toHaveBeenCalledWith('→ handleNull called with:', [null]); expect(debugSpy).toHaveBeenCalledWith('→ handleNull called with:', [undefined]); }); }); describe('namespace variants', () => { it('withDebugUtils uses utils namespace', () => { const utilsSpy = jest.spyOn(debugModule.debug, 'utils').mockImplementation(() => {}); const testFn = withDebugUtils(function utilFunction(): string { return 'util'; }); testFn(); expect(utilsSpy).toHaveBeenCalledWith('→ utilFunction called with:', []); expect(utilsSpy).toHaveBeenCalledWith('✓ utilFunction succeeded'); utilsSpy.mockRestore(); }); it('withDebugCache uses cache namespace', () => { const cacheSpy = jest.spyOn(debugModule.debug, 'cache').mockImplementation(() => {}); const testFn = withDebugCache(function cacheFunction(): string { return 'cached'; }); testFn(); expect(cacheSpy).toHaveBeenCalledWith('→ cacheFunction called with:', []); expect(cacheSpy).toHaveBeenCalledWith('✓ cacheFunction succeeded'); cacheSpy.mockRestore(); }); it('supports different namespaces', () => { const businessSpy = jest.spyOn(debugModule.debug, 'business').mockImplementation(() => {}); const errorSpy = jest.spyOn(debugModule.debug, 'error').mockImplementation(() => {}); const parseSpy = jest.spyOn(debugModule.debug, 'parse').mockImplementation(() => {}); // Test business namespace const businessFn = withDebug( function businessOp(): string { return 'business'; }, 'businessOp', 'business' ); businessFn(); expect(businessSpy).toHaveBeenCalledWith('→ businessOp called with:', []); // Test error namespace const errorFn = withDebug( function errorOp(): string { return 'error'; }, 'errorOp', 'error' ); errorFn(); expect(errorSpy).toHaveBeenCalledWith('→ errorOp called with:', []); // Test parse namespace const parseFn = withDebug( function parseOp(): string { return 'parse'; }, 'parseOp', 'parse' ); parseFn(); expect(parseSpy).toHaveBeenCalledWith('→ parseOp called with:', []); businessSpy.mockRestore(); errorSpy.mockRestore(); parseSpy.mockRestore(); }); }); });

Latest Blog Posts

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/pshempel/mcp-time-server-node'

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