fix(git-sync): a malformed non-UUID gitmost_id no longer wedges a space's sync (C9-D1)
A vault file with a broken/hand-edited `gitmost_id` frontmatter (e.g. `gitmost_id: [unclosed` or a non-uuid token) fed that value into a Postgres `uuid` predicate (page update/delete), throwing 22P02 "invalid input syntax for type uuid". The push apply recorded it as a per-cycle failure that never cleared — refs never advance when failures>0, so the WHOLE space's sync looped on the same failure indefinitely and no further legitimate change synced (found via web-test). Wrap the id-scoped write ops (import/delete/move/rename/restore) at the bind() seam: swallow exactly the 22P02 as an inert no-op so the cycle succeeds and the rest of the space keeps syncing; re-throw anything else. pageId is the only user-influenced uuid in these ops, so a 22P02 there unambiguously means it. Verified on the stand: pushing a non-UUID gitmost_id now logs a skip warn and the space stays at 0 failures (was 1 failure/cycle forever); a concurrent legit edit to another page still syncs. Unit tests: import/delete swallow 22P02, non-22P02 re-throws. Full server suite green (2145). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -418,6 +418,45 @@ describe('GitmostDataSourceService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Bug C9-D1: a vault file with a malformed (non-UUID) `gitmost_id` frontmatter
|
||||
// makes the id reach a Postgres `uuid` predicate, which throws error code
|
||||
// '22P02'. Left unhandled the push apply records it as a per-cycle failure that
|
||||
// never clears -> the whole space's sync loops forever. The bind() seam wraps the
|
||||
// id-scoped writes so exactly that error is swallowed as an inert no-op.
|
||||
describe('malformed-id guard (bug C9-D1: non-UUID gitmost_id must not wedge sync)', () => {
|
||||
const pgInvalidUuid = Object.assign(
|
||||
new Error('invalid input syntax for type uuid: "not-a-uuid"'),
|
||||
{ code: '22P02' },
|
||||
);
|
||||
|
||||
it('importPageMarkdown swallows a 22P02 and does NOT write the body', async () => {
|
||||
const { service, mocks } = build();
|
||||
mocks.pageRepo.findById.mockRejectedValue(pgInvalidUuid);
|
||||
const res = await service
|
||||
.bind(CTX)
|
||||
.importPageMarkdown('not-a-uuid', '# x');
|
||||
expect(res).toEqual({}); // inert no-op, no throw
|
||||
expect(mocks.collabGateway.writePageBody).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('deletePage swallows the 22P02 thrown by the uuid predicate (no wedge)', async () => {
|
||||
const { service, mocks } = build();
|
||||
// The malformed id reaches removePage's `uuid` predicate, which throws 22P02.
|
||||
mocks.pageService.removePage.mockRejectedValue(pgInvalidUuid);
|
||||
await expect(
|
||||
service.bind(CTX).deletePage('not-a-uuid'),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('re-throws a NON-22P02 error (does not mask real failures)', async () => {
|
||||
const { service, mocks } = build();
|
||||
mocks.pageRepo.findById.mockRejectedValue(new Error('db down'));
|
||||
await expect(
|
||||
service.bind(CTX).importPageMarkdown('not-a-uuid', '# x'),
|
||||
).rejects.toThrow('db down');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createPage', () => {
|
||||
it('creates the shell with git-sync provenance, writes body, returns id', async () => {
|
||||
const { service, mocks } = build();
|
||||
|
||||
Reference in New Issue
Block a user