Skip to main content
Glama
server.test.ts11.6 kB
/** * Server Integration Tests */ import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; import { createServer } from '../../index.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; describe('PinePaper MCP Server', () => { let server: Promise<any>; beforeEach(() => { server = createServer(); }); afterEach(async () => { // Clean up if needed }); describe('Server Creation', () => { it('should create a server instance', async () => { const resolvedServer = await server; expect(resolvedServer).toBeDefined(); expect(typeof resolvedServer.connect).toBe('function'); }); }); describe('Tool Listing', () => { it('should list all available tools', async () => { // Test that tools are properly registered // The actual list tools call would require a connected transport expect(server).toBeDefined(); }); }); describe('Resource Listing', () => { it('should have documentation resources', async () => { // Test that resources are properly registered expect(server).toBeDefined(); }); }); }); describe('Tool Handler Integration', () => { describe('Item Creation Flow', () => { it('should handle create -> modify -> delete flow', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Create const createResult = await handleToolCall('pinepaper_create_item', { itemType: 'circle', position: { x: 100, y: 100 }, properties: { radius: 50, color: '#ff0000' }, }); expect(createResult.isError).toBeFalsy(); // The generated code would create item_1 // Modify (simulated as item_1) const modifyResult = await handleToolCall('pinepaper_modify_item', { itemId: 'item_1', properties: { color: '#00ff00', scale: 1.5 }, }); expect(modifyResult.isError).toBeFalsy(); // Delete const deleteResult = await handleToolCall('pinepaper_delete_item', { itemId: 'item_1', }); expect(deleteResult.isError).toBeFalsy(); }); }); describe('Relation Creation Flow', () => { it('should handle relation add -> query -> remove flow', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Add relation const addResult = await handleToolCall('pinepaper_add_relation', { sourceId: 'item_2', targetId: 'item_1', relationType: 'orbits', params: { radius: 150, speed: 0.5 }, }); expect(addResult.isError).toBeFalsy(); expect((addResult.content[0] as { text: string }).text).toContain('orbits'); // Query relations const queryResult = await handleToolCall('pinepaper_query_relations', { itemId: 'item_1', direction: 'incoming', }); expect(queryResult.isError).toBeFalsy(); // Remove relation const removeResult = await handleToolCall('pinepaper_remove_relation', { sourceId: 'item_2', targetId: 'item_1', relationType: 'orbits', }); expect(removeResult.isError).toBeFalsy(); }); }); describe('Animation Flow', () => { it('should handle animation add -> play -> stop flow', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Apply simple animation const animateResult = await handleToolCall('pinepaper_animate', { itemId: 'item_1', animationType: 'pulse', speed: 1.0, }); expect(animateResult.isError).toBeFalsy(); // Apply keyframe animation const keyframeResult = await handleToolCall('pinepaper_keyframe_animate', { itemId: 'item_1', keyframes: [ { time: 0, properties: { opacity: 0 } }, { time: 2, properties: { opacity: 1 } }, ], duration: 2, loop: true, }); expect(keyframeResult.isError).toBeFalsy(); // Play timeline const playResult = await handleToolCall('pinepaper_play_timeline', { action: 'play', duration: 5, loop: true, }); expect(playResult.isError).toBeFalsy(); // Stop timeline const stopResult = await handleToolCall('pinepaper_play_timeline', { action: 'stop', }); expect(stopResult.isError).toBeFalsy(); }); }); describe('Generator Flow', () => { it('should handle list generators -> execute generator flow', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // List generators const listResult = await handleToolCall('pinepaper_list_generators', {}); expect(listResult.isError).toBeFalsy(); expect((listResult.content[0] as { text: string }).text).toContain('getAvailableBackgroundGenerators'); // Execute generator const executeResult = await handleToolCall('pinepaper_execute_generator', { generatorName: 'drawSunburst', params: { rayCount: 12, animated: true }, }); expect(executeResult.isError).toBeFalsy(); expect((executeResult.content[0] as { text: string }).text).toContain('drawSunburst'); }); }); describe('Export Flow', () => { it('should handle SVG export', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); const result = await handleToolCall('pinepaper_export_svg', {}); expect(result.isError).toBeFalsy(); expect((result.content[0] as { text: string }).text).toContain('exportAnimatedSVG'); }); it('should handle training data export', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // JSON format const jsonResult = await handleToolCall('pinepaper_export_training_data', { format: 'json', includeMetadata: true, }); expect(jsonResult.isError).toBeFalsy(); // JSONL format const jsonlResult = await handleToolCall('pinepaper_export_training_data', { format: 'jsonl', includeMetadata: false, }); expect(jsonlResult.isError).toBeFalsy(); expect((jsonlResult.content[0] as { text: string }).text).toContain('jsonl'); }); }); describe('Canvas Control Flow', () => { it('should handle canvas setup flow', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Set background color const bgResult = await handleToolCall('pinepaper_set_background_color', { color: '#0f172a', }); expect(bgResult.isError).toBeFalsy(); // Set canvas size const sizeResult = await handleToolCall('pinepaper_set_canvas_size', { width: 1920, height: 1080, }); expect(sizeResult.isError).toBeFalsy(); }); }); describe('Query Flow', () => { it('should handle item and relation queries', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Get items const itemsResult = await handleToolCall('pinepaper_get_items', { filter: { type: 'circle' }, }); expect(itemsResult.isError).toBeFalsy(); // Get relation stats const statsResult = await handleToolCall('pinepaper_get_relation_stats', {}); expect(statsResult.isError).toBeFalsy(); }); }); describe('Error Handling', () => { it('should handle unknown tool gracefully', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); const result = await handleToolCall('unknown_tool', {}); expect(result.isError).toBe(true); expect((result.content[0] as { text: string }).text).toContain('UNKNOWN_TOOL'); }); it('should handle invalid parameters gracefully', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); const result = await handleToolCall('pinepaper_create_item', { itemType: 'invalid_type', }); expect(result.isError).toBe(true); }); it('should handle missing required parameters', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); const result = await handleToolCall('pinepaper_modify_item', { // Missing itemId and properties }); expect(result.isError).toBe(true); }); }); }); describe('Complex Scenarios', () => { describe('Solar System Creation', () => { it('should generate code for a complete solar system', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Create sun const sunResult = await handleToolCall('pinepaper_create_item', { itemType: 'circle', position: { x: 400, y: 300 }, properties: { radius: 60, color: '#fbbf24' }, }); expect(sunResult.isError).toBeFalsy(); // Create earth const earthResult = await handleToolCall('pinepaper_create_item', { itemType: 'circle', position: { x: 550, y: 300 }, properties: { radius: 20, color: '#3b82f6' }, }); expect(earthResult.isError).toBeFalsy(); // Create moon const moonResult = await handleToolCall('pinepaper_create_item', { itemType: 'circle', position: { x: 590, y: 300 }, properties: { radius: 8, color: '#9ca3af' }, }); expect(moonResult.isError).toBeFalsy(); // Earth orbits sun const earthOrbitResult = await handleToolCall('pinepaper_add_relation', { sourceId: 'item_2', targetId: 'item_1', relationType: 'orbits', params: { radius: 150, speed: 0.3 }, }); expect(earthOrbitResult.isError).toBeFalsy(); // Moon orbits earth const moonOrbitResult = await handleToolCall('pinepaper_add_relation', { sourceId: 'item_3', targetId: 'item_2', relationType: 'orbits', params: { radius: 40, speed: 0.8 }, }); expect(moonOrbitResult.isError).toBeFalsy(); // Set dark background const bgResult = await handleToolCall('pinepaper_set_background_color', { color: '#0f172a', }); expect(bgResult.isError).toBeFalsy(); }); }); describe('Animated Logo Creation', () => { it('should generate code for an animated logo', async () => { const { handleToolCall } = await import('../../tools/handlers.js'); // Create text const textResult = await handleToolCall('pinepaper_create_item', { itemType: 'text', position: { x: 400, y: 300 }, properties: { content: 'BRAND', fontSize: 96, color: '#ffffff', fontWeight: 'bold', }, }); expect(textResult.isError).toBeFalsy(); // Apply pulse animation const pulseResult = await handleToolCall('pinepaper_animate', { itemId: 'item_1', animationType: 'pulse', speed: 0.5, }); expect(pulseResult.isError).toBeFalsy(); // Apply sparkle effect const sparkleResult = await handleToolCall('pinepaper_apply_effect', { itemId: 'item_1', effectType: 'sparkle', params: { color: '#fbbf24', speed: 1.5 }, }); expect(sparkleResult.isError).toBeFalsy(); // Add sunburst background const bgResult = await handleToolCall('pinepaper_execute_generator', { generatorName: 'drawSunburst', params: { colors: ['#3b82f6', '#8b5cf6'], animated: true, }, }); expect(bgResult.isError).toBeFalsy(); }); }); });

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/pinepaper/mcp-server'

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