mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
Some checks are pending
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
158 lines
5 KiB
TypeScript
158 lines
5 KiB
TypeScript
/**
|
|
* Tests for WAL corruption feedback in MCP error responses (#1402).
|
|
*/
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const { lbugMocks, platformMocks, repoMocks } = vi.hoisted(() => ({
|
|
lbugMocks: {
|
|
initLbug: vi.fn().mockResolvedValue(undefined),
|
|
executeQuery: vi.fn(),
|
|
executeParameterized: vi.fn(),
|
|
closeLbug: vi.fn().mockResolvedValue(undefined),
|
|
isLbugReady: vi.fn().mockReturnValue(true),
|
|
},
|
|
platformMocks: {
|
|
isVectorExtensionSupportedByPlatform: vi.fn().mockReturnValue(true),
|
|
},
|
|
repoMocks: {
|
|
listRegisteredRepos: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../src/core/lbug/pool-adapter.js', async (importOriginal) => {
|
|
const actual = await importOriginal();
|
|
return { ...actual, ...lbugMocks };
|
|
});
|
|
|
|
vi.mock('../../src/mcp/core/lbug-adapter.js', async (importOriginal) => {
|
|
const actual = await importOriginal();
|
|
return { ...actual, ...lbugMocks };
|
|
});
|
|
|
|
vi.mock('../../src/storage/repo-manager.js', () => ({
|
|
listRegisteredRepos: repoMocks.listRegisteredRepos,
|
|
cleanupOldKuzuFiles: vi.fn().mockResolvedValue({ found: false, needsReindex: false }),
|
|
findSiblingClones: vi.fn().mockResolvedValue([]),
|
|
}));
|
|
|
|
vi.mock('../../src/core/git-staleness.js', () => ({
|
|
checkStaleness: vi.fn().mockReturnValue({ isStale: false, commitsBehind: 0 }),
|
|
checkCwdMatch: vi.fn().mockResolvedValue({ match: 'none' }),
|
|
}));
|
|
|
|
vi.mock('../../src/core/platform/capabilities.js', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('../../src/core/platform/capabilities.js')>();
|
|
return {
|
|
...actual,
|
|
isVectorExtensionSupportedByPlatform: platformMocks.isVectorExtensionSupportedByPlatform,
|
|
};
|
|
});
|
|
|
|
vi.mock('../../src/core/search/bm25-index.js', () => ({
|
|
searchFTSFromLbug: vi.fn().mockResolvedValue([]),
|
|
}));
|
|
|
|
vi.mock('../../src/mcp/core/embedder.js', () => ({
|
|
embedQuery: vi.fn().mockResolvedValue([]),
|
|
getEmbeddingDims: vi.fn().mockReturnValue(384),
|
|
}));
|
|
|
|
import { LocalBackend } from '../../src/mcp/local/local-backend.js';
|
|
|
|
const MOCK_REPO_ENTRY = {
|
|
name: 'test-repo',
|
|
path: '/tmp/test',
|
|
storagePath: '/tmp/test/.gitnexus',
|
|
indexedAt: '2026-05-01T00:00:00Z',
|
|
lastCommit: 'abc1234',
|
|
};
|
|
|
|
async function makeBackend(): Promise<LocalBackend> {
|
|
const backend = new LocalBackend();
|
|
await backend.init();
|
|
return backend;
|
|
}
|
|
|
|
describe('WAL corruption feedback in MCP responses (#1402)', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
lbugMocks.initLbug.mockResolvedValue(undefined);
|
|
lbugMocks.executeQuery.mockResolvedValue([]);
|
|
lbugMocks.executeParameterized.mockResolvedValue([]);
|
|
lbugMocks.isLbugReady.mockReturnValue(true);
|
|
repoMocks.listRegisteredRepos.mockResolvedValue([MOCK_REPO_ENTRY]);
|
|
});
|
|
|
|
it('impact returns WAL suggestion on corrupted WAL error', async () => {
|
|
const backend = await makeBackend();
|
|
lbugMocks.executeParameterized.mockRejectedValueOnce(
|
|
new Error('Runtime exception: Corrupted wal file. Read out invalid WAL record type.'),
|
|
);
|
|
|
|
const result = await backend.callTool('impact', {
|
|
repo: 'test-repo',
|
|
target: 'MyClass',
|
|
direction: 'upstream',
|
|
});
|
|
|
|
expect(result.error).toBeDefined();
|
|
expect(result.suggestion).toBe(
|
|
'The graph query failed — try gitnexus context <symbol> as a fallback',
|
|
);
|
|
expect(result.recoverySuggestion).toBeDefined();
|
|
});
|
|
|
|
it('cypher returns WAL recoverySuggestion on corrupted WAL error', async () => {
|
|
const backend = await makeBackend();
|
|
lbugMocks.executeParameterized.mockRejectedValueOnce(new Error('Corrupted wal file'));
|
|
|
|
const result = await backend.callTool('cypher', {
|
|
repo: 'test-repo',
|
|
query: 'MATCH (n) RETURN n LIMIT 1',
|
|
});
|
|
|
|
expect(result.error).toBe('Corrupted wal file');
|
|
expect(result.recoverySuggestion).toBeDefined();
|
|
});
|
|
|
|
it('context returns WAL recoverySuggestion on corrupted WAL error', async () => {
|
|
const backend = await makeBackend();
|
|
lbugMocks.executeParameterized.mockRejectedValueOnce(new Error('Corrupted wal file'));
|
|
|
|
const result = await backend.callTool('context', {
|
|
repo: 'test-repo',
|
|
name: 'MyClass',
|
|
});
|
|
|
|
expect(result.error).toBe('Corrupted wal file');
|
|
expect(result.recoverySuggestion).toBeDefined();
|
|
});
|
|
|
|
it('non-WAL errors do not include WAL suggestion', async () => {
|
|
const backend = await makeBackend();
|
|
lbugMocks.executeParameterized.mockRejectedValueOnce(new Error('Some other error'));
|
|
|
|
const result = await backend.callTool('impact', {
|
|
repo: 'test-repo',
|
|
target: 'MyClass',
|
|
direction: 'upstream',
|
|
});
|
|
|
|
expect(result.error).toBeDefined();
|
|
expect(result.suggestion).toBe(
|
|
'The graph query failed — try gitnexus context <symbol> as a fallback',
|
|
);
|
|
});
|
|
|
|
it('context preserves non-WAL throw behavior', async () => {
|
|
const backend = await makeBackend();
|
|
lbugMocks.executeParameterized.mockRejectedValueOnce(new Error('Some other error'));
|
|
|
|
await expect(
|
|
backend.callTool('context', {
|
|
repo: 'test-repo',
|
|
name: 'MyClass',
|
|
}),
|
|
).rejects.toThrow('Some other error');
|
|
});
|
|
});
|