mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-22 00:31:17 +00:00
* fix(lbug): recover from WAL corruption by quarantining .wal file (#1402) LadybugDB crashes when the WAL file is corrupted — the open fails with an unrecoverable native error. This makes the pool adapter detect WAL corruption errors, quarantine the offending .wal file, and retry the open. MCP tool responses (cypher, context, impact) now include a recoverySuggestion field when WAL corruption is detected. Changes: - Add isWalCorruptionError() regex-based detector in lbug-config.ts - Add throwOnWalReplayFailure and enableChecksums to createLbugDatabase() - Extract openReadOnlyDatabase() with stdout silencing + db.init() - Add tryQuarantineAndReopen() for .wal quarantine + retry in doInitLbug - Wrap cypher/context/impact with WAL recoverySuggestion in MCP responses - Share WAL_RECOVERY_SUGGESTION constant across all MCP error paths - Fix restoreStdout() placement (before db.init() → finally block) - Add unit tests for detection, pool recovery, and MCP feedback * fix(test): remove superfluous argument from LocalBackend constructor (#1402) LocalBackend has no constructor — the { registryPath } argument was ignored. * fix(lbug): address WAL recovery review feedback --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
59 lines
2 KiB
TypeScript
59 lines
2 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
import { createLbugDatabase, isWalCorruptionError } from '../../src/core/lbug/lbug-config.js';
|
|
|
|
describe('isWalCorruptionError', () => {
|
|
it.each([
|
|
[
|
|
'Corrupted wal file',
|
|
'Runtime exception: Corrupted wal file. Read out invalid WAL record type.',
|
|
],
|
|
['invalid WAL record', 'Error: invalid WAL record type'],
|
|
['WAL checksum', 'Checksum verification failed, the WAL file is corrupted.'],
|
|
['WAL + corrupt', 'the WAL file is corrupted'],
|
|
])('matches WAL corruption: %s', (_label, msg) => {
|
|
expect(isWalCorruptionError(msg)).toBe(true);
|
|
expect(isWalCorruptionError(new Error(msg))).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
['lock error', 'Could not set lock on file : /path/to/db'],
|
|
['generic', 'Query failed'],
|
|
['not found', 'LadybugDB not found at /path'],
|
|
['checksum without WAL', 'Checksum verification failed for parquet file'],
|
|
['permission path with WAL', "EACCES: permission denied '/path/to/wal'"],
|
|
['schema mismatch WAL', 'schema version mismatch in WAL'],
|
|
])('does not match non-WAL error: %s', (_label, msg) => {
|
|
expect(isWalCorruptionError(msg)).toBe(false);
|
|
});
|
|
|
|
it('handles non-string input', () => {
|
|
expect(isWalCorruptionError(undefined)).toBe(false);
|
|
expect(isWalCorruptionError(null)).toBe(false);
|
|
expect(isWalCorruptionError(42)).toBe(false);
|
|
expect(isWalCorruptionError(new Error('ok'))).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('createLbugDatabase WAL replay option', () => {
|
|
it('passes throwOnWalReplayFailure and checksum constructor args explicitly', () => {
|
|
const Database = vi.fn(function (this: any) {});
|
|
const lbugModule = { Database } as any;
|
|
|
|
createLbugDatabase(lbugModule, '/tmp/lbug', {
|
|
readOnly: true,
|
|
throwOnWalReplayFailure: false,
|
|
});
|
|
|
|
expect(Database).toHaveBeenCalledWith(
|
|
'/tmp/lbug',
|
|
0,
|
|
false,
|
|
true,
|
|
expect.any(Number),
|
|
true,
|
|
-1,
|
|
false,
|
|
true,
|
|
);
|
|
});
|
|
});
|