Files
waggle-os/packages/hive-mind-core/tests/mind/content-hash-dedup.test.ts
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

94 lines
4.3 KiB
TypeScript

import { describe, it, expect, afterEach } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { MindDB } from '../../src/mind/db.js';
import { FrameStore } from '../../src/mind/frames.js';
import { SessionStore } from '../../src/mind/sessions.js';
import { hashFrameContent, stripHmPrefix } from '../../src/mind/content-hash.js';
/**
* oss-drift D3 — indexed content_hash dedup with MONO semantics
* (stripHmPrefix + trim). The old findDuplicate scanned only the last 500
* frames; the indexed lookup has NO recency window. Backfill covers rows
* written before the column existed.
*/
describe('D3 — content-hash dedup (indexed, unbounded)', () => {
const cleanups: Array<() => void> = [];
afterEach(() => { while (cleanups.length) cleanups.pop()!(); });
function freshMind(): { db: MindDB; frames: FrameStore; gopId: string } {
const db = new MindDB(':memory:');
cleanups.push(() => db.close());
const frames = new FrameStore(db);
const gopId = new SessionStore(db).create().gop_id;
return { db, frames, gopId };
}
it('hashFrameContent is stripHmPrefix-aware and trim-stable', () => {
expect(hashFrameContent(' body text \n')).toBe(hashFrameContent('body text'));
expect(hashFrameContent('[hm session:x src:claude-code event:stop] body text'))
.toBe(hashFrameContent('body text'));
expect(stripHmPrefix('[hm src:a] hello')).toBe('hello');
});
it('dedups beyond the old 500-frame recency window', () => {
const { frames, gopId } = freshMind();
const first = frames.createIFrame(gopId, 'the very first unique frame body', 'normal', 'system');
// bury it under 550 distinct frames (old implementation would miss it)
for (let i = 0; i < 550; i++) {
frames.createIFrame(gopId, `filler frame number ${i}`, 'normal', 'system');
}
const dup = frames.createIFrame(gopId, 'the very first unique frame body', 'normal', 'system');
expect(dup.id).toBe(first.id); // dedup hit, no new row
});
it('provenance-insensitive dedup still holds (OQ-6 regression)', () => {
const { frames, gopId } = freshMind();
const a = frames.createIFrame(gopId, '[hm session:s1 src:openclaw event:stop] same turn body', 'normal', 'system');
const b = frames.createIFrame(gopId, '[hm session:s2 src:claude-code event:stop] same turn body', 'normal', 'system');
expect(b.id).toBe(a.id);
});
it('content_hash is maintained on insert, update, and stays consistent', () => {
const { db, frames, gopId } = freshMind();
const f = frames.createIFrame(gopId, 'original content', 'normal', 'system');
const raw = db.getDatabase();
const row = (): { content_hash: string } =>
raw.prepare('SELECT content_hash FROM memory_frames WHERE id = ?').get(f.id) as { content_hash: string };
expect(row().content_hash).toBe(hashFrameContent('original content'));
frames.update(f.id, 'updated content');
expect(row().content_hash).toBe(hashFrameContent('updated content'));
// the updated frame is now findable as a duplicate of the NEW content
expect(frames.findDuplicate('updated content')?.id).toBe(f.id);
expect(frames.findDuplicate('original content')).toBeNull();
});
it('migration backfills NULL hashes from rows written by raw SQL', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-d3-'));
cleanups.push(() => fs.rmSync(dir, { recursive: true, force: true }));
const file = path.join(dir, 'test.mind');
const db1 = new MindDB(file);
new SessionStore(db1).ensure('raw-sess', 'system', 'raw');
// simulate a pre-column writer: insert WITHOUT content_hash
db1.getDatabase().prepare(
`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('I', 'raw-sess', 0, 'legacy row body', 'normal')`
).run();
db1.close();
const db2 = new MindDB(file); // runMigrations → backfill
cleanups.push(() => db2.close());
const row = db2.getDatabase().prepare(
`SELECT content_hash FROM memory_frames WHERE content = 'legacy row body'`
).get() as { content_hash: string | null };
expect(row.content_hash).toBe(hashFrameContent('legacy row body'));
// and the legacy row now participates in dedup
const frames2 = new FrameStore(db2);
expect(frames2.findDuplicate('legacy row body')).not.toBeNull();
});
});