|
| 1 | +/*--------------------------------------------------------------------------------------------- |
| 2 | + * Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | + * Licensed under the MIT License. See License.txt in the project root for license information. |
| 4 | + *--------------------------------------------------------------------------------------------*/ |
| 5 | + |
| 6 | +import { describe, expect, it } from 'vitest'; |
| 7 | +import { extractFilePath, extractRefsFromMcpTool, extractRefsFromTerminal, extractRepoFromMcpTool, isGitHubMcpTool } from '../sessionStoreTracking'; |
| 8 | + |
| 9 | +describe('extractFilePath', () => { |
| 10 | + it('extracts filePath from replace_string_in_file', () => { |
| 11 | + expect(extractFilePath('replace_string_in_file', { filePath: '/src/foo.ts', oldString: 'a', newString: 'b' })) |
| 12 | + .toBe('/src/foo.ts'); |
| 13 | + }); |
| 14 | + |
| 15 | + it('extracts filePath from multi_replace_string_in_file replacements array', () => { |
| 16 | + expect(extractFilePath('multi_replace_string_in_file', { |
| 17 | + explanation: 'fix imports', |
| 18 | + replacements: [ |
| 19 | + { filePath: '/src/bar.ts', oldString: 'a', newString: 'b' }, |
| 20 | + { filePath: '/src/baz.ts', oldString: 'c', newString: 'd' }, |
| 21 | + ] |
| 22 | + })).toBe('/src/bar.ts'); |
| 23 | + }); |
| 24 | + |
| 25 | + it('extracts filePath from insert_edit_into_file', () => { |
| 26 | + expect(extractFilePath('insert_edit_into_file', { filePath: '/src/edit.ts', code: '// new' })) |
| 27 | + .toBe('/src/edit.ts'); |
| 28 | + }); |
| 29 | + |
| 30 | + it('extracts filePath from create_file', () => { |
| 31 | + expect(extractFilePath('create_file', { filePath: '/src/new.ts', content: '' })) |
| 32 | + .toBe('/src/new.ts'); |
| 33 | + }); |
| 34 | + |
| 35 | + it('extracts filePath from edit_notebook_file', () => { |
| 36 | + expect(extractFilePath('edit_notebook_file', { filePath: '/nb.ipynb', editType: 'edit', cellId: 'c1' })) |
| 37 | + .toBe('/nb.ipynb'); |
| 38 | + }); |
| 39 | + |
| 40 | + it('extracts filePath from read_file', () => { |
| 41 | + expect(extractFilePath('read_file', { filePath: '/src/read.ts', startLine: 1, endLine: 10 })) |
| 42 | + .toBe('/src/read.ts'); |
| 43 | + }); |
| 44 | + |
| 45 | + it('extracts path from list_dir', () => { |
| 46 | + expect(extractFilePath('list_dir', { path: '/src' })) |
| 47 | + .toBe('/src'); |
| 48 | + }); |
| 49 | + |
| 50 | + it('extracts dirPath from create_directory', () => { |
| 51 | + expect(extractFilePath('create_directory', { dirPath: '/src/new-dir' })) |
| 52 | + .toBe('/src/new-dir'); |
| 53 | + }); |
| 54 | + |
| 55 | + it('extracts file path from apply_patch input text', () => { |
| 56 | + const input = '*** Begin Patch\n*** Update File: /src/hello.ts\n@@class Foo\n- bar\n+ baz\n*** End Patch'; |
| 57 | + expect(extractFilePath('apply_patch', { input })) |
| 58 | + .toBe('/src/hello.ts'); |
| 59 | + }); |
| 60 | + |
| 61 | + it('extracts file path from apply_patch Add File', () => { |
| 62 | + const input = '*** Begin Patch\n*** Add File: /src/new.ts\n+export const x = 1;\n*** End Patch'; |
| 63 | + expect(extractFilePath('apply_patch', { input })) |
| 64 | + .toBe('/src/new.ts'); |
| 65 | + }); |
| 66 | + |
| 67 | + it('extracts file path from apply_patch Delete File', () => { |
| 68 | + const input = '*** Begin Patch\n*** Delete File: /src/old.ts\n*** End Patch'; |
| 69 | + expect(extractFilePath('apply_patch', { input })) |
| 70 | + .toBe('/src/old.ts'); |
| 71 | + }); |
| 72 | + |
| 73 | + it('falls back to filePath arg for apply_patch when present', () => { |
| 74 | + expect(extractFilePath('apply_patch', { filePath: '/from/arg.ts', input: '*** Update File: /from/input.ts' })) |
| 75 | + .toBe('/from/arg.ts'); |
| 76 | + }); |
| 77 | + |
| 78 | + it('extracts path from CLI str_replace_editor (backward compat)', () => { |
| 79 | + expect(extractFilePath('str_replace_editor', { path: '/cli/file.py' })) |
| 80 | + .toBe('/cli/file.py'); |
| 81 | + }); |
| 82 | + |
| 83 | + it('extracts path from CLI create tool (backward compat)', () => { |
| 84 | + expect(extractFilePath('create', { path: '/cli/new.py' })) |
| 85 | + .toBe('/cli/new.py'); |
| 86 | + }); |
| 87 | + |
| 88 | + it('returns undefined for unknown tools', () => { |
| 89 | + expect(extractFilePath('run_in_terminal', { command: 'ls' })) |
| 90 | + .toBeUndefined(); |
| 91 | + expect(extractFilePath('file_search', { query: '**/*.ts' })) |
| 92 | + .toBeUndefined(); |
| 93 | + }); |
| 94 | + |
| 95 | + it('returns undefined for null args', () => { |
| 96 | + expect(extractFilePath('create_file', null)) |
| 97 | + .toBeUndefined(); |
| 98 | + }); |
| 99 | + |
| 100 | + it('returns undefined when filePath is not a string', () => { |
| 101 | + expect(extractFilePath('create_file', { filePath: 42 })) |
| 102 | + .toBeUndefined(); |
| 103 | + }); |
| 104 | +}); |
| 105 | + |
| 106 | +describe('isGitHubMcpTool', () => { |
| 107 | + it('matches mcp_github_ prefix (VS Code)', () => { |
| 108 | + expect(isGitHubMcpTool('mcp_github_issue_write')).toBe(true); |
| 109 | + expect(isGitHubMcpTool('mcp_github_create_pull_request')).toBe(true); |
| 110 | + expect(isGitHubMcpTool('mcp_github_search_issues')).toBe(true); |
| 111 | + }); |
| 112 | + |
| 113 | + it('matches github-mcp-server- prefix (CLI)', () => { |
| 114 | + expect(isGitHubMcpTool('github-mcp-server-create_issue')).toBe(true); |
| 115 | + }); |
| 116 | + |
| 117 | + it('rejects non-GitHub MCP tools', () => { |
| 118 | + expect(isGitHubMcpTool('read_file')).toBe(false); |
| 119 | + expect(isGitHubMcpTool('mcp_perplexity_ask')).toBe(false); |
| 120 | + expect(isGitHubMcpTool('run_in_terminal')).toBe(false); |
| 121 | + }); |
| 122 | +}); |
| 123 | + |
| 124 | +describe('extractRefsFromMcpTool', () => { |
| 125 | + it('extracts PR ref from mcp_github_pull_request_read', () => { |
| 126 | + const refs = extractRefsFromMcpTool('mcp_github_pull_request_read', { pullNumber: 42, owner: 'ms', repo: 'vscode' }); |
| 127 | + expect(refs).toEqual([{ ref_type: 'pr', ref_value: '42' }]); |
| 128 | + }); |
| 129 | + |
| 130 | + it('extracts issue ref from mcp_github_issue_write', () => { |
| 131 | + const refs = extractRefsFromMcpTool('mcp_github_issue_write', { issue_number: 123, owner: 'ms', repo: 'vscode' }); |
| 132 | + expect(refs).toEqual([{ ref_type: 'issue', ref_value: '123' }]); |
| 133 | + }); |
| 134 | + |
| 135 | + it('extracts commit ref from mcp_github_get_commit', () => { |
| 136 | + const refs = extractRefsFromMcpTool('mcp_github_get_commit', { sha: 'abc123', owner: 'ms', repo: 'vscode' }); |
| 137 | + expect(refs).toEqual([{ ref_type: 'commit', ref_value: 'abc123' }]); |
| 138 | + }); |
| 139 | + |
| 140 | + it('returns empty for non-matching tool name', () => { |
| 141 | + expect(extractRefsFromMcpTool('mcp_github_search_code', { query: 'foo' })).toEqual([]); |
| 142 | + }); |
| 143 | +}); |
| 144 | + |
| 145 | +describe('extractRepoFromMcpTool', () => { |
| 146 | + it('extracts owner/repo', () => { |
| 147 | + expect(extractRepoFromMcpTool({ owner: 'microsoft', repo: 'vscode' })) |
| 148 | + .toBe('microsoft/vscode'); |
| 149 | + }); |
| 150 | + |
| 151 | + it('returns undefined when missing', () => { |
| 152 | + expect(extractRepoFromMcpTool({ owner: 'microsoft' })).toBeUndefined(); |
| 153 | + expect(extractRepoFromMcpTool({})).toBeUndefined(); |
| 154 | + }); |
| 155 | +}); |
| 156 | + |
| 157 | +describe('extractRefsFromTerminal', () => { |
| 158 | + it('extracts PR ref from gh pr create output', () => { |
| 159 | + const refs = extractRefsFromTerminal( |
| 160 | + { command: 'gh pr create --title "fix"' }, |
| 161 | + 'https://github.com/microsoft/vscode/pull/999', |
| 162 | + ); |
| 163 | + expect(refs).toEqual([{ ref_type: 'pr', ref_value: '999' }]); |
| 164 | + }); |
| 165 | + |
| 166 | + it('extracts issue ref from gh issue create output', () => { |
| 167 | + const refs = extractRefsFromTerminal( |
| 168 | + { command: 'gh issue create --title "bug"' }, |
| 169 | + 'https://github.com/microsoft/vscode/issues/456', |
| 170 | + ); |
| 171 | + expect(refs).toEqual([{ ref_type: 'issue', ref_value: '456' }]); |
| 172 | + }); |
| 173 | + |
| 174 | + it('extracts commit SHA from git commit output', () => { |
| 175 | + const refs = extractRefsFromTerminal( |
| 176 | + { command: 'git commit -m "fix"' }, |
| 177 | + '[main abc1234] fix\n 1 file changed', |
| 178 | + ); |
| 179 | + expect(refs).toEqual([{ ref_type: 'commit', ref_value: 'abc1234' }]); |
| 180 | + }); |
| 181 | + |
| 182 | + it('returns empty for unrelated commands', () => { |
| 183 | + expect(extractRefsFromTerminal({ command: 'ls -la' }, 'output')).toEqual([]); |
| 184 | + }); |
| 185 | +}); |
0 commit comments