Skip to main content
Glama
loadLamlHandler.test.ts9.7 kB
import {CallToolRequest} from '@modelcontextprotocol/sdk/types.js'; import {McpSession} from 'flowmcp'; import {jest, describe, it, expect, beforeEach, beforeAll, afterAll} from '@jest/globals'; import {handleLoadLaml, LoadLamlResult} from '../loadLamlHandler.js'; import {writeFile, mkdir, chmod, rmdir, unlink, symlink} from 'fs/promises'; import {tmpdir} from 'os'; import {join} from 'path'; // Mock session for testing (following pattern from validateLaml.test.ts) function createMockSession() { const errors: unknown[] = []; const warnings: unknown[] = []; return { logger: { addError: (error: any) => errors.push(error), addWarning: (warning: any) => warnings.push(warning) }, throwError: (error: any) => { throw new Error(error.message); }, getResult: (result: any) => ({ content: result }), _errors: errors, _warnings: warnings } as any; } describe('handleLoadLaml', () => { let testDir: string; let testFile: string; let restrictedFile: string; // Valid LAML content for testing const validLamlContent = `\`\`\`yaml $meta: name: 'testDocument' purpose: 'Test LAML document' version: 1.0 domains: ['test.domain'] testSection: purpose: "Test section for validation" testProperty: "test value" \`\`\``; beforeAll(async () => { // Create temp directory for tests testDir = join(tmpdir(), 'laml-handler-test-' + Date.now()); await mkdir(testDir, { recursive: true }); // Create test file with valid LAML content testFile = join(testDir, 'test.laml'); await writeFile(testFile, validLamlContent, 'utf-8'); // Create restricted file (if possible on this system) restrictedFile = join(testDir, 'restricted.laml'); await writeFile(restrictedFile, 'restricted content', 'utf-8'); try { await chmod(restrictedFile, 0o000); } catch { // Skip if chmod is not supported } }); afterAll(async () => { // Cleanup try { await chmod(restrictedFile, 0o644); await unlink(testFile); await unlink(restrictedFile); await rmdir(testDir); } catch { // Ignore cleanup errors } }); it('should successfully load LAML file with absolute path', async () => { const mockSession = createMockSession(); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: testFile } } }; const result = await handleLoadLaml(mockSession, request); // Verify the result structure and types expect(result.content).toHaveProperty('content'); expect(result.content).toHaveProperty('autoFixedIssues'); expect(result.content).toHaveProperty('isValid'); const actualResult = result.content as LoadLamlResult; // Verify content types expect(typeof actualResult.content).toBe('string'); expect(Array.isArray(actualResult.autoFixedIssues)).toBe(true); expect(typeof actualResult.isValid).toBe('boolean'); // Verify the result contains the expected YAML content expect(actualResult.content).toContain('$meta:'); expect(actualResult.content).toContain('testSection:'); }); it('should handle relative path by adding error', async () => { const mockSession = createMockSession(); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: 'relative/path/to/file.laml' } } }; const result = await handleLoadLaml(mockSession, request); expect((mockSession as any)._errors).toHaveLength(1); expect((mockSession as any)._errors[0]).toEqual({ code: 'LAML_INVALID_PATH_FORMAT', message: 'LAML file path must be absolute', context: {path: 'relative/path/to/file.laml'} }); expect(result).toEqual({content: {}}); }); it('should handle Windows-style relative path', async () => { const mockSession = createMockSession(); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: '.\\relative\\path\\to\\file.laml' } } }; const result = await handleLoadLaml(mockSession, request); expect((mockSession as any)._errors).toHaveLength(1); expect((mockSession as any)._errors[0]).toEqual({ code: 'LAML_INVALID_PATH_FORMAT', message: 'LAML file path must be absolute', context: {path: '.\\relative\\path\\to\\file.laml'} }); expect(result).toEqual({content: {}}); }); it('should handle empty path', async () => { const mockSession = createMockSession(); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: '' } } }; const result = await handleLoadLaml(mockSession, request); expect((mockSession as any)._errors).toHaveLength(1); expect((mockSession as any)._errors[0]).toEqual({ code: 'LAML_INVALID_PATH_FORMAT', message: 'LAML file path must be absolute', context: {path: ''} }); expect(result).toEqual({content: {}}); }); it('should handle file not found error', async () => { const mockSession = createMockSession(); const nonExistentFile = join(testDir, 'nonexistent.laml'); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: nonExistentFile } } }; const result = await handleLoadLaml(mockSession, request); expect((mockSession as any)._errors).toHaveLength(1); expect((mockSession as any)._errors[0]).toEqual({ code: 'LAML_FILE_NOT_FOUND', context: {path: nonExistentFile} }); expect(result).toEqual({content: {}}); }); it('should handle directory instead of file error', async () => { const mockSession = createMockSession(); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: testDir // directory instead of file } } }; const result = await handleLoadLaml(mockSession, request); expect((mockSession as any)._errors).toHaveLength(1); expect((mockSession as any)._errors[0]).toEqual({ code: 'LAML_PATH_IS_DIRECTORY', context: {path: testDir} }); expect(result).toEqual({content: {}}); }); it('should handle permission denied error if supported by system', async () => { // Skip this test on systems that don't support file permissions try { await chmod(restrictedFile, 0o000); } catch { return; // Skip test if chmod is not supported } const mockSession = createMockSession(); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: restrictedFile } } }; const result = await handleLoadLaml(mockSession, request); // Check if we got either permission denied or file not found // (behavior can vary by system) const errors = (mockSession as any)._errors; expect(errors.length).toBe(1); const errorCall = errors[0] as { code: string }; expect(['LAML_PERMISSION_DENIED', 'LAML_FILE_NOT_FOUND']).toContain(errorCall.code); expect(result).toEqual({content: {}}); }); it('should handle critical errors by throwing', async () => { const mockSession = createMockSession(); // Create a circular symbolic link to trigger ELOOP or similar critical error const circularLink = join(testDir, 'circular.laml'); try { await symlink(circularLink, circularLink); } catch { // If symlink fails, create a very long path to trigger different error const longPath = join(testDir, 'a'.repeat(300) + '.laml'); const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: longPath } } }; const result = await handleLoadLaml(mockSession, request); const errors = (mockSession as any)._errors; expect(errors.length).toBe(1); expect(errors[0]).toEqual( expect.objectContaining({ code: 'LAML_READ_CRITICAL_ERROR', context: expect.objectContaining({ path: longPath, originalError: expect.any(String) }) }) ); expect(result).toEqual({content: {}}); return; } const request: CallToolRequest = { method: 'tools/call', params: { name: 'loadLaml', arguments: { project: '/project/root', path: circularLink } } }; const result = await handleLoadLaml(mockSession, request); const errors = (mockSession as any)._errors; expect(errors.length).toBe(1); expect(errors[0]).toEqual( expect.objectContaining({ code: 'LAML_READ_CRITICAL_ERROR', context: expect.objectContaining({ path: circularLink, originalError: expect.any(String) }) }) ); expect(result).toEqual({content: {}}); // Cleanup circular link try { await unlink(circularLink); } catch { // Ignore cleanup errors } }); });

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/EgorKluch/mcp-laml'

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