mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-10 03:27:59 +00:00
test(recovery): verify native replay preserves retained bytes (#3463)
Exercise successful recovery, malformed WAL rejection, and interrupted-checkpoint replay against native LadybugDB while asserting that the original source family remains unchanged.
This commit is contained in:
parent
f5673418a0
commit
6d6807e775
1 changed files with 45 additions and 0 deletions
|
|
@ -46,6 +46,17 @@ describe('isolated native staged embedding recovery', () => {
|
|||
else expect(result.status, result.stderr).toBe(0);
|
||||
}
|
||||
|
||||
function snapshotSourceFamily(dbPath: string) {
|
||||
const basename = path.basename(dbPath);
|
||||
return Object.fromEntries(
|
||||
fs
|
||||
.readdirSync(path.dirname(dbPath))
|
||||
.filter((name) => name === basename || name.startsWith(`${basename}.`))
|
||||
.sort()
|
||||
.map((name) => [name, fs.readFileSync(path.join(path.dirname(dbPath), name))]),
|
||||
);
|
||||
}
|
||||
|
||||
it('streams complete same-hash groups and rejects malformed whole nodes', async () => {
|
||||
const dbPath = stagePath();
|
||||
seed(dbPath);
|
||||
|
|
@ -75,6 +86,7 @@ describe('isolated native staged embedding recovery', () => {
|
|||
it('replays a hard-killed native writer strictly and excludes the incomplete active window', async () => {
|
||||
const dbPath = stagePath();
|
||||
seed(dbPath, 'hard-kill');
|
||||
const sourceBefore = snapshotSourceFamily(dbPath);
|
||||
recovered = await recoverStagedEmbeddings(dbPath, {
|
||||
dimensions: 2,
|
||||
excludedNodeIds: ['unsafe-prefix', 'other'],
|
||||
|
|
@ -86,6 +98,7 @@ describe('isolated native staged embedding recovery', () => {
|
|||
.map((row) => row.chunkIndex)
|
||||
.sort(),
|
||||
).toEqual([0, 1]);
|
||||
expect(snapshotSourceFamily(dbPath)).toEqual(sourceBefore);
|
||||
}, 30_000);
|
||||
|
||||
it('rejects vectors from a different dimension instead of coercing them', async () => {
|
||||
|
|
@ -95,6 +108,34 @@ describe('isolated native staged embedding recovery', () => {
|
|||
expect(recovered.rows).toEqual([]);
|
||||
}, 30_000);
|
||||
|
||||
it('strictly replays an interrupted checkpoint copy with both checkpoint locks retained', async (ctx) => {
|
||||
const version = JSON.parse(
|
||||
fs.readFileSync(
|
||||
new URL('../../node_modules/@ladybugdb/core/package.json', import.meta.url),
|
||||
'utf8',
|
||||
),
|
||||
).version as string;
|
||||
const [major, minor] = version.split('.').map(Number);
|
||||
// Older pins do not support the deterministic interrupted-checkpoint plant.
|
||||
if (Number.isFinite(major) && Number.isFinite(minor) && major === 0 && minor < 19) ctx.skip();
|
||||
const dbPath = stagePath();
|
||||
seed(dbPath, 'interrupted-checkpoint');
|
||||
const sourceBefore = snapshotSourceFamily(dbPath);
|
||||
expect(fs.statSync(`${dbPath}.wal.checkpoint`).size).toBeGreaterThan(0);
|
||||
expect(fs.existsSync(`${dbPath}.checkpoint.intent.lock`)).toBe(true);
|
||||
expect(fs.existsSync(`${dbPath}.checkpoint.apply.lock`)).toBe(true);
|
||||
|
||||
recovered = await recoverStagedEmbeddings(dbPath, { dimensions: 2 });
|
||||
|
||||
expect([...recovered.embeddingNodeIds].sort()).toEqual([
|
||||
'checkpoint-only',
|
||||
'complete',
|
||||
'other',
|
||||
]);
|
||||
expect(recovered.rows).toHaveLength(4);
|
||||
expect(snapshotSourceFamily(dbPath)).toEqual(sourceBefore);
|
||||
}, 30_000);
|
||||
|
||||
it('contains a malformed native source in a subprocess and preserves it', async () => {
|
||||
const dbPath = stagePath();
|
||||
fs.writeFileSync(dbPath, 'not a ladybug database');
|
||||
|
|
@ -109,6 +150,7 @@ describe('isolated native staged embedding recovery', () => {
|
|||
seed(dbPath, 'hard-kill');
|
||||
const walPath = `${dbPath}.wal`;
|
||||
fs.writeFileSync(walPath, Buffer.alloc(128, 0xff));
|
||||
const sourceBefore = snapshotSourceFamily(dbPath);
|
||||
await expect(recoverStagedEmbeddings(dbPath, { dimensions: 2 })).rejects.toThrow(
|
||||
/extraction failed/,
|
||||
);
|
||||
|
|
@ -116,6 +158,7 @@ describe('isolated native staged embedding recovery', () => {
|
|||
expect(fs.readdirSync(path.dirname(dbPath)).some((name) => /bad|quarantine/i.test(name))).toBe(
|
||||
false,
|
||||
);
|
||||
expect(snapshotSourceFamily(dbPath)).toEqual(sourceBefore);
|
||||
}, 30_000);
|
||||
|
||||
it('does not create a missing source and refuses a symlink', async () => {
|
||||
|
|
@ -133,10 +176,12 @@ describe('isolated native staged embedding recovery', () => {
|
|||
it('can kill a timed out native subprocess without aborting analyze', async () => {
|
||||
const dbPath = stagePath();
|
||||
seed(dbPath);
|
||||
const sourceBefore = snapshotSourceFamily(dbPath);
|
||||
await expect(recoverStagedEmbeddings(dbPath, { dimensions: 2, timeoutMs: 1 })).rejects.toThrow(
|
||||
/timeout/,
|
||||
);
|
||||
expect(fs.existsSync(dbPath)).toBe(true);
|
||||
expect(snapshotSourceFamily(dbPath)).toEqual(sourceBefore);
|
||||
}, 30_000);
|
||||
|
||||
it('loads the source child when analyze runs in another repository directory', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue