mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
195328f7a6
* Persist saved annotate file edits in drafts * Handle stale saved file edit context * Test source edit conflict actions * Return source metadata for single-file docs * Fix saved file edit conflict races * Handle deleted source files in annotate edits * Tighten annotate missing-file recovery * Reset edit state for missing file reopen * Tighten source edit restore path handling * Harden source edit recovery paths * Harden source edit disk reconciliation * Fix live file tree startup delay * Document file tree watcher startup ordering * Preserve source save through missing files and symlinks * Harden missing source file recovery * Tighten annotate source edit boundaries
106 lines
3.0 KiB
TypeScript
106 lines
3.0 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test';
|
|
import { fetchSourceDocumentSnapshot, probeSourceSave } from './sourceDocumentClient';
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
function mockFetch(response: Response | Error) {
|
|
globalThis.fetch = (async () => {
|
|
if (response instanceof Error) throw response;
|
|
return response;
|
|
}) as typeof fetch;
|
|
}
|
|
|
|
afterEach(() => {
|
|
globalThis.fetch = originalFetch;
|
|
});
|
|
|
|
describe('source document client', () => {
|
|
test('probes source-save metadata from /api/doc', async () => {
|
|
mockFetch(Response.json({
|
|
sourceSave: {
|
|
enabled: true,
|
|
kind: 'local-text-file',
|
|
scope: 'folder-file',
|
|
path: '/repo/docs/a.md',
|
|
basename: 'a.md',
|
|
language: 'markdown',
|
|
hash: 'sha256:after',
|
|
mtimeMs: 1000,
|
|
size: 6,
|
|
eol: 'lf',
|
|
},
|
|
}));
|
|
|
|
expect(await probeSourceSave('/repo/docs/a.md')).toEqual({
|
|
status: 'ok',
|
|
sourceSave: {
|
|
enabled: true,
|
|
kind: 'local-text-file',
|
|
scope: 'folder-file',
|
|
path: '/repo/docs/a.md',
|
|
basename: 'a.md',
|
|
language: 'markdown',
|
|
hash: 'sha256:after',
|
|
mtimeMs: 1000,
|
|
size: 6,
|
|
eol: 'lf',
|
|
},
|
|
});
|
|
});
|
|
|
|
test('distinguishes missing and unavailable source probes', async () => {
|
|
mockFetch(new Response('missing', { status: 404 }));
|
|
expect(await probeSourceSave('/repo/docs/missing.md')).toEqual({ status: 'missing' });
|
|
|
|
mockFetch(new Error('network'));
|
|
expect(await probeSourceSave('/repo/docs/a.md')).toEqual({ status: 'unavailable' });
|
|
});
|
|
|
|
test('fetches markdown source snapshots and rejects html documents', async () => {
|
|
mockFetch(Response.json({
|
|
markdown: 'after\n',
|
|
sourceSave: {
|
|
enabled: true,
|
|
kind: 'local-text-file',
|
|
scope: 'folder-file',
|
|
path: '/repo/docs/a.md',
|
|
basename: 'a.md',
|
|
language: 'markdown',
|
|
hash: 'sha256:after',
|
|
mtimeMs: 1000,
|
|
size: 6,
|
|
eol: 'lf',
|
|
},
|
|
}));
|
|
expect(await fetchSourceDocumentSnapshot('/repo/docs/a.md')).toEqual({
|
|
status: 'ok',
|
|
snapshot: {
|
|
markdown: 'after\n',
|
|
sourceSave: {
|
|
enabled: true,
|
|
kind: 'local-text-file',
|
|
scope: 'folder-file',
|
|
path: '/repo/docs/a.md',
|
|
basename: 'a.md',
|
|
language: 'markdown',
|
|
hash: 'sha256:after',
|
|
mtimeMs: 1000,
|
|
size: 6,
|
|
eol: 'lf',
|
|
},
|
|
},
|
|
});
|
|
|
|
mockFetch(Response.json({ markdown: '<p>after</p>', renderAs: 'html' }));
|
|
expect(await fetchSourceDocumentSnapshot('/repo/docs/a.html')).toEqual({ status: 'unavailable' });
|
|
});
|
|
|
|
test('distinguishes missing and unavailable source snapshots', async () => {
|
|
mockFetch(new Response('missing', { status: 404 }));
|
|
expect(await fetchSourceDocumentSnapshot('/repo/docs/missing.md')).toEqual({ status: 'missing' });
|
|
|
|
mockFetch(new Error('network'));
|
|
expect(await fetchSourceDocumentSnapshot('/repo/docs/a.md')).toEqual({ status: 'unavailable' });
|
|
});
|
|
});
|