package graphsync import ( "context" "errors" "os" "path/filepath" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/mathiasbq/hyperguild/ingestion/internal/graph" ) // fakeStore captures the calls IndexDoc / BackfillFromBrainDir made. type fakeStore struct { mu sync.Mutex upserts []graph.Entity replaces map[string][]graph.Edge deletes []string failOn string // upsert fails when entity slug == failOn } func newFakeStore() *fakeStore { return &fakeStore{replaces: make(map[string][]graph.Edge)} } func (f *fakeStore) UpsertEntity(_ context.Context, e graph.Entity) error { f.mu.Lock() defer f.mu.Unlock() if f.failOn != "" && e.Slug == f.failOn { return errors.New("synthetic failure") } f.upserts = append(f.upserts, e) return nil } func (f *fakeStore) ReplaceEdgesForDoc(_ context.Context, docPath string, edges []graph.Edge) error { f.mu.Lock() defer f.mu.Unlock() f.replaces[docPath] = append([]graph.Edge(nil), edges...) return nil } func (f *fakeStore) DeleteByDoc(_ context.Context, docPath string) error { f.mu.Lock() defer f.mu.Unlock() f.deletes = append(f.deletes, docPath) return nil } func writeBrain(t *testing.T, brainDir, relPath, body string) { t.Helper() full := filepath.Join(brainDir, filepath.FromSlash(relPath)) require.NoError(t, os.MkdirAll(filepath.Dir(full), 0o755)) require.NoError(t, os.WriteFile(full, []byte(body), 0o644)) } func TestIndexDoc_UpsertsEntityAndEdges(t *testing.T) { tmp := t.TempDir() writeBrain(t, tmp, "wiki/concepts/foo.md", `--- title: Foo --- # Foo Linking to [[bar]] and [[baz|Baz]]. `) fs := newFakeStore() require.NoError(t, IndexDoc(context.Background(), fs, tmp, "wiki/concepts/foo.md")) require.Len(t, fs.upserts, 1) assert.Equal(t, "foo", fs.upserts[0].Slug) assert.Equal(t, "concept", fs.upserts[0].Type) edges := fs.replaces["wiki/concepts/foo.md"] require.Len(t, edges, 2) assert.Equal(t, "bar", edges[0].DstSlug) assert.Equal(t, "baz", edges[1].DstSlug) } func TestIndexDoc_NoopOnNilStore(t *testing.T) { require.NoError(t, IndexDoc(context.Background(), nil, "anywhere", "foo.md")) } func TestIndexDoc_NoopOnEmptyRelPath(t *testing.T) { fs := newFakeStore() require.NoError(t, IndexDoc(context.Background(), fs, "anywhere", "")) assert.Empty(t, fs.upserts) } func TestIndexDoc_ErrorsOnMissingFile(t *testing.T) { fs := newFakeStore() err := IndexDoc(context.Background(), fs, t.TempDir(), "wiki/nope.md") require.Error(t, err) } func TestIndexDoc_SurfacesStoreFailure(t *testing.T) { tmp := t.TempDir() writeBrain(t, tmp, "wiki/concepts/boom.md", "# Boom\n") fs := newFakeStore() fs.failOn = "boom" err := IndexDoc(context.Background(), fs, tmp, "wiki/concepts/boom.md") require.Error(t, err) } func TestBackfillFromBrainDir_WalksWikiAndKnowledge(t *testing.T) { tmp := t.TempDir() writeBrain(t, tmp, "wiki/concepts/foo.md", "# Foo\n[[bar]]\n") writeBrain(t, tmp, "wiki/entities/bar.md", "# Bar\n") writeBrain(t, tmp, "knowledge/legacy.md", "# Legacy [[foo]]\n") // non-markdown file should be skipped writeBrain(t, tmp, "wiki/concepts/skip.txt", "ignore me") fs := newFakeStore() n, err := BackfillFromBrainDir(context.Background(), fs, tmp) require.NoError(t, err) assert.Equal(t, 3, n) assert.Len(t, fs.upserts, 3) } func TestBackfillFromBrainDir_TolerantOfMissingDirs(t *testing.T) { tmp := t.TempDir() fs := newFakeStore() n, err := BackfillFromBrainDir(context.Background(), fs, tmp) require.NoError(t, err) assert.Equal(t, 0, n) } func TestBackfillFromBrainDir_NilStoreNoop(t *testing.T) { n, err := BackfillFromBrainDir(context.Background(), nil, t.TempDir()) require.NoError(t, err) assert.Equal(t, 0, n) }