mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
fix(core): empty code files, multi-source cycle sync, root-level entity refs
Three community-PR takeovers rebased onto master: - import-file (#840): importCodeFile skips empty (0-byte) code files instead of writing a page with empty compiled_truth that fails every reindex-code pass. When a previously-imported file BECOMES empty, the stale page is deleted (chunks cascade) instead of ghosting in search — mirrors the markdown importer's empty-content branch. - cycle (#1079): runPhaseSync now enumerates every registered, non-archived, sync-enabled source with a local checkout instead of syncing only the single brainDir-matched source. Preserves the SyncLockBusyError per-source skip (#1794), keeps the legacy brainDir fallback when no source covers it, and respects explicit --source scoping (#1503) so a scoped cycle stays single-source. Single-target result shape is unchanged. - link-extraction (#1101): ENTITY_REF_RE gains a root-level branch so [Name](action-tracker.md) resolves to slug 'action-tracker'. The bare branch requires the .md suffix and excludes '#', '/', ':' so anchors, non-markdown assets, and URLs don't emit bogus refs; the capture's .md is stripped in extractEntityRefs and dir is '' (not the filename). LINK_EXTRACTOR_VERSION_TS bumped so stamped pages re-extract. Takeover of #840, #1079, #1101 (rebased; flaws named in review fixed). Co-authored-by: nightlaro <nightlaro@users.noreply.github.com> Co-authored-by: samvilian <samvilian@users.noreply.github.com> Co-authored-by: essexcanning <essexcanning@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
nightlaro
samvilian
essexcanning
Claude Fable 5
parent
0612b0daa8
commit
bccd97c75b
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Regression test for `importCodeFile` skipping empty (0-byte) code files (#840).
|
||||
*
|
||||
* Before this guard, an empty file produced a page row with empty
|
||||
* compiled_truth, which then failed every subsequent `gbrain reindex-code`
|
||||
* pass with `missing compiled_truth`. Empty files are legitimate in real
|
||||
* repos (stub/placeholder files committed during refactors); the importer
|
||||
* skips them the same way it skips files over MAX_FILE_SIZE — and when a
|
||||
* previously-imported file BECOMES empty, the stale page is deleted (chunks
|
||||
* cascade) instead of ghosting in search, mirroring the markdown importer's
|
||||
* empty-content branch.
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
||||
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
|
||||
import { importCodeFile } from '../src/core/import-file.ts';
|
||||
|
||||
let engine: PGLiteEngine;
|
||||
|
||||
beforeAll(async () => {
|
||||
engine = new PGLiteEngine();
|
||||
await engine.connect({});
|
||||
await engine.initSchema();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await engine.disconnect();
|
||||
});
|
||||
|
||||
describe('importCodeFile — empty file guard (#840)', () => {
|
||||
test('zero-byte content is skipped, not inserted', async () => {
|
||||
const result = await importCodeFile(engine, 'src/empty.ts', '', { noEmbed: true });
|
||||
|
||||
expect(result.status).toBe('skipped');
|
||||
expect(result.chunks).toBe(0);
|
||||
expect(result.error).toBe('empty code file');
|
||||
|
||||
// The skip must short-circuit before any page row is written, otherwise
|
||||
// reindex-code will still trip on the missing compiled_truth.
|
||||
const page = await engine.getPage(result.slug);
|
||||
expect(page).toBeNull();
|
||||
});
|
||||
|
||||
test('file that BECOMES empty deletes the stale page instead of ghosting', async () => {
|
||||
const first = await importCodeFile(engine, 'src/becomes-empty.ts', 'export const x = 1;\n', { noEmbed: true });
|
||||
expect(first.status).toBe('imported');
|
||||
expect(await engine.getPage(first.slug)).not.toBeNull();
|
||||
|
||||
const second = await importCodeFile(engine, 'src/becomes-empty.ts', '', { noEmbed: true });
|
||||
expect(second.status).toBe('skipped');
|
||||
expect(second.error).toBe('empty code file');
|
||||
|
||||
// Stale page (and its chunks, via FK cascade) must be gone.
|
||||
expect(await engine.getPage(first.slug)).toBeNull();
|
||||
expect(await engine.getChunks(first.slug)).toEqual([]);
|
||||
});
|
||||
|
||||
test('non-empty content still imports normally', async () => {
|
||||
const result = await importCodeFile(engine, 'src/non-empty.ts', 'export const x = 1;\n', { noEmbed: true });
|
||||
|
||||
expect(result.status).toBe('imported');
|
||||
expect(result.chunks).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user