Skip to main content
Glama
template-integration.test.js13.2 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import http from 'http'; import path from 'path'; // Mock os module first vi.mock('os', () => ({ default: { homedir: () => '/mock/home', tmpdir: () => '/mock/tmp' } })); // Mock fs module const mockFs = { readFile: vi.fn(), writeFile: vi.fn(), mkdir: vi.fn(), readdir: vi.fn(), stat: vi.fn(), unlink: vi.fn() }; vi.mock('fs', async () => { const actual = await vi.importActual('fs'); return { ...actual, promises: mockFs, default: actual }; }); // Mock glob module vi.mock('glob', () => ({ glob: vi.fn() })); // Import server after mocks are set up const { startServer } = await import('../server.js'); describe('Template Integration - Custom Templates Actually Used', () => { let server; const mockSettingsFile = path.join('/mock/home', '.shrimp-task-viewer-settings.json'); const mockTemplatesDir = path.join('/mock/home', '.shrimp-task-viewer-templates'); const mockDefaultTemplatesDir = '/mock/project/src/prompts/templates_en'; const defaultPlanTaskTemplate = `## Default Plan Task Template This is the default template from the system. Task: {description}`; const customPlanTaskTemplate = `## CUSTOM Plan Task Template This is a CUSTOM template that should override the default. Enhanced Task: {description} Requirements: {requirements} Custom Field: {customField}`; const customExecuteTaskTemplate = `## CUSTOM Execute Task Template This custom template is for executing tasks. Execute: {description} Context: {context}`; beforeEach(async () => { vi.clearAllMocks(); // Setup mock file system mockFs.readFile.mockImplementation((filePath) => { // Settings file if (filePath === mockSettingsFile) { return Promise.resolve(JSON.stringify({ agents: [] })); } // Custom templates (these should take precedence) if (filePath === path.join(mockTemplatesDir, 'planTask.txt')) { return Promise.resolve(customPlanTaskTemplate); } if (filePath === path.join(mockTemplatesDir, 'executeTask.txt')) { return Promise.resolve(customExecuteTaskTemplate); } // Default templates (should be overridden by custom) if (filePath.includes('templates_en') && filePath.includes('planTask')) { return Promise.resolve(defaultPlanTaskTemplate); } const error = new Error('ENOENT: no such file or directory'); error.code = 'ENOENT'; return Promise.reject(error); }); mockFs.writeFile.mockResolvedValue(); mockFs.mkdir.mockResolvedValue(); mockFs.stat.mockImplementation((filePath) => { if (filePath.includes('templates_en') || filePath.includes('.shrimp-task-viewer-templates')) { return Promise.resolve({ isDirectory: () => true }); } const error = new Error('ENOENT'); error.code = 'ENOENT'; return Promise.reject(error); }); mockFs.readdir.mockImplementation((dirPath) => { if (dirPath.includes('templates_en')) { return Promise.resolve(['planTask.txt', 'executeTask.txt', 'analyzeTask.txt']); } if (dirPath.includes('.shrimp-task-viewer-templates')) { return Promise.resolve(['planTask.txt', 'executeTask.txt']); } return Promise.resolve([]); }); server = await startServer(); }); afterEach(async () => { if (server) { await new Promise((resolve) => { server.close(resolve); }); server = null; } }); describe('Custom Template Priority', () => { it('should use custom template when it exists instead of default', async () => { const response = await makeRequest(server, '/api/templates/planTask'); expect(response.statusCode).toBe(200); const data = JSON.parse(response.body); // Should return the CUSTOM template, not the default expect(data.content).toContain('CUSTOM Plan Task Template'); expect(data.content).toContain('Enhanced Task:'); expect(data.content).toContain('Custom Field:'); // Should NOT contain default template content expect(data.content).not.toContain('Default Plan Task Template'); expect(data.content).not.toContain('This is the default template from the system'); // Status should indicate it's custom expect(data.status).toBe('custom'); expect(data.source).toBe('user-custom'); }); it('should use custom template for executeTask when available', async () => { const response = await makeRequest(server, '/api/templates/executeTask'); expect(response.statusCode).toBe(200); const data = JSON.parse(response.body); // Should return the CUSTOM execute template expect(data.content).toContain('CUSTOM Execute Task Template'); expect(data.content).toContain('Execute:'); expect(data.content).toContain('Context:'); expect(data.status).toBe('custom'); expect(data.source).toBe('user-custom'); }); it('should list both custom and default templates with correct status', async () => { const response = await makeRequest(server, '/api/templates'); expect(response.statusCode).toBe(200); const templates = JSON.parse(response.body); // Find the planTask template const planTask = templates.find(t => t.functionName === 'planTask'); expect(planTask).toBeDefined(); expect(planTask.status).toBe('custom'); expect(planTask.source).toBe('user-custom'); // Find the executeTask template const executeTask = templates.find(t => t.functionName === 'executeTask'); expect(executeTask).toBeDefined(); expect(executeTask.status).toBe('custom'); expect(executeTask.source).toBe('user-custom'); // analyzeTask should be default (no custom version) const analyzeTask = templates.find(t => t.functionName === 'analyzeTask'); expect(analyzeTask).toBeDefined(); expect(analyzeTask.status).toBe('default'); expect(analyzeTask.source).toBe('built-in'); }); it('should use environment variable override even when custom template exists', async () => { // Set environment variable override process.env.MCP_PROMPT_PLAN_TASK = 'Environment Override Content for planTask'; const response = await makeRequest(server, '/api/templates/planTask'); expect(response.statusCode).toBe(200); const data = JSON.parse(response.body); // Should use environment variable, not custom template expect(data.content).toBe('Environment Override Content for planTask'); expect(data.status).toBe('env-override'); expect(data.source).toBe('environment'); // Clean up delete process.env.MCP_PROMPT_PLAN_TASK; }); it('should append to custom template when using append mode', async () => { // Setup append template const appendContent = '\n\n## Additional Instructions\nAppended content here'; mockFs.readFile.mockImplementation((filePath) => { if (filePath === mockSettingsFile) { return Promise.resolve(JSON.stringify({ agents: [] })); } if (filePath === path.join(mockTemplatesDir, 'planTask.txt')) { return Promise.resolve(customPlanTaskTemplate); } if (filePath === path.join(mockTemplatesDir, 'planTask_append.txt')) { return Promise.resolve(appendContent); } if (filePath.includes('templates_en') && filePath.includes('planTask')) { return Promise.resolve(defaultPlanTaskTemplate); } const error = new Error('ENOENT'); error.code = 'ENOENT'; return Promise.reject(error); }); // Also update readdir to include append file mockFs.readdir.mockImplementation((dirPath) => { if (dirPath.includes('.shrimp-task-viewer-templates')) { return Promise.resolve(['planTask.txt', 'planTask_append.txt', 'executeTask.txt']); } if (dirPath.includes('templates_en')) { return Promise.resolve(['planTask.txt', 'executeTask.txt', 'analyzeTask.txt']); } return Promise.resolve([]); }); const response = await makeRequest(server, '/api/templates/planTask'); expect(response.statusCode).toBe(200); const data = JSON.parse(response.body); // Should contain both custom template and appended content expect(data.content).toContain('CUSTOM Plan Task Template'); expect(data.content).toContain('Additional Instructions'); expect(data.content).toContain('Appended content here'); expect(data.status).toBe('custom-append'); }); }); describe('Template Application in Practice', () => { it('should correctly apply custom template when saving updates', async () => { const updatedTemplate = `## UPDATED Custom Template New content for planTask Task: {description} New Field: {newField}`; const response = await makeRequest(server, '/api/templates/planTask', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: updatedTemplate, mode: 'override' }) }); expect(response.statusCode).toBe(200); // Verify the template was saved with custom content expect(mockFs.writeFile).toHaveBeenCalledWith( path.join(mockTemplatesDir, 'planTask.txt'), updatedTemplate ); }); it('should verify custom template is actually loaded after save', async () => { // First, save a new custom template const newCustomTemplate = `## Brand New Custom Template Completely new template content Task: {description}`; await makeRequest(server, '/api/templates/newTemplate', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: newCustomTemplate, mode: 'override' }) }); // Update mock to return the new template mockFs.readFile.mockImplementation((filePath) => { if (filePath === path.join(mockTemplatesDir, 'newTemplate.txt')) { return Promise.resolve(newCustomTemplate); } // ... other mock implementations return Promise.reject(new Error('ENOENT')); }); // Now fetch the template to verify it returns custom content const response = await makeRequest(server, '/api/templates/newTemplate'); if (response.statusCode === 200) { const data = JSON.parse(response.body); expect(data.content).toBe(newCustomTemplate); expect(data.status).toBe('custom'); } }); it('should handle deletion of custom template and fallback to default', async () => { // First, delete the custom template mockFs.unlink.mockResolvedValue(); const deleteResponse = await makeRequest(server, '/api/templates/planTask', { method: 'DELETE' }); expect(deleteResponse.statusCode).toBe(200); expect(mockFs.unlink).toHaveBeenCalledWith( path.join(mockTemplatesDir, 'planTask.txt') ); // Update mock to simulate deleted custom template mockFs.readFile.mockImplementation((filePath) => { if (filePath === mockSettingsFile) { return Promise.resolve(JSON.stringify({ agents: [] })); } // Custom template no longer exists if (filePath === path.join(mockTemplatesDir, 'planTask.txt')) { const error = new Error('ENOENT'); error.code = 'ENOENT'; return Promise.reject(error); } // Should fall back to default if (filePath.includes('templates_en') && filePath.includes('planTask')) { return Promise.resolve(defaultPlanTaskTemplate); } const error = new Error('ENOENT'); error.code = 'ENOENT'; return Promise.reject(error); }); // Now fetch the template - should get default const getResponse = await makeRequest(server, '/api/templates/planTask'); if (getResponse.statusCode === 200) { const data = JSON.parse(getResponse.body); expect(data.content).toContain('Default Plan Task Template'); expect(data.status).toBe('default'); expect(data.source).toBe('built-in'); } }); }); }); // Helper function to make HTTP requests function makeRequest(server, path, options = {}) { return new Promise((resolve) => { const port = server.address().port; const reqOptions = { hostname: '127.0.0.1', port, path, method: options.method || 'GET', headers: options.headers || {} }; const req = http.request(reqOptions, (res) => { let body = ''; res.on('data', (chunk) => body += chunk.toString()); res.on('end', () => { resolve({ statusCode: res.statusCode, headers: res.headers, body }); }); }); if (options.body) { req.write(options.body); } req.end(); }); }

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/cjo4m06/mcp-shrimp-task-manager'

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