mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 06:23:01 +00:00
Closes #345. The bulk-import walker isCollectibleForWalker filtered admitted files by extension only, while incremental sync excludes README/index/log/schema via isSyncable -> SYNC_SKIP_FILES. A directory import therefore ingested every directory README as a folder-titled ghost page that trigram-corrupts fuzzy entity resolution and inflates orphan count. Apply SYNC_SKIP_FILES (basename guard) at the top of isCollectibleForWalker so both the FS-walk and the git-fast-path collection routes agree with sync. Also add RESOLVER.md to SYNC_SKIP_FILES: a structural routing metafile (docs-aligned with schema.md/index.md/log.md/README.md), not indexable content. Co-authored-by: ElliotDrel <ElliotDrel@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
3.1 KiB
TypeScript
65 lines
3.1 KiB
TypeScript
/**
|
|
* v0.41.13 (#1433) — isSyncable / unsyncableReason classifier shape.
|
|
*
|
|
* The two public APIs route through the same internal `classifySync`
|
|
* helper so they cannot drift. These tests pin the contract that
|
|
* `isSyncable` returns true iff `unsyncableReason` returns null, AND
|
|
* pin the canonical SYNC_SKIP_FILES list (since the cleanup-loop guard
|
|
* at commands/sync.ts:772 keys on `unsyncableReason === 'metafile'`).
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import {
|
|
isSyncable,
|
|
unsyncableReason,
|
|
SYNC_SKIP_FILES,
|
|
type SyncableReason,
|
|
} from '../src/core/sync.ts';
|
|
|
|
describe('#1433 — isSyncable / unsyncableReason are duals of one classifier', () => {
|
|
const cases: Array<{ path: string; expected: SyncableReason | null; note: string }> = [
|
|
{ path: 'people/alice.md', expected: null, note: 'normal markdown page' },
|
|
{ path: 'docs/guide.mdx', expected: null, note: 'mdx accepted by markdown strategy' },
|
|
{ path: 'learning-and-strategy/log.md', expected: 'metafile', note: 'log.md anywhere is metafile' },
|
|
{ path: 'wiki/schema.md', expected: 'metafile', note: 'schema.md anywhere is metafile' },
|
|
{ path: 'index.md', expected: 'metafile', note: 'top-level index.md' },
|
|
{ path: 'README.md', expected: 'metafile', note: 'top-level README' },
|
|
{ path: 'docs/README.md', expected: 'metafile', note: 'nested README' },
|
|
{ path: 'RESOLVER.md', expected: 'metafile', note: 'top-level master routing config (closes #345)' },
|
|
{ path: 'brain/RESOLVER.md', expected: 'metafile', note: 'RESOLVER.md anywhere is metafile (closes #345)' },
|
|
{ path: 'people/alice.txt', expected: 'strategy', note: '.txt rejected by markdown strategy' },
|
|
{ path: 'ops/scratch/note.md', expected: 'pruned-dir', note: 'ops/ is pruned' },
|
|
{ path: '.git/notes.md', expected: 'pruned-dir', note: 'hidden dir pruned' },
|
|
{ path: 'node_modules/foo/README.md', expected: 'pruned-dir', note: 'node_modules pruned' },
|
|
];
|
|
|
|
for (const c of cases) {
|
|
test(`${c.path} → ${c.expected ?? 'syncable'} (${c.note})`, () => {
|
|
const reason = unsyncableReason(c.path);
|
|
const sync = isSyncable(c.path);
|
|
expect(reason).toBe(c.expected);
|
|
expect(sync).toBe(c.expected === null);
|
|
});
|
|
}
|
|
|
|
test('include glob: path not matching include returns include-glob-miss', () => {
|
|
expect(unsyncableReason('docs/guide.md', { include: ['people/**'] })).toBe('include-glob-miss');
|
|
expect(isSyncable('docs/guide.md', { include: ['people/**'] })).toBe(false);
|
|
});
|
|
|
|
test('exclude glob: matching path returns exclude-glob-hit', () => {
|
|
expect(unsyncableReason('drafts/wip.md', { exclude: ['drafts/**'] })).toBe('exclude-glob-hit');
|
|
expect(isSyncable('drafts/wip.md', { exclude: ['drafts/**'] })).toBe(false);
|
|
});
|
|
|
|
test('SYNC_SKIP_FILES export contains the canonical structural metafiles', () => {
|
|
expect([...SYNC_SKIP_FILES]).toEqual(['schema.md', 'index.md', 'log.md', 'README.md', 'RESOLVER.md']);
|
|
});
|
|
|
|
test('isSyncable(p) === (unsyncableReason(p) === null) — duality holds for all canonical cases', () => {
|
|
for (const c of cases) {
|
|
expect(isSyncable(c.path)).toBe(unsyncableReason(c.path) === null);
|
|
}
|
|
});
|
|
});
|