UNPKG

@razorpay/blade-mcp

Version:

Model Context Protocol server for Blade

183 lines 9.32 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { getBladePatternDocsHttpCallback } from '../getBladePatternDocs.js'; import * as analyticsUtils from '../../utils/analyticsUtils.js'; import * as cursorRulesUtils from '../../utils/cursorRulesUtils.js'; import * as getBladeDocsResponseText from '../../utils/getBladeDocsResponseText.js'; import * as generalUtils from '../../utils/generalUtils.js'; import { CURSOR_RULES_VERSION } from '../../utils/tokens.js'; // Mock the analytics and utility functions vi.mock('../../utils/analyticsUtils.js', async () => { const actual = await vi.importActual('../../utils/analyticsUtils.js'); return { ...actual, sendAnalytics: vi.fn(), }; }); vi.mock('../../utils/cursorRulesUtils.js'); vi.mock('../../utils/getBladeDocsResponseText.js'); vi.mock('../../utils/generalUtils.js', () => ({ getBladeDocsList: vi.fn(() => ['ListView', 'DetailedView', 'FormGroup']), })); vi.mock('fs', () => ({ readFileSync: vi.fn(() => 'Mock pattern guide content'), existsSync: vi.fn(() => false), })); // Create a mock context object for tool callbacks // eslint-disable-next-line @typescript-eslint/no-explicit-any const createMockContext = () => ({ signal: new AbortController().signal, requestId: 'test-request-id', sendNotification: vi.fn().mockResolvedValue(undefined), sendRequest: vi.fn().mockResolvedValue({}), }); describe('getBladePatternDocs Tool', () => { beforeEach(() => { vi.clearAllMocks(); // Setup default mocks vi.spyOn(generalUtils, 'getBladeDocsList').mockReturnValue([ 'ListView', 'DetailedView', 'FormGroup', ]); vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); }); it('should return pattern docs for valid patterns', () => { const mockCurrentProjectRootDirectory = '/Users/test/project'; const mockPatternsList = 'ListView, DetailedView'; const mockResponseText = 'Mock pattern documentation'; // Mock the getBladeDocsResponseText function vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockReturnValue(mockResponseText); // Get the HTTP callback const httpCallback = getBladePatternDocsHttpCallback; // Call the tool callback const result = httpCallback({ patternsList: mockPatternsList, currentProjectRootDirectory: mockCurrentProjectRootDirectory, clientName: 'cursor', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Verify analytics was called expect(analyticsUtils.sendAnalytics).toHaveBeenCalledWith({ eventName: expect.any(String), properties: { toolName: 'get_blade_pattern_docs', patternsList: mockPatternsList, rootDirectoryName: 'project', cursorRuleVersion: CURSOR_RULES_VERSION, clientName: 'cursor', }, }); // Verify the result structure expect(result).toHaveProperty('content'); if ('content' in result && !('isError' in result)) { expect(result.content).toHaveLength(1); expect(result.content[0]).toHaveProperty('type', 'text'); if ('text' in result.content[0]) { expect(result.content[0].text).toContain(mockResponseText); expect(result.content[0].text).toContain('get_blade_component_docs'); } } // Verify getBladeDocsResponseText was called with correct parameters expect(getBladeDocsResponseText.getBladeDocsResponseText).toHaveBeenCalledWith({ docsList: mockPatternsList, documentationType: 'patterns', }); }); it('should return error for invalid patterns', () => { const mockCurrentProjectRootDirectory = '/Users/test/project'; const mockPatternsList = 'InvalidPattern, AnotherInvalid'; // Get the HTTP callback const httpCallback = getBladePatternDocsHttpCallback; // Call the tool callback const result = httpCallback({ patternsList: mockPatternsList, currentProjectRootDirectory: mockCurrentProjectRootDirectory, clientName: 'cursor', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Verify the result is an error expect(result).toBeDefined(); expect(result).toHaveProperty('isError', true); if ('isError' in result && result.isError) { expect(result).toMatchObject({ content: [ { type: 'text', text: expect.any(String), }, ], }); } // Verify analytics was not called for invalid patterns expect(analyticsUtils.sendAnalytics).not.toHaveBeenCalled(); }); it('should return consistent pattern docs response (snapshot)', async () => { const testProjectRootDirectory = '/Users/test/project'; const testPatternsList = 'FormGroup'; // Unmock fs first so that getBladeDocsResponseText can read real files vi.doUnmock('fs'); // Get the actual implementations (not mocked) to test real output // Re-import getBladeDocsResponseText after unmocking fs so it uses actual readFileSync vi.doUnmock('../../utils/getBladeDocsResponseText.js'); const actualGetBladeDocsResponseText = await vi.importActual('../../utils/getBladeDocsResponseText.js'); const actualGeneralUtils = await vi.importActual('../../utils/generalUtils.js'); // Unmock analytics to allow the actual function to run, but we'll still spy on it vi.restoreAllMocks(); vi.spyOn(analyticsUtils, 'sendAnalytics').mockImplementation(() => { // Mock implementation that doesn't throw }); if (actualGetBladeDocsResponseText && actualGeneralUtils) { // Temporarily replace the mocked functions with the actual ones vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockImplementation(actualGetBladeDocsResponseText.getBladeDocsResponseText); vi.spyOn(generalUtils, 'getBladeDocsList').mockImplementation(actualGeneralUtils.getBladeDocsList); } // Mock cursor rules as not needing update vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); // Get the HTTP callback const httpCallback = getBladePatternDocsHttpCallback; // Call the tool callback with actual implementation const result = httpCallback({ patternsList: testPatternsList, currentProjectRootDirectory: testProjectRootDirectory, clientName: 'cursor', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Snapshot test to ensure the output format remains consistent expect(result).toMatchSnapshot(); }); it('should return consistent pattern docs response for claude agent', async () => { const testProjectRootDirectory = '/Users/test/project'; const testPatternsList = 'FormGroup'; // Unmock fs first so that getBladeDocsResponseText can read real files vi.doUnmock('fs'); // Get the actual implementations (not mocked) to test real output // Re-import getBladeDocsResponseText after unmocking fs so it uses actual readFileSync vi.doUnmock('../../utils/getBladeDocsResponseText.js'); const actualGetBladeDocsResponseText = await vi.importActual('../../utils/getBladeDocsResponseText.js'); const actualGeneralUtils = await vi.importActual('../../utils/generalUtils.js'); // Unmock analytics to allow the actual function to run, but we'll still spy on it vi.restoreAllMocks(); vi.spyOn(analyticsUtils, 'sendAnalytics').mockImplementation(() => { // Mock implementation that doesn't throw }); if (actualGetBladeDocsResponseText && actualGeneralUtils) { // Temporarily replace the mocked functions with the actual ones vi.spyOn(getBladeDocsResponseText, 'getBladeDocsResponseText').mockImplementation(actualGetBladeDocsResponseText.getBladeDocsResponseText); vi.spyOn(generalUtils, 'getBladeDocsList').mockImplementation(actualGeneralUtils.getBladeDocsList); } // Mock cursor rules as not needing update vi.spyOn(cursorRulesUtils, 'shouldCreateOrUpdateCursorRule').mockReturnValue(undefined); // Get the HTTP callback const httpCallback = getBladePatternDocsHttpCallback; // Call the tool callback with actual implementation const result = httpCallback({ patternsList: testPatternsList, currentProjectRootDirectory: testProjectRootDirectory, clientName: 'claude', cursorRuleVersion: CURSOR_RULES_VERSION, }, createMockContext()); // Snapshot test to ensure the output format remains consistent expect(result).toMatchSnapshot(); }); }); //# sourceMappingURL=getBladePatternDocs.test.js.map