Skip to main content
Glama

SFCC Development MCP Server

by taurgis
job-log-handler.test.ts9.56 kB
import { JobLogToolHandler } from '../src/core/handlers/job-log-handler.js'; import { HandlerContext } from '../src/core/handlers/base-handler.js'; import { SFCCLogClient } from '../src/clients/log-client.js'; import { Logger } from '../src/utils/logger.js'; // Mock dependencies jest.mock('../src/clients/log-client.js'); jest.mock('../src/utils/logger.js'); describe('JobLogToolHandler', () => { let mockContext: HandlerContext; let handler: JobLogToolHandler; let mockLogger: jest.Mocked<Logger>; let mockLogClient: jest.Mocked<SFCCLogClient>; beforeEach(() => { jest.clearAllMocks(); mockLogger = { debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn(), methodEntry: jest.fn(), methodExit: jest.fn(), timing: jest.fn(), log: jest.fn(), } as any; mockLogClient = { getLatestJobLogFiles: jest.fn(), searchJobLogsByName: jest.fn(), getJobLogEntries: jest.fn(), searchJobLogs: jest.fn(), getJobExecutionSummary: jest.fn(), } as any; // Mock the SFCCLogClient constructor (SFCCLogClient as jest.MockedClass<typeof SFCCLogClient>).mockImplementation(() => mockLogClient); // Mock Logger.getChildLogger jest.spyOn(Logger, 'getChildLogger').mockReturnValue(mockLogger); mockContext = { logger: mockLogger, config: { hostname: 'test.demandware.net', username: 'test', password: 'test', clientId: 'test', clientSecret: 'test', }, capabilities: { canAccessLogs: true, canAccessOCAPI: true, }, }; handler = new JobLogToolHandler(mockContext, 'job-log-handler'); }); afterEach(() => { jest.restoreAllMocks(); }); // Helper function to initialize handler const initializeHandler = async () => { await (handler as any).initialize(); }; describe('canHandle', () => { it('should handle job log tool names', () => { const jobLogTools = [ 'get_latest_job_log_files', 'search_job_logs_by_name', 'get_job_log_entries', 'search_job_logs', 'get_job_execution_summary', ]; jobLogTools.forEach(toolName => { expect(handler.canHandle(toolName)).toBe(true); }); }); it('should not handle non-job-log tool names', () => { const nonJobLogTools = [ 'get_latest_error', 'search_logs', 'get_log_file_contents', 'summarize_logs', 'list_log_files', 'unknown_tool', '', ]; nonJobLogTools.forEach(toolName => { expect(handler.canHandle(toolName)).toBe(false); }); }); it('should handle case-sensitive tool names only', () => { expect(handler.canHandle('GET_LATEST_JOB_LOG_FILES')).toBe(false); expect(handler.canHandle('get_Latest_Job_Log_Files')).toBe(false); expect(handler.canHandle('get_latest_job_log_files')).toBe(true); }); }); describe('handle method - get_latest_job_log_files', () => { beforeEach(async () => { await initializeHandler(); }); it('should handle get_latest_job_log_files with default limit', async () => { const mockResult = 'Latest job log files result'; mockLogClient.getLatestJobLogFiles.mockResolvedValue(mockResult); const result = await handler.handle('get_latest_job_log_files', {}, Date.now()); expect(mockLogClient.getLatestJobLogFiles).toHaveBeenCalledWith(10); // default limit expect(result.content[0].text).toContain(mockResult); }); it('should handle get_latest_job_log_files with custom limit', async () => { const mockResult = 'Latest job log files result'; mockLogClient.getLatestJobLogFiles.mockResolvedValue(mockResult); const result = await handler.handle('get_latest_job_log_files', { limit: 25 }, Date.now()); expect(mockLogClient.getLatestJobLogFiles).toHaveBeenCalledWith(25); expect(result.content[0].text).toContain(mockResult); }); it('should validate limit parameter', async () => { const result = await handler.handle('get_latest_job_log_files', { limit: -1 }, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Invalid limit'); }); }); describe('handle method - search_job_logs_by_name', () => { beforeEach(async () => { await initializeHandler(); }); it('should handle search_job_logs_by_name with required jobName', async () => { const mockResult = 'Search job logs by name result'; mockLogClient.searchJobLogsByName.mockResolvedValue(mockResult); const result = await handler.handle('search_job_logs_by_name', { jobName: 'TestJob', limit: 15, }, Date.now()); expect(mockLogClient.searchJobLogsByName).toHaveBeenCalledWith('TestJob', 15); expect(result.content[0].text).toContain(mockResult); }); it('should validate required jobName parameter', async () => { const result = await handler.handle('search_job_logs_by_name', {}, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('jobName must be a non-empty string'); }); }); describe('handle method - get_job_log_entries', () => { beforeEach(async () => { await initializeHandler(); }); it('should handle get_job_log_entries with default parameters', async () => { const mockResult = 'Job log entries result'; mockLogClient.getJobLogEntries.mockResolvedValue(mockResult); const result = await handler.handle('get_job_log_entries', {}, Date.now()); expect(mockLogClient.getJobLogEntries).toHaveBeenCalledWith('all', 50, undefined); // default limit is 50 for job entries expect(result.content[0].text).toContain(mockResult); }); it('should validate log level parameter', async () => { const result = await handler.handle('get_job_log_entries', { level: 'invalid' }, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Invalid log level: invalid'); }); }); describe('handle method - search_job_logs', () => { beforeEach(async () => { await initializeHandler(); }); it('should handle search_job_logs with required pattern', async () => { const mockResult = 'Search job logs result'; mockLogClient.searchJobLogs.mockResolvedValue(mockResult); const result = await handler.handle('search_job_logs', { pattern: 'error-pattern', }, Date.now()); expect(mockLogClient.searchJobLogs).toHaveBeenCalledWith('error-pattern', 'all', 20, undefined); expect(result.content[0].text).toContain(mockResult); }); it('should validate required pattern parameter', async () => { const result = await handler.handle('search_job_logs', {}, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('pattern must be a non-empty string'); }); }); describe('handle method - get_job_execution_summary', () => { beforeEach(async () => { await initializeHandler(); }); it('should handle get_job_execution_summary with required jobName', async () => { const mockResult = 'Job execution summary result'; mockLogClient.getJobExecutionSummary.mockResolvedValue(mockResult); const result = await handler.handle('get_job_execution_summary', { jobName: 'SummaryJob', }, Date.now()); expect(mockLogClient.getJobExecutionSummary).toHaveBeenCalledWith('SummaryJob'); expect(result.content[0].text).toContain(mockResult); }); it('should validate required jobName parameter', async () => { const result = await handler.handle('get_job_execution_summary', {}, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('jobName must be a non-empty string'); }); }); describe('error handling', () => { beforeEach(async () => { await initializeHandler(); }); it('should handle client errors gracefully', async () => { const clientError = new Error('Client connection failed'); mockLogClient.getLatestJobLogFiles.mockRejectedValue(clientError); const result = await handler.handle('get_latest_job_log_files', {}, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Client connection failed'); }); it('should handle unsupported tool names', async () => { await expect(handler.handle('unsupported_tool', {}, Date.now())) .rejects.toThrow('Unsupported tool: unsupported_tool'); }); }); describe('initialization', () => { it('should initialize log client when capabilities allow', async () => { await initializeHandler(); expect(SFCCLogClient).toHaveBeenCalledWith(mockContext.config); expect(mockLogger.debug).toHaveBeenCalledWith('Log client initialized'); }); it('should handle missing capabilities gracefully', async () => { const contextWithoutLogs = { ...mockContext, capabilities: { canAccessLogs: false, canAccessOCAPI: false }, }; const handlerWithoutLogs = new JobLogToolHandler(contextWithoutLogs, 'job-log'); const result = await handlerWithoutLogs.handle('get_latest_job_log_files', {}, Date.now()); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Log client not configured'); }); }); });

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/taurgis/sfcc-dev-mcp'

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