fix(pages): restore soft-deleted rows on putPage (#2779)

This commit is contained in:
Ziyang Guo
2026-07-23 11:25:00 -07:00
committed by GitHub
parent e0d2cbf353
commit 7bbd087cb7
4 changed files with 48 additions and 0 deletions
+1
View File
@@ -1048,6 +1048,7 @@ export class PGLiteEngine implements BrainEngine {
frontmatter = EXCLUDED.frontmatter,
content_hash = EXCLUDED.content_hash,
updated_at = now(),
deleted_at = NULL,
effective_date = COALESCE(EXCLUDED.effective_date, pages.effective_date),
effective_date_source = COALESCE(EXCLUDED.effective_date_source, pages.effective_date_source),
import_filename = COALESCE(EXCLUDED.import_filename, pages.import_filename),
+1
View File
@@ -1110,6 +1110,7 @@ export class PostgresEngine implements BrainEngine {
frontmatter = EXCLUDED.frontmatter,
content_hash = EXCLUDED.content_hash,
updated_at = now(),
deleted_at = NULL,
effective_date = COALESCE(EXCLUDED.effective_date, pages.effective_date),
effective_date_source = COALESCE(EXCLUDED.effective_date_source, pages.effective_date_source),
import_filename = COALESCE(EXCLUDED.import_filename, pages.import_filename),
+22
View File
@@ -371,6 +371,28 @@ describeBoth('Engine parity — Postgres vs PGLite', () => {
expect(pglitePage!.title).toBe('V2');
});
test('putPage restores soft-deleted rows on both engines', async () => {
const slug = 'notes/put-page-restore-parity';
for (const engine of [pgEngine, pgliteEngine]) {
await engine.putPage(slug, {
type: 'note',
title: 'Before delete',
compiled_truth: 'before',
timeline: '',
});
await engine.softDeletePage(slug, { sourceId: 'default' });
expect(await engine.getPage(slug, { sourceId: 'default' })).toBeNull();
await engine.putPage(slug, {
type: 'note',
title: 'After restore',
compiled_truth: 'after',
timeline: '',
});
expect((await engine.getPage(slug, { sourceId: 'default' }))?.title).toBe('After restore');
}
});
test('v0.41.19.0 deletePages parity: both engines return same confirmed-deleted slugs', async () => {
const realSlugs = ['wiki/dpp-1', 'wiki/dpp-2', 'wiki/dpp-3'];
for (const slug of realSlugs) {
+24
View File
@@ -88,6 +88,30 @@ describe('PGLiteEngine: Pages', () => {
expect(matches.length).toBe(1);
});
test('putPage restores a soft-deleted page', async () => {
const slug = 'notes/restore-on-put';
await engine.putPage(slug, testPage);
await engine.upsertChunks(slug, [{
chunk_index: 0,
chunk_text: 'restored visibility marker',
chunk_source: 'compiled_truth',
token_count: 3,
}]);
await engine.softDeletePage(slug, { sourceId: 'default' });
expect(await engine.getPage(slug)).toBeNull();
expect((await engine.searchKeyword('restored visibility marker')).map(result => result.slug)).not.toContain(slug);
const restored = await engine.putPage(slug, {
...testPage,
title: 'Restored Title',
compiled_truth: 'restored visibility marker',
});
expect(restored.title).toBe('Restored Title');
expect((await engine.getPage(slug))?.title).toBe('Restored Title');
expect((await engine.searchKeyword('restored visibility marker')).map(result => result.slug)).toContain(slug);
});
test('getPage returns null for missing slug', async () => {
const result = await engine.getPage('nonexistent/slug');
expect(result).toBeNull();