diff --git a/src/everything/__tests__/registrations.test.ts b/src/everything/__tests__/registrations.test.ts new file mode 100644 index 00000000..072bc5c6 --- /dev/null +++ b/src/everything/__tests__/registrations.test.ts @@ -0,0 +1,134 @@ +import { describe, it, expect, vi } from 'vitest'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; + +// Create mock server +function createMockServer() { + return { + registerTool: vi.fn(), + registerPrompt: vi.fn(), + registerResource: vi.fn(), + server: { + getClientCapabilities: vi.fn(() => ({})), + setRequestHandler: vi.fn(), + }, + sendLoggingMessage: vi.fn(), + sendResourceUpdated: vi.fn(), + } as unknown as McpServer; +} + +describe('Registration Index Files', () => { + describe('tools/index.ts', () => { + it('should register all standard tools', async () => { + const { registerTools } = await import('../tools/index.js'); + const mockServer = createMockServer(); + + registerTools(mockServer); + + // Should register 12 standard tools (non-conditional) + expect(mockServer.registerTool).toHaveBeenCalledTimes(12); + + // Verify specific tools are registered + const registeredTools = (mockServer.registerTool as any).mock.calls.map( + (call: any[]) => call[0] + ); + expect(registeredTools).toContain('echo'); + expect(registeredTools).toContain('get-sum'); + expect(registeredTools).toContain('get-env'); + expect(registeredTools).toContain('get-tiny-image'); + expect(registeredTools).toContain('get-structured-content'); + expect(registeredTools).toContain('get-annotated-message'); + expect(registeredTools).toContain('trigger-long-running-operation'); + expect(registeredTools).toContain('get-resource-links'); + expect(registeredTools).toContain('get-resource-reference'); + expect(registeredTools).toContain('gzip-file-as-resource'); + expect(registeredTools).toContain('toggle-simulated-logging'); + expect(registeredTools).toContain('toggle-subscriber-updates'); + }); + + it('should register conditional tools based on capabilities', async () => { + const { registerConditionalTools } = await import('../tools/index.js'); + + // Server with all capabilities + const mockServerWithCapabilities = { + registerTool: vi.fn(), + server: { + getClientCapabilities: vi.fn(() => ({ + roots: {}, + elicitation: {}, + sampling: {}, + })), + }, + } as unknown as McpServer; + + registerConditionalTools(mockServerWithCapabilities); + + // Should register 3 conditional tools when all capabilities present + expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(3); + + const registeredTools = ( + mockServerWithCapabilities.registerTool as any + ).mock.calls.map((call: any[]) => call[0]); + expect(registeredTools).toContain('get-roots-list'); + expect(registeredTools).toContain('trigger-elicitation-request'); + expect(registeredTools).toContain('trigger-sampling-request'); + }); + + it('should not register conditional tools when capabilities missing', async () => { + const { registerConditionalTools } = await import('../tools/index.js'); + + const mockServerNoCapabilities = { + registerTool: vi.fn(), + server: { + getClientCapabilities: vi.fn(() => ({})), + }, + } as unknown as McpServer; + + registerConditionalTools(mockServerNoCapabilities); + + // Should not register any tools when capabilities are missing + expect(mockServerNoCapabilities.registerTool).not.toHaveBeenCalled(); + }); + }); + + describe('prompts/index.ts', () => { + it('should register all prompts', async () => { + const { registerPrompts } = await import('../prompts/index.js'); + const mockServer = createMockServer(); + + registerPrompts(mockServer); + + // Should register 4 prompts + expect(mockServer.registerPrompt).toHaveBeenCalledTimes(4); + + const registeredPrompts = (mockServer.registerPrompt as any).mock.calls.map( + (call: any[]) => call[0] + ); + expect(registeredPrompts).toContain('simple-prompt'); + expect(registeredPrompts).toContain('args-prompt'); + expect(registeredPrompts).toContain('completable-prompt'); + expect(registeredPrompts).toContain('resource-prompt'); + }); + }); + + describe('resources/index.ts', () => { + it('should register resource templates', async () => { + const { registerResources } = await import('../resources/index.js'); + const mockServer = createMockServer(); + + registerResources(mockServer); + + // Should register at least the 2 resource templates (text and blob) + expect(mockServer.registerResource).toHaveBeenCalled(); + }); + + it('should read instructions from file', async () => { + const { readInstructions } = await import('../resources/index.js'); + + const instructions = readInstructions(); + + // Should return a string (either content or error message) + expect(typeof instructions).toBe('string'); + expect(instructions.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/src/everything/__tests__/resources.test.ts b/src/everything/__tests__/resources.test.ts index c664059b..d766d38b 100644 --- a/src/everything/__tests__/resources.test.ts +++ b/src/everything/__tests__/resources.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; import { textResource, @@ -17,6 +17,12 @@ import { getSessionResourceURI, registerSessionResource, } from '../resources/session.js'; +import { registerFileResources } from '../resources/files.js'; +import { + setSubscriptionHandlers, + beginSimulatedResourceUpdates, + stopSimulatedResourceUpdates, +} from '../resources/subscriptions.js'; describe('Resource Templates', () => { describe('Constants', () => { @@ -266,3 +272,100 @@ describe('Session Resources', () => { }); }); }); + +describe('File Resources', () => { + describe('registerFileResources', () => { + it('should register file resources from docs directory', () => { + const mockServer = { + registerResource: vi.fn(), + } as unknown as McpServer; + + // This may or may not register resources depending on if docs/ exists + registerFileResources(mockServer); + + // If docs folder exists and has files, resources should be registered + // If not, the function should not throw + expect(true).toBe(true); + }); + + it('should not throw when docs directory is missing', () => { + const mockServer = { + registerResource: vi.fn(), + } as unknown as McpServer; + + // Should gracefully handle missing docs directory + expect(() => registerFileResources(mockServer)).not.toThrow(); + }); + }); +}); + +describe('Subscriptions', () => { + describe('setSubscriptionHandlers', () => { + it('should set request handlers on server', () => { + const mockServer = { + server: { + setRequestHandler: vi.fn(), + }, + sendLoggingMessage: vi.fn(), + } as unknown as McpServer; + + setSubscriptionHandlers(mockServer); + + // Should set both subscribe and unsubscribe handlers + expect(mockServer.server.setRequestHandler).toHaveBeenCalledTimes(2); + }); + }); + + describe('beginSimulatedResourceUpdates', () => { + afterEach(() => { + // Clean up any intervals + stopSimulatedResourceUpdates('test-session-updates'); + stopSimulatedResourceUpdates(undefined); + }); + + it('should start update interval for session', () => { + const mockServer = { + server: { + notification: vi.fn(), + }, + } as unknown as McpServer; + + // Should not throw + expect(() => + beginSimulatedResourceUpdates(mockServer, 'test-session-updates') + ).not.toThrow(); + }); + + it('should handle undefined sessionId', () => { + const mockServer = { + server: { + notification: vi.fn(), + }, + } as unknown as McpServer; + + expect(() => beginSimulatedResourceUpdates(mockServer, undefined)).not.toThrow(); + }); + }); + + describe('stopSimulatedResourceUpdates', () => { + it('should stop updates for session', () => { + const mockServer = { + server: { + notification: vi.fn(), + }, + } as unknown as McpServer; + + // Start then stop + beginSimulatedResourceUpdates(mockServer, 'stop-test-session'); + expect(() => stopSimulatedResourceUpdates('stop-test-session')).not.toThrow(); + }); + + it('should handle stopping non-existent session', () => { + expect(() => stopSimulatedResourceUpdates('non-existent-session')).not.toThrow(); + }); + + it('should handle undefined sessionId', () => { + expect(() => stopSimulatedResourceUpdates(undefined)).not.toThrow(); + }); + }); +}); diff --git a/src/everything/__tests__/server.test.ts b/src/everything/__tests__/server.test.ts new file mode 100644 index 00000000..7ee0590c --- /dev/null +++ b/src/everything/__tests__/server.test.ts @@ -0,0 +1,62 @@ +import { describe, it, expect, vi } from 'vitest'; +import { createServer } from '../server/index.js'; + +describe('Server Factory', () => { + describe('createServer', () => { + it('should return a ServerFactoryResponse object', () => { + const result = createServer(); + + expect(result).toHaveProperty('server'); + expect(result).toHaveProperty('cleanup'); + }); + + it('should return a cleanup function', () => { + const { cleanup } = createServer(); + + expect(typeof cleanup).toBe('function'); + }); + + it('should create an McpServer instance', () => { + const { server } = createServer(); + + expect(server).toBeDefined(); + expect(server.server).toBeDefined(); + }); + + it('should have tools capability enabled', () => { + const { server } = createServer(); + + // Server should be properly configured + expect(server).toBeDefined(); + }); + + it('should cleanup without throwing errors', () => { + const { cleanup } = createServer(); + + // Cleanup should not throw when called with a session ID + expect(() => cleanup('test-session')).not.toThrow(); + }); + + it('should cleanup without throwing errors when sessionId is undefined', () => { + const { cleanup } = createServer(); + + // Cleanup should not throw when called without a session ID + expect(() => cleanup()).not.toThrow(); + }); + + it('should allow multiple servers to be created', () => { + const result1 = createServer(); + const result2 = createServer(); + + expect(result1.server).toBeDefined(); + expect(result2.server).toBeDefined(); + expect(result1.server).not.toBe(result2.server); + }); + + it('should have an oninitialized handler set', () => { + const { server } = createServer(); + + expect(server.server.oninitialized).toBeDefined(); + }); + }); +});