Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { beforeEach, describe, expect, it, vi } from 'vitest';6import { TestLogService } from '../../../../../platform/testing/common/testLogService';7import { MockMcpServer, parseToolResult, createMockUri, createMockDiagnostic } from './testHelpers';89const { mockGetDiagnostics } = vi.hoisted(() => ({10mockGetDiagnostics: vi.fn(),11}));1213vi.mock('vscode', () => {14const DiagnosticSeverity = {15Error: 0,16Warning: 1,17Information: 2,18Hint: 3,190: 'Error',201: 'Warning',212: 'Information',223: 'Hint',23};2425return {26Uri: {27parse: (str: string) => ({28toString: () => str,29fsPath: str.replace('file://', ''),30scheme: 'file',31}),32},33languages: {34getDiagnostics: mockGetDiagnostics,35},36DiagnosticSeverity,37};38});3940import { registerGetDiagnosticsTool } from '../tools/getDiagnostics';4142interface DiagnosticsFileResult {43uri: string;44filePath: string;45diagnostics: Array<{46message: string;47severity: string;48source?: string;49code?: string | number;50range: {51start: { line: number; character: number };52end: { line: number; character: number };53};54}>;55}5657describe('getDiagnostics tool', () => {58const logger = new TestLogService();59let server: MockMcpServer;6061beforeEach(() => {62vi.clearAllMocks();63server = new MockMcpServer();64registerGetDiagnosticsTool(server as unknown as import('@modelcontextprotocol/sdk/server/mcp.js').McpServer, logger);65});6667it('should register the get_diagnostics tool', () => {68expect(server.hasToolRegistered('get_diagnostics')).toBe(true);69});7071it('should return diagnostics for a specific URI', async () => {72const mockDiag = createMockDiagnostic('Test error', 0, 0, 0, 0, 10, 'test-source', 'TEST001');73mockGetDiagnostics.mockReturnValue([mockDiag]);7475const handler = server.getToolHandler('get_diagnostics')!;76const result = parseToolResult<DiagnosticsFileResult[]>(await handler({ uri: 'file:///test/file.ts' }));7778expect(result).toHaveLength(1);79expect(result[0].uri).toBe('file:///test/file.ts');80expect(result[0].diagnostics).toHaveLength(1);81});8283it('should return empty array when no diagnostics exist for URI', async () => {84mockGetDiagnostics.mockReturnValue([]);8586const handler = server.getToolHandler('get_diagnostics')!;87const result = parseToolResult<DiagnosticsFileResult[]>(await handler({ uri: 'file:///clean/file.ts' }));8889expect(result).toHaveLength(0);90});9192it('should return all diagnostics when no URI is provided', async () => {93const uri1 = createMockUri('/file1.ts');94const uri2 = createMockUri('/file2.ts');95const diag1 = createMockDiagnostic('Error in file 1', 0, 0, 0, 0, 5);96const diag2 = createMockDiagnostic('Warning in file 2', 1, 1, 0, 1, 5);97mockGetDiagnostics.mockReturnValue([98[uri1, [diag1]],99[uri2, [diag2]],100]);101102const handler = server.getToolHandler('get_diagnostics')!;103const result = parseToolResult<DiagnosticsFileResult[]>(await handler({}));104105expect(result).toHaveLength(2);106});107108it('should map severity levels correctly', async () => {109const diagnostics = [110createMockDiagnostic('Error', 0, 0, 0, 0, 1),111createMockDiagnostic('Warning', 1, 1, 0, 1, 1),112createMockDiagnostic('Information', 2, 2, 0, 2, 1),113createMockDiagnostic('Hint', 3, 3, 0, 3, 1),114];115mockGetDiagnostics.mockReturnValue(diagnostics);116117const handler = server.getToolHandler('get_diagnostics')!;118const result = parseToolResult<DiagnosticsFileResult[]>(await handler({ uri: 'file:///test.ts' }));119120const severities = result[0].diagnostics.map(d => d.severity);121expect(severities).toContain('error');122expect(severities).toContain('warning');123expect(severities).toContain('information');124expect(severities).toContain('hint');125});126127it('should include diagnostic range, source, and code', async () => {128const mockDiag = createMockDiagnostic('Test error', 0, 5, 10, 5, 20, 'test-linter', 'WARN001');129mockGetDiagnostics.mockReturnValue([mockDiag]);130131const handler = server.getToolHandler('get_diagnostics')!;132const result = parseToolResult<DiagnosticsFileResult[]>(await handler({ uri: 'file:///test.ts' }));133134const diag = result[0].diagnostics[0];135expect(diag.message).toBe('Test error');136expect(diag.source).toBe('test-linter');137expect(diag.code).toBe('WARN001');138expect(diag.range.start.line).toBe(5);139expect(diag.range.start.character).toBe(10);140expect(diag.range.end.line).toBe(5);141expect(diag.range.end.character).toBe(20);142});143144it('should filter out files with no diagnostics when returning all', async () => {145const uri1 = createMockUri('/has-diagnostics.ts');146const uri2 = createMockUri('/no-diagnostics.ts');147const diag = createMockDiagnostic('Has error', 0, 0, 0, 0, 1);148mockGetDiagnostics.mockReturnValue([149[uri1, [diag]],150[uri2, []],151]);152153const handler = server.getToolHandler('get_diagnostics')!;154const result = parseToolResult<DiagnosticsFileResult[]>(await handler({}));155156expect(result).toHaveLength(1);157expect(result[0].uri).toBe('file:///has-diagnostics.ts');158});159160it('should handle object-style code in diagnostics', async () => {161const mockDiag = {162message: 'Object code error',163severity: 0,164range: {165start: { line: 0, character: 0 },166end: { line: 0, character: 5 },167},168source: 'ts',169code: { value: 2304, target: 'https://example.com' },170};171mockGetDiagnostics.mockReturnValue([mockDiag]);172173const handler = server.getToolHandler('get_diagnostics')!;174const result = parseToolResult<DiagnosticsFileResult[]>(await handler({ uri: 'file:///test.ts' }));175176expect(result[0].diagnostics[0].code).toBe(2304);177});178});179180181