@razorpay/blade-mcp
Version:
Model Context Protocol server for Blade
183 lines • 9.32 kB
JavaScript
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