test(ui): pin CRLF source slices to the index line numbering (CG-42)
A CRLF file must come back with the graph's own line numbers and without a trailing carriage return on every line — the case a Windows checkout with core.autocrlf produces. It is decided by bytes rather than by the OS, so it is covered here rather than only on the VM. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
951ba3678a
commit
e7288ffa36
@@ -192,6 +192,15 @@ ${callers}
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// CRLF on purpose: tree-sitter numbers rows by `\n`, so a CRLF file must come
|
||||||
|
// back with the same line numbers the graph recorded — and without the stray
|
||||||
|
// `\r` rendering at the end of every line. This is what a Windows checkout
|
||||||
|
// with core.autocrlf looks like, and it is decided by bytes, not by the OS.
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(srcDir, 'crlf.ts'),
|
||||||
|
['export function windowsStyle(n: number): number {', ' return n + 1;', '}', ''].join('\r\n')
|
||||||
|
);
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(testsDir, 'service.test.ts'),
|
path.join(testsDir, 'service.test.ts'),
|
||||||
`import { Service } from '../src/service';
|
`import { Service } from '../src/service';
|
||||||
@@ -613,6 +622,27 @@ describe('GET /api/source', () => {
|
|||||||
expect(body.lines).toHaveLength(2);
|
expect(body.lines).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps a CRLF file on the index line numbering, without the stray carriage returns', async () => {
|
||||||
|
const node = await getJson(`/api/node/${await idOf('windowsStyle', 'function')}`);
|
||||||
|
expect(node.node.file).toBe('src/crlf.ts');
|
||||||
|
|
||||||
|
const body = await getJson('/api/source?file=src/crlf.ts');
|
||||||
|
expect(body.drift).toBe(false);
|
||||||
|
expect(body.totalLines).toBe(3);
|
||||||
|
expect(body.lines).toEqual([
|
||||||
|
'export function windowsStyle(n: number): number {',
|
||||||
|
' return n + 1;',
|
||||||
|
'}',
|
||||||
|
]);
|
||||||
|
expect(body.lines.some((l: string) => l.includes('\r'))).toBe(false);
|
||||||
|
|
||||||
|
// The symbol's indexed range still names its own body.
|
||||||
|
const slice = await getJson(
|
||||||
|
`/api/source?file=src/crlf.ts&from=${node.node.line}&to=${node.node.endLine}`
|
||||||
|
);
|
||||||
|
expect(slice.lines[0]).toContain('windowsStyle');
|
||||||
|
});
|
||||||
|
|
||||||
it('refuses a path that escapes the project', async () => {
|
it('refuses a path that escapes the project', async () => {
|
||||||
const traversal = await getStatusAndJson(
|
const traversal = await getStatusAndJson(
|
||||||
'/api/source?file=' + encodeURIComponent('../../../etc/passwd')
|
'/api/source?file=' + encodeURIComponent('../../../etc/passwd')
|
||||||
|
|||||||
Reference in New Issue
Block a user