/**
* @vitest-environment node
*/
import { vi } from 'vitest';
import type { DeepSourceIssue } from '../models/issues';
import type { PaginatedResult } from '../utils/pagination/types';
// Create mock logger
const mockLogger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
// Mock modules before importing the implementation
vi.mock('../utils/logging/logger', () => ({
createLogger: vi.fn(() => mockLogger),
}));
// Mock the DeepSource client
const mockGetIssues = vi.fn();
vi.mock('../deepsource', () => ({
DeepSourceClient: vi.fn().mockImplementation(() => ({
getIssues: mockGetIssues,
})),
ReportType: {
OWASP_TOP_10: 'OWASP_TOP_10',
SANS_TOP_25: 'SANS_TOP_25',
MISRA_C: 'MISRA_C',
CODE_COVERAGE: 'CODE_COVERAGE',
CODE_HEALTH_TREND: 'CODE_HEALTH_TREND',
ISSUE_DISTRIBUTION: 'ISSUE_DISTRIBUTION',
ISSUES_PREVENTED: 'ISSUES_PREVENTED',
ISSUES_AUTOFIXED: 'ISSUES_AUTOFIXED',
},
ReportStatus: {
PASSING: 'PASSING',
FAILING: 'FAILING',
NOOP: 'NOOP',
},
}));
// Import the modules under test AFTER mocking
const { handleDeepsourceProjectIssues } = await import('../handlers/project-issues');
describe('Project Issues Handler', () => {
// Environment backup
let originalEnv: Record<string, string | undefined>;
beforeEach(() => {
// Backup environment
originalEnv = { ...process.env };
process.env.DEEPSOURCE_API_KEY = 'test-api-key';
// Reset mocks
vi.clearAllMocks();
});
afterEach(() => {
// Restore environment
process.env = originalEnv;
});
describe('handleDeepsourceProjectIssues', () => {
it('should fetch project issues successfully', async () => {
// Mock response data
const mockIssues: PaginatedResult<DeepSourceIssue> = {
items: [
{
id: 'issue-1',
title: 'Test Issue 1',
shortcode: 'TEST-001',
category: 'BUG',
severity: 'CRITICAL',
status: 'OPEN',
issue_text: 'This is a test issue',
file_path: 'src/test.ts',
line_number: 42,
tags: ['javascript'],
},
],
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'cursor-1',
endCursor: 'cursor-2',
},
totalCount: 1,
};
// Set up the mock implementation
mockGetIssues.mockResolvedValue(mockIssues);
// Call the handler
const result = await handleDeepsourceProjectIssues({
projectKey: 'test-project',
});
// Verify the result
expect(result).toBeDefined();
expect(result.content).toHaveLength(1);
expect(result.content[0].type).toBe('text');
// Parse the response and verify structure
const parsedContent = JSON.parse(result.content[0].text);
expect(parsedContent).toHaveProperty('issues');
expect(parsedContent.issues).toHaveLength(1);
expect(parsedContent.issues[0]).toMatchObject({
id: 'issue-1',
title: 'Test Issue 1',
shortcode: 'TEST-001',
category: 'BUG',
severity: 'CRITICAL',
status: 'OPEN',
});
});
it('should throw error when API key is not set', async () => {
// Remove API key from environment
delete process.env.DEEPSOURCE_API_KEY;
// Call the handler and expect it to throw
await expect(
handleDeepsourceProjectIssues({
projectKey: 'test-project',
})
).rejects.toThrow('Configuration error: DeepSource API key is required but not configured');
});
it('should handle errors from the client', async () => {
// Set up the mock to throw an error
const testError = new Error('API client error');
mockGetIssues.mockRejectedValue(testError);
// Call the handler and expect it to throw
await expect(
handleDeepsourceProjectIssues({
projectKey: 'test-project',
})
).rejects.toThrow('API client error');
});
it('should handle max_pages parameter with multi-page fetching message', async () => {
// Mock response data
const mockIssues: PaginatedResult<DeepSourceIssue> = {
items: [
{
id: 'issue-1',
title: 'Test Issue 1',
shortcode: 'TEST-001',
category: 'BUG',
severity: 'CRITICAL',
status: 'OPEN',
issue_text: 'Test issue message',
file_path: 'src/test.ts',
line_number: 10,
tags: ['test'],
},
],
totalCount: 1,
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
},
pagination: {
has_more_pages: false,
page_size: 1,
},
};
mockGetIssues.mockResolvedValue(mockIssues);
const result = await handleDeepsourceProjectIssues({
projectKey: 'test-project',
max_pages: 5,
});
// Parse the response content
const parsedContent = JSON.parse(result.content[0].text);
expect(parsedContent.issues).toEqual(mockIssues.items);
expect(parsedContent.usage_examples.pagination.next_page).toBe(
'Multi-page fetching enabled with max_pages parameter'
);
expect(mockGetIssues).toHaveBeenCalledWith('test-project', { max_pages: 5 });
});
});
});