fix(graph,wiki): read the brain through brainfs (#157, slice 4)
graphsync.IndexDoc/BackfillFromBrainDir and wiki.LoadInventory/ RebuildIndex read notes by path (os.ReadFile, os.Open, os.ReadDir + open). A symlinked note put the target's title, aliases and wikilinks into the knowledge graph, the wiki inventory (and the LLM prompts built from it) and wiki/index.md; a FIFO hung each read. - graphsync: IndexDoc and the backfill read through brainfs; the backfill walks with WalkRegular, skips a missing root and aborts on any other walk error, including a symlinked root. - wiki: LoadInventory lists type dirs with the new brainfs.ReadDirRegular (every component a real dir, regular files only) and reads frontmatter through the root; a symlinked type dir is an error. RebuildIndex reads first sentences through the root. - brainfstest's outside file now carries the sentinel in frontmatter title and aliases, a heading and a wikilink, so extractors that ignore the body are tested too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package graphsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs/brainfstest"
|
||||
)
|
||||
|
||||
func (f *fakeStore) dump() string {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return fmt.Sprintf("%+v %+v", f.upserts, f.replaces)
|
||||
}
|
||||
|
||||
// hyperguild#157 slice 4: IndexDoc and BackfillFromBrainDir read notes with
|
||||
// os.ReadFile, so a symlinked note put the target's title and wikilinks into
|
||||
// the graph, and a FIFO hung the read.
|
||||
func TestIndexDocRefusesSymlinksAndSurvivesSwapRaces(t *testing.T) {
|
||||
brain := t.TempDir()
|
||||
facts := filepath.Join(brain, "wiki", "w", "facts")
|
||||
require.NoError(t, os.MkdirAll(facts, 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: zqinside\n---\nbody [[other]]\n"), 0o644))
|
||||
require.NoError(t, os.Symlink(brainfstest.Outside(t), filepath.Join(facts, "leak.md")))
|
||||
|
||||
store := newFakeStore()
|
||||
require.NoError(t, IndexDoc(context.Background(), store, brain, "wiki/w/facts/ok.md"))
|
||||
assert.Contains(t, store.dump(), "zqinside", "positive control: a regular note is indexed")
|
||||
assert.Error(t, IndexDoc(context.Background(), store, brain, "wiki/w/facts/leak.md"))
|
||||
assert.NotContains(t, store.dump(), brainfstest.Sentinel)
|
||||
|
||||
p := filepath.Join(facts, "r.md")
|
||||
read := func() string {
|
||||
s := newFakeStore()
|
||||
_ = IndexDoc(context.Background(), s, brain, "wiki/w/facts/r.md")
|
||||
return s.dump()
|
||||
}
|
||||
brainfstest.AssertNoLeak(t, p, "---\ntitle: zqinside\n---\n", 1500*time.Millisecond, read)
|
||||
brainfstest.AssertNoHang(t, p, "---\ntitle: zqinside\n---\n", 1500*time.Millisecond, 6*time.Second, func() { _ = read() })
|
||||
}
|
||||
|
||||
func TestBackfillSkipsSymlinkedNotesAndASymlinkedRoot(t *testing.T) {
|
||||
brain := t.TempDir()
|
||||
facts := filepath.Join(brain, "wiki", "w", "facts")
|
||||
require.NoError(t, os.MkdirAll(facts, 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: zqinside\n---\n"), 0o644))
|
||||
require.NoError(t, os.Symlink(brainfstest.Outside(t), filepath.Join(facts, "leak.md")))
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(brain, "raw"), 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(brain, "raw", "r.md"), []byte("---\ntitle: zqrawonly\n---\n"), 0o644))
|
||||
require.NoError(t, os.Symlink(".", filepath.Join(brain, "knowledge")))
|
||||
|
||||
store := newFakeStore()
|
||||
_, err := BackfillFromBrainDir(context.Background(), store, brain)
|
||||
assert.Error(t, err, "a symlinked root aborts the backfill")
|
||||
assert.NotContains(t, store.dump(), brainfstest.Sentinel)
|
||||
assert.NotContains(t, store.dump(), "zqrawonly")
|
||||
|
||||
require.NoError(t, os.Remove(filepath.Join(brain, "knowledge")))
|
||||
store = newFakeStore()
|
||||
n, err := BackfillFromBrainDir(context.Background(), store, brain)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, n, "only the regular note is indexed")
|
||||
assert.Contains(t, store.dump(), "zqinside")
|
||||
assert.NotContains(t, store.dump(), brainfstest.Sentinel)
|
||||
}
|
||||
Reference in New Issue
Block a user