Skip to main content
Glama
filePlaceholderIntegration.test.ts13.3 kB
import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { join } from 'path'; import { writeFileSync, mkdirSync, rmSync, existsSync } from 'fs'; describe('File Placeholder Integration Tests', () => { let client: Client; const testDir = join(process.cwd(), 'test-files'); const testScriptFile = join(testDir, 'test-script.js'); const testTemplateFile = join(testDir, 'test-template.html'); const testConfigFile = join(testDir, 'test-config.json'); const testScopeFile = join(testDir, 'scope.txt'); const testQueryFile = join(testDir, 'query.txt'); const testNameFile = join(testDir, 'name.txt'); const testDescriptionFile = join(testDir, 'description.txt'); const testLargeFile = join(testDir, 'large.txt'); beforeAll(async () => { // Set up environment variables for ServiceNow (using placeholder values for tests) process.env.SERVICENOW_ACE_INSTANCE = 'test-instance.service-now.com'; process.env.SERVICENOW_ACE_USERNAME = 'test_user'; process.env.SERVICENOW_ACE_PASSWORD = 'test_password'; // Create test files if (!existsSync(testDir)) { mkdirSync(testDir, { recursive: true }); } writeFileSync(testScriptFile, 'console.log("Hello from file!");\nconst x = 42;'); writeFileSync(testTemplateFile, '<div>Hello World</div>\n<p>This is a template</p>'); writeFileSync(testConfigFile, '{"name": "test", "value": 123}'); writeFileSync(testScopeFile, 'global'); writeFileSync(testQueryFile, 'name=TestScript'); writeFileSync(testNameFile, 'TestUpdateSet'); writeFileSync(testDescriptionFile, 'Test update set description'); writeFileSync(testLargeFile, 'x'.repeat(2 * 1024 * 1024)); // 2MB file // Create MCP client using the official SDK approach const serverPath = join(__dirname, '../../build/index.js'); const transport = new StdioClientTransport({ command: 'node', args: [serverPath], env: { NODE_ENV: 'test', SERVICENOW_ACE_INSTANCE: 'test-instance.service-now.com', SERVICENOW_ACE_USERNAME: 'test_user', SERVICENOW_ACE_PASSWORD: 'test_password', }, }); client = new Client({ name: 'test-client', version: '1.4.0', }); await client.connect(transport); }, 10000); // 10 second timeout for server startup afterAll(async () => { if (client) { await client.close(); } // Clean up test files if (existsSync(testDir)) { rmSync(testDir, { recursive: true, force: true }); } }); describe('Background Script Tool with File Placeholders', () => { it('should resolve file placeholder in script parameter', async () => { const result = await client.callTool({ name: 'execute_background_script', arguments: { script: '{{file:${testScriptFile}}}', scope: 'global', }, }); // The tool should attempt to execute (may fail due to no real ServiceNow connection) // but should not fail due to file placeholder resolution expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should resolve file placeholder in scope parameter', async () => { // Create a scope file const scopeFile = join(testDir, 'scope.txt'); writeFileSync(scopeFile, 'x_cls_clear_skye_i'); const result = await client.callTool({ name: 'execute_background_script', arguments: { script: 'gs.print("test");', scope: '{{file:${testScopeFile}}}', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle file not found error', async () => { const result = await client.callTool({ name: 'execute_background_script', arguments: { script: '{{file:/nonexistent/file.js}}', scope: 'global', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); // Should contain error information const content = (result.content as any[])[0]?.text; if (content) { const parsed = JSON.parse(content); expect(parsed.success).toBe(false); expect(parsed.error.code).toBe('FILE_PLACEHOLDER_ERROR'); } }); }); describe('Table Operation Tool with File Placeholders', () => { it('should resolve file placeholder in data parameter for insert', async () => { const result = await client.callTool({ name: 'execute_table_operation', arguments: { operation: 'POST', table: 'sys_script_include', data: { name: 'TestScriptInclude', script: '{{file:${testScriptFile}}}', active: true, }, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should resolve file placeholder in batch data', async () => { const result = await client.callTool({ name: 'execute_table_operation', arguments: { operation: 'POST', table: 'sys_script_include', data: [ { name: 'Script1', script: '{{file:${testScriptFile}}}', active: true, }, { name: 'Script2', script: 'console.log("inline script");', active: true, }, ], batch: true, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should resolve file placeholder in query parameter', async () => { // Create a query file const queryFile = join(testDir, 'query.txt'); writeFileSync(queryFile, 'active=true^nameLIKEtest'); const result = await client.callTool({ name: 'execute_table_operation', arguments: { operation: 'GET', table: 'sys_script_include', query: '{{file:${testQueryFile}}}', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle file not found error in table operation', async () => { const result = await client.callTool({ name: 'execute_table_operation', arguments: { operation: 'POST', table: 'sys_script_include', data: { name: 'TestScriptInclude', script: '{{file:/nonexistent/file.js}}', active: true, }, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); // Should contain error information const content = (result.content as any[])[0]?.text; if (content) { const parsed = JSON.parse(content); expect(parsed.success).toBe(false); expect(parsed.error.code).toBe('FILE_PLACEHOLDER_ERROR'); } }); }); describe('Update Set Operation Tool with File Placeholders', () => { it('should resolve file placeholder in data parameter for insert', async () => { const result = await client.callTool({ name: 'execute_updateset_operation', arguments: { operation: 'insert', table: 'sys_script_include', data: { name: 'TestScriptInclude', script: '{{file:${testScriptFile}}}', active: true, }, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should resolve file placeholder in name and description', async () => { // Create name and description files const nameFile = join(testDir, 'name.txt'); const descFile = join(testDir, 'description.txt'); writeFileSync(nameFile, 'Test Update Set'); writeFileSync(descFile, 'This is a test update set created from file content'); const result = await client.callTool({ name: 'execute_updateset_operation', arguments: { operation: 'create', name: '{{file:${testNameFile}}}', description: '{{file:${testDescriptionFile}}}', scope: 'x_cls_clear_skye_i', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should resolve file placeholder in batch data', async () => { const result = await client.callTool({ name: 'execute_updateset_operation', arguments: { operation: 'insert', table: 'sys_script_include', data: [ { name: 'Script1', script: '{{file:${testScriptFile}}}', active: true, }, { name: 'Script2', script: 'console.log("inline script");', active: true, }, ], batch: true, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should handle file not found error in update set operation', async () => { const result = await client.callTool({ name: 'execute_updateset_operation', arguments: { operation: 'insert', table: 'sys_script_include', data: { name: 'TestScriptInclude', script: '{{file:/nonexistent/file.js}}', active: true, }, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); // Should contain error information const content = (result.content as any[])[0]?.text; if (content) { const parsed = JSON.parse(content); expect(parsed.success).toBe(false); expect(parsed.error.code).toBe('FILE_PLACEHOLDER_ERROR'); } }); }); describe('Multiple Placeholders in Single String', () => { it('should resolve multiple placeholders in background script', async () => { const result = await client.callTool({ name: 'execute_background_script', arguments: { script: '// Script from file: {{file:${testScriptFile}}}\n// Template: {{file:${testTemplateFile}}}', scope: 'global', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); it('should resolve multiple placeholders in table operation data', async () => { const result = await client.callTool({ name: 'execute_table_operation', arguments: { operation: 'POST', table: 'sys_script_include', data: { name: 'MultiPlaceholderScript', script: '{{file:${testScriptFile}}}', description: 'Template: {{file:${testTemplateFile}}}', active: true, }, }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); }); }); describe('Error Handling', () => { it('should handle directory traversal attempts', async () => { const result = await client.callTool({ name: 'execute_background_script', arguments: { script: '{{file:../../../etc/passwd}}', scope: 'global', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); // Should contain error information const content = (result.content as any[])[0]?.text; if (content) { const parsed = JSON.parse(content); expect(parsed.success).toBe(false); expect(parsed.error.code).toBe('FILE_PLACEHOLDER_ERROR'); } }); it('should handle absolute paths outside workspace', async () => { const result = await client.callTool({ name: 'execute_background_script', arguments: { script: '{{file:/etc/passwd}}', scope: 'global', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); // Should contain error information const content = (result.content as any[])[0]?.text; if (content) { const parsed = JSON.parse(content); expect(parsed.success).toBe(false); expect(parsed.error.code).toBe('FILE_PLACEHOLDER_ERROR'); } }); }); describe('Configuration', () => { it('should respect file size limits', async () => { // Create a large file (2MB) const largeFile = join(testDir, 'large.txt'); const largeContent = 'x'.repeat(2 * 1024 * 1024); writeFileSync(largeFile, largeContent); const result = await client.callTool({ name: 'execute_background_script', arguments: { script: '{{file:${testLargeFile}}}', scope: 'global', }, }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); // Should contain error information about file size const content = (result.content as any[])[0]?.text; if (content) { const parsed = JSON.parse(content); expect(parsed.success).toBe(false); expect(parsed.error.code).toBe('FILE_PLACEHOLDER_ERROR'); } }); }); });

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/ClearSkye/SkyeNet-MCP-ACE'

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