@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
218 lines (174 loc) • 7.7 kB
text/typescript
/**
* Integration tests for Components Discovery MCP Tool
* These tests verify the tool's behavior with mocked GitLab responses
*/
// Mock the GitLab client module
const mockFetchGitLabFileContentSafe = jest.fn();
jest.mock('../../services/gitlabClient', () => ({
fetchGitLabFileContentSafe: mockFetchGitLabFileContentSafe,
GitLabError: class GitLabError extends Error {
constructor(message: string, public statusCode?: number, public url?: string) {
super(message);
this.name = 'GitLabError';
}
}
}));
import { componentsDiscoveryTool } from '../../tools/componentsDiscovery';
// Mock the config
jest.mock('../../config', () => ({
config: {
urls: {
gitlabBase: 'https://gitlab.example.com/repo/-/raw/main/',
mainLlms: 'https://gitlab.example.com/repo/-/raw/main/llms.txt'
},
auth: {
gitlabToken: 'test-token'
},
llmsTxtUrl: 'https://gitlab.example.com/repo/-/raw/main/llms.txt',
llmsTxtProjectId: null,
llmsTxtFile: null,
gitlabMcpServerAddress: null,
internalGitlabHost: null,
internalGuidesProjectId: null
}
}));
describe('Components Discovery Tool Integration Tests', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('Direct fetch mode (LLMS_TXT_URL configured)', () => {
const mockLlmsContent = `# Awesome Components
button-component
input-component
modal-component
guide/getting-started
docs/api-reference.md
tutorial/basic-usage
// This is a comment
another-component`;
it('should execute successfully and return JSON format', async () => {
mockFetchGitLabFileContentSafe.mockResolvedValueOnce(mockLlmsContent);
const result = await componentsDiscoveryTool();
expect(result.content).toHaveLength(1);
expect(result.content[0].type).toBe('text');
// Parse the JSON response
const jsonData = JSON.parse(result.content[0].text);
expect(jsonData).toHaveProperty('source');
expect(jsonData).toHaveProperty('totalComponents', 7);
expect(jsonData).toHaveProperty('componentsByType');
expect(jsonData).toHaveProperty('components');
expect(jsonData.components).toHaveLength(7);
expect(result.isError).toBeUndefined();
expect(mockFetchGitLabFileContentSafe).toHaveBeenCalledWith(
'https://gitlab.example.com/repo/-/raw/main/llms.txt'
);
});
it('should categorize components correctly', async () => {
mockFetchGitLabFileContentSafe.mockResolvedValueOnce(mockLlmsContent);
const result = await componentsDiscoveryTool();
const jsonData = JSON.parse(result.content[0].text);
expect(jsonData.componentsByType).toEqual({
component: 4,
guide: 1,
documentation: 1,
tutorial: 1
});
// Check specific components
const buttonComponent = jsonData.components.find((c: any) => c.name === 'button-component');
expect(buttonComponent).toEqual({
name: 'button-component',
type: 'component',
path: 'button-component'
});
const guideComponent = jsonData.components.find((c: any) => c.name === 'getting-started');
expect(guideComponent).toEqual({
name: 'getting-started',
type: 'guide',
path: 'guide/getting-started'
});
expect(result.isError).toBeUndefined();
});
it('should handle empty content gracefully', async () => {
mockFetchGitLabFileContentSafe.mockResolvedValueOnce('');
const result = await componentsDiscoveryTool();
expect(result.content).toHaveLength(1);
const jsonData = JSON.parse(result.content[0].text);
expect(jsonData.totalComponents).toBe(0);
expect(jsonData.components).toEqual([]);
expect(jsonData.componentsByType).toEqual({});
expect(result.isError).toBeUndefined();
});
});
// Note: GitLab MCP instruction mode tests would require more complex mocking
// For now, we'll test the direct fetch mode which is the current default
describe('Error handling scenarios', () => {
it('should handle GitLab errors gracefully', async () => {
const { GitLabError } = require('../../services/gitlabClient');
const gitlabError = new GitLabError('File not found', 404, 'https://example.com/file.txt');
mockFetchGitLabFileContentSafe.mockRejectedValueOnce(gitlabError);
const result = await componentsDiscoveryTool();
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('Error: Components Discovery Failed');
expect(result.content[0].text).toContain('GitLab Error: File not found');
expect(result.content[0].text).toContain('**Status Code:** 404');
expect(result.isError).toBe(true);
});
it('should handle network errors gracefully', async () => {
mockFetchGitLabFileContentSafe.mockRejectedValueOnce(new Error('Network timeout'));
const result = await componentsDiscoveryTool();
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('Error: Components Discovery Failed');
expect(result.content[0].text).toContain('Error: Network timeout');
expect(result.isError).toBe(true);
});
it('should handle unexpected errors gracefully', async () => {
mockFetchGitLabFileContentSafe.mockRejectedValueOnce('Unexpected error type');
const result = await componentsDiscoveryTool();
expect(result.content).toHaveLength(1);
expect(result.content[0].text).toContain('Error: Components Discovery Failed');
expect(result.content[0].text).toContain('Error: Unexpected error type');
expect(result.isError).toBe(true);
});
});
describe('Tool behavior verification', () => {
it('should log the correct URL being fetched', async () => {
const consoleSpy = jest.spyOn(console, 'log');
mockFetchGitLabFileContentSafe.mockResolvedValueOnce('test content');
await componentsDiscoveryTool();
expect(consoleSpy).toHaveBeenCalledWith(
'Fetching components list from: https://gitlab.example.com/repo/-/raw/main/llms.txt'
);
});
it('should call GitLab client with correct URL', async () => {
mockFetchGitLabFileContentSafe.mockResolvedValueOnce('test content');
await componentsDiscoveryTool();
expect(mockFetchGitLabFileContentSafe).toHaveBeenCalledTimes(1);
expect(mockFetchGitLabFileContentSafe).toHaveBeenCalledWith(
'https://gitlab.example.com/repo/-/raw/main/llms.txt'
);
});
it('should return MCP-compliant response structure with valid JSON', async () => {
mockFetchGitLabFileContentSafe.mockResolvedValueOnce('test content');
const result = await componentsDiscoveryTool();
// 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');
// Verify the content is valid JSON
expect(() => JSON.parse(result.content[0].text)).not.toThrow();
const jsonData = JSON.parse(result.content[0].text);
expect(jsonData).toHaveProperty('source');
expect(jsonData).toHaveProperty('totalComponents');
expect(jsonData).toHaveProperty('componentsByType');
expect(jsonData).toHaveProperty('components');
});
});
});