mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-16 23:43:12 +00:00
* fix(cli): do not call zero-symbol detect-changes a clean tree Print backend summary.message and distinguish a parsed diff with no overlapping indexed symbols from an empty git diff. Pin --color=never so color.ui=always cannot hide +++ b/ headers. * fix(cli): localize clean-tree detect-changes and skip no-overlap when partial Production empty diffs carry English summary.message; route that through t() so zh-CN fires. Do not claim no indexed-symbol overlap on queryDegraded partial results. Pin formatter tests to en and cover the production payload shapes. * fix(cli): prettier detect-changes-format and degraded eval assertion --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
106 lines
4.1 KiB
TypeScript
106 lines
4.1 KiB
TypeScript
import { execFileSync } from 'node:child_process';
|
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { buildDetectChangesDiffArgs } from '../../src/mcp/local/local-backend.js';
|
|
import { parseDiffHunks } from '../../src/storage/git.js';
|
|
import { diffArgsFor } from '../helpers/detect-changes-diff-args.js';
|
|
import { commitAll, initGitRepo } from '../helpers/temp-git-repo.js';
|
|
|
|
/** The six flags every scope carries, ahead of its own ref/staging arguments. */
|
|
const GUARD_FLAGS = [
|
|
'diff',
|
|
'--ignore-cr-at-eol',
|
|
'--no-ext-diff',
|
|
'--color=never',
|
|
'--src-prefix=a/',
|
|
'--dst-prefix=b/',
|
|
];
|
|
|
|
describe('detect_changes EOL filtering', () => {
|
|
it.each([
|
|
['unstaged', undefined, [...GUARD_FLAGS, '-U0']],
|
|
['staged', undefined, [...GUARD_FLAGS, '--staged', '-U0']],
|
|
['all', undefined, [...GUARD_FLAGS, 'HEAD', '-U0']],
|
|
['compare', 'main', [...GUARD_FLAGS, 'main', '-U0']],
|
|
])('adds the EOL and prefix guards for %s scope', (scope, baseRef, expected) => {
|
|
expect(buildDetectChangesDiffArgs(scope, baseRef)).toEqual(expected);
|
|
});
|
|
|
|
it('requires a base ref for compare scope', () => {
|
|
expect(buildDetectChangesDiffArgs('compare')).toBeNull();
|
|
});
|
|
|
|
it('suppresses CRLF-only changes but retains other whitespace changes', () => {
|
|
const repoDir = mkdtempSync(path.join(tmpdir(), 'gitnexus-detect-eol-'));
|
|
try {
|
|
initGitRepo(repoDir);
|
|
writeFileSync(path.join(repoDir, 'sample.ts'), 'const first = 1;\r\nconst second = 2;\r\n');
|
|
commitAll(repoDir, 'initial');
|
|
|
|
writeFileSync(path.join(repoDir, 'sample.ts'), 'const first = 1;\nconst second = 2;\n');
|
|
const diffArgs = diffArgsFor('unstaged');
|
|
expect(
|
|
execFileSync('git', diffArgs, {
|
|
cwd: repoDir,
|
|
encoding: 'utf8',
|
|
}),
|
|
).toBe('');
|
|
|
|
writeFileSync(path.join(repoDir, 'sample.ts'), 'const first = 1;\n const second = 2;\n');
|
|
expect(
|
|
execFileSync('git', diffArgs, {
|
|
cwd: repoDir,
|
|
encoding: 'utf8',
|
|
}),
|
|
).toContain('+ const second = 2;');
|
|
} finally {
|
|
rmSync(repoDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
/**
|
|
* #2915 — the user's own git config could turn the pre-commit gate into a
|
|
* silent all-clear.
|
|
*
|
|
* `parseDiffHunks` recognises a file by its `+++ b/` header, and git only emits
|
|
* that prefix by default: `diff.noprefix` emits `+++ sample.py` and
|
|
* `diff.mnemonicPrefix` emits `+++ w/sample.py`. Either one parses to ZERO
|
|
* files, which `detect_changes` reported as "No changes detected." with exit 0
|
|
* and no `partial`. The flags pin the prefixes the parser matches.
|
|
*/
|
|
describe('detect_changes diff prefix pinning', () => {
|
|
it.each([
|
|
['diff.noprefix', '+++ sample.py'],
|
|
['diff.mnemonicPrefix', '+++ w/sample.py'],
|
|
])('parses the diff even with %s configured', (configKey, hostileHeader) => {
|
|
const repoDir = mkdtempSync(path.join(tmpdir(), 'gitnexus-detect-prefix-'));
|
|
try {
|
|
initGitRepo(repoDir);
|
|
writeFileSync(path.join(repoDir, 'sample.py'), 'def hello():\n return 1\n');
|
|
commitAll(repoDir, 'initial');
|
|
execFileSync('git', ['config', configKey, 'true'], { cwd: repoDir });
|
|
writeFileSync(path.join(repoDir, 'sample.py'), 'def hello():\n return 2\n');
|
|
|
|
// The config really is hostile: without the prefix flags git relabels the
|
|
// headers and the whole diff parses to nothing.
|
|
const unguarded = execFileSync('git', ['diff', '--ignore-cr-at-eol', '-U0'], {
|
|
cwd: repoDir,
|
|
encoding: 'utf8',
|
|
});
|
|
expect(unguarded).toContain(hostileHeader);
|
|
expect(parseDiffHunks(unguarded)).toEqual([]);
|
|
|
|
// Source line 2 is the edit; the hunk header is git's own 1-based space.
|
|
expect(
|
|
parseDiffHunks(
|
|
execFileSync('git', diffArgsFor('unstaged'), { cwd: repoDir, encoding: 'utf8' }),
|
|
),
|
|
).toEqual([{ filePath: 'sample.py', hunks: [{ startLine: 2, endLine: 2 }] }]);
|
|
} finally {
|
|
rmSync(repoDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|