mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-15 04:18:05 +00:00
84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { renameSymbol } from '../../src/mcp/local/rename/rename-plan.js';
|
|
import { createRenameFixtureHelpers } from '../helpers/rename-provider-fixture.js';
|
|
|
|
const { expectRename, fixture } = createRenameFixtureHelpers();
|
|
const functions = 'def target(): return 7\ndef run(): return target()\n';
|
|
|
|
describe('Python module export-list rename safety', () => {
|
|
it.each([
|
|
['old name in a list', '__all__ = ["target"]\n'],
|
|
['destination in a tuple', '__all__ = ("rena\\u006ded",)\n'],
|
|
['annotated export list', '__all__: list[str] = ["target"]\n'],
|
|
['unknown export list', '__all__ = discover_exports()\n'],
|
|
['unknown literal element', '__all__ = [export_name]\n'],
|
|
['augmented assignment', '__all__ = ["run"]\n__all__ += ["target"]\n'],
|
|
['mutation', '__all__ = ["run"]\n__all__.append("target")\n'],
|
|
['escaping list alias', '__all__ = ["run"]\nexports = __all__\nexports.append("target")\n'],
|
|
[
|
|
'mutation through a global binding',
|
|
'__all__ = ["run"]\ndef configure():\n global __all__\n __all__.append("target")\n',
|
|
],
|
|
])(
|
|
'refuses %s without changing any source',
|
|
async (_description, exports) => {
|
|
const content = exports + functions;
|
|
const { root, nodes, symbol } = await fixture({ 'model.py': content });
|
|
for (const dry_run of [true, false]) {
|
|
expect(
|
|
await renameSymbol(root, symbol, { new_name: 'renamed', dry_run }, nodes),
|
|
).toMatchObject({
|
|
status: 'error',
|
|
planning_status: 'unsupported',
|
|
code: 'unsupported_occurrence',
|
|
total_edits: 0,
|
|
applied: false,
|
|
});
|
|
expect(await fs.readFile(path.join(root, 'model.py'), 'utf8')).toBe(content);
|
|
}
|
|
},
|
|
60_000,
|
|
);
|
|
|
|
it('checks an unaliased re-export module whose local binding changes', async () => {
|
|
const files = {
|
|
'model.py': functions,
|
|
'public.py': 'from model import target\n__all__ = ["target"]\n',
|
|
};
|
|
const { root, nodes, symbol } = await fixture(files, 'target', 'Function', 'model.py');
|
|
for (const dry_run of [true, false]) {
|
|
expect(
|
|
await renameSymbol(root, symbol, { new_name: 'renamed', dry_run }, nodes),
|
|
).toMatchObject({
|
|
status: 'error',
|
|
planning_status: 'unsupported',
|
|
code: 'unsupported_occurrence',
|
|
total_edits: 0,
|
|
applied: false,
|
|
});
|
|
for (const [file, content] of Object.entries(files))
|
|
expect(await fs.readFile(path.join(root, file), 'utf8')).toBe(content);
|
|
}
|
|
}, 60_000);
|
|
|
|
it('preserves unrelated module exports, local export-list shadows, and ordinary strings', async () => {
|
|
const decoys =
|
|
'__all__ = ("run",)\nnote = "target renamed"\n' +
|
|
'def inspect():\n __all__ = ["target", "renamed"]\n return __all__\n' +
|
|
'class Metadata:\n __all__ = ["target", "renamed"]\n';
|
|
const other = '__all__ = ["target", "renamed"]\ndef target(): return 99\n';
|
|
await expectRename(
|
|
{ 'model.py': decoys + functions, 'other.py': other },
|
|
{
|
|
'model.py': decoys + 'def renamed(): return 7\ndef run(): return renamed()\n',
|
|
'other.py': other,
|
|
},
|
|
'target',
|
|
'Function',
|
|
'renamed',
|
|
'model.py',
|
|
);
|
|
}, 60_000);
|
|
});
|