@yeepay/awesome-components-mcp
Version:
MCP server providing access to awesome-components documentation and integration guides with dual-mode operation: direct fetch and GitLab MCP instruction generation
258 lines (243 loc) • 12.7 kB
JavaScript
;
/**
* Integration tests for Get Components Guide MCP Tool
* These tests verify the tool's behavior with mocked GitLab responses
*/
Object.defineProperty(exports, "__esModule", { value: true });
// Mock the GitLab client module
const mockFetchGitLabFileContentSafe = jest.fn();
jest.mock('../../services/gitlabClient', () => ({
fetchGitLabFileContentSafe: mockFetchGitLabFileContentSafe,
GitLabError: class GitLabError extends Error {
statusCode;
url;
constructor(message, statusCode, url) {
super(message);
this.statusCode = statusCode;
this.url = url;
this.name = 'GitLabError';
}
}
}));
const getComponentsGuide_1 = require("../../tools/getComponentsGuide");
// Mock the config
jest.mock('../../config', () => ({
config: {
urls: {
gitlabBase: 'https://gitlab.example.com/repo/-/raw/main/'
},
auth: {
gitlabToken: 'test-token'
},
internalGitlabHost: 'gitlab.example.com',
// GitLab MCP settings for relative paths
internalGuidesProjectId: 'test-project'
}
}));
describe('Get Components Guide Tool Integration Tests', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(console, 'log').mockImplementation(() => { });
jest.spyOn(console, 'error').mockImplementation(() => { });
// Reset global fetch mock
if (global.fetch && jest.isMockFunction(global.fetch)) {
global.fetch.mockReset();
}
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('Successful execution scenarios', () => {
const mockGuideContent = `# Dynamic Password Component
This component provides secure dynamic password generation functionality for payment systems.
## Installation
\`\`\`bash
npm install @yeepay/dynamic-password
\`\`\`
## Basic Usage
\`\`\`javascript
import { generatePassword } from '@yeepay/dynamic-password';
const password = generatePassword({
length: 12,
includeSpecialChars: true
});
\`\`\`
## Configuration
The component supports various configuration options:
- \`length\`: Password length (default: 8)
- \`includeSpecialChars\`: Include special characters (default: false)
- \`excludeSimilar\`: Exclude similar characters (default: true)
## API Reference
### generatePassword(options)
Generates a secure dynamic password.
**Parameters:**
- \`options\` (Object): Configuration options
**Returns:**
- \`string\`: Generated password`;
it('should generate GitLab MCP instruction for relative path', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'yeepay/dynamicpassword/llms.txt'
});
expect(result.content).toHaveLength(1);
expect(result.content[0].type).toBe('text');
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('get_file_contents');
expect(result.content[0].text).toContain('test-project');
expect(result.content[0].text).toContain('yeepay/dynamicpassword/llms.txt');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should execute successfully with absolute URL', async () => {
const absoluteUrl = 'https://external.gitlab.com/project/-/raw/main/component/guide.txt';
// Mock fetch for external URL (since it's external, it should use fetch, not GitLab client)
global.fetch = jest.fn().mockResolvedValueOnce({
ok: true,
status: 200,
text: jest.fn().mockResolvedValueOnce(mockGuideContent)
});
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: absoluteUrl });
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('# Component Guide: Dynamic Password Component');
expect(result.content[0].text).toContain(`**Source Path:** \`${absoluteUrl}\``);
expect(result.isError).toBeUndefined();
expect(global.fetch).toHaveBeenCalledWith(absoluteUrl, {
headers: {
'User-Agent': 'awesome-components-mcp/1.0.0'
}
});
});
it('should generate GitLab MCP instruction for path with leading slash', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: '/yeepay/dynamicpassword/llms.txt'
});
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('yeepay/dynamicpassword/llms.txt');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should generate GitLab MCP instruction for markdown file', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'component/guide.md'
});
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('component/guide.md');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should generate GitLab MCP instruction for empty path', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'empty/component.txt'
});
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('empty/component.txt');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
});
describe('Path validation and error scenarios', () => {
it('should reject invalid paths with dangerous characters', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: '../../../etc/passwd'
});
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('Error: Invalid Component Guide Path');
expect(result.content[0].text).toContain('invalid characters or format');
expect(result.isError).toBe(true);
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should reject paths with script injection attempts', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'path<script>alert(1)</script>'
});
expect(result.content[0].text).toContain('Error: Invalid Component Guide Path');
expect(result.isError).toBe(true);
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should reject empty paths', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: '' });
expect(result.content[0].text).toContain('Error: Invalid Component Guide Path');
expect(result.isError).toBe(true);
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should generate GitLab MCP instruction for nonexistent path', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'nonexistent/component.txt'
});
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('nonexistent/component.txt');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should generate GitLab MCP instruction for private path', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'private/component.txt'
});
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('private/component.txt');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
it('should generate GitLab MCP instruction for any relative path', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({
path: 'component/guide.txt'
});
expect(result.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(result.content[0].text).toContain('component/guide.txt');
expect(result.isError).toBeUndefined();
// Should not call GitLab client directly
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
});
});
describe('Tool behavior verification', () => {
it('should log the correct path and GitLab MCP instruction generation', async () => {
const consoleSpy = jest.spyOn(console, 'log');
await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: 'yeepay/component/llms.txt' });
expect(consoleSpy).toHaveBeenCalledWith('Getting component guide for path: yeepay/component/llms.txt');
expect(consoleSpy).toHaveBeenCalledWith('Relative/internal path detected, generating gitlab-mcp instruction: yeepay/component/llms.txt');
});
it('should return MCP-compliant response structure', async () => {
const result = await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: 'test/component.txt' });
// Verify MCP response structure
expect(result).toHaveProperty('content');
expect(Array.isArray(result.content)).toBe(true);
expect(result.content).toHaveLength(1);
expect(result.content[0]).toHaveProperty('type', 'text');
expect(result.content[0]).toHaveProperty('text');
expect(typeof result.content[0].text).toBe('string');
});
it('should handle different path types correctly', async () => {
// Test relative path - should generate GitLab MCP instruction
const result1 = await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: 'component/guide.txt' });
expect(result1.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalled();
// Test path with leading slash - should generate GitLab MCP instruction
const result2 = await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: '/component/guide.txt' });
expect(result2.content[0].text).toContain('# Component Guide - GitLab MCP Instruction');
// Test external URL (should use fetch, not GitLab client)
const externalUrl = 'https://external.com/file.txt';
// Mock fetch for external URL
global.fetch = jest.fn().mockResolvedValueOnce({
ok: true,
status: 200,
text: jest.fn().mockResolvedValueOnce('external content')
});
const result3 = await (0, getComponentsGuide_1.getComponentsGuideTool)({ path: externalUrl });
// Should use fetch, not GitLab client
expect(global.fetch).toHaveBeenCalledWith(externalUrl, {
headers: {
'User-Agent': 'awesome-components-mcp/1.0.0'
}
});
expect(result3.content[0].text).toContain('# Component Guide: file');
expect(mockFetchGitLabFileContentSafe).not.toHaveBeenCalledWith(externalUrl);
});
});
});