Files
hyperguild/ingestion/internal/brainstore/store_test.go
mathiasandClaude Opus 4.8 0ac165cca3 feat(brainstore): shared BrainStore impl; re-point MCP handlers (#51)
Extracts the #45 write/update/get logic + the wiki upkeep that must
accompany a write (wing _index rebuild, cross-wing auto-tunnel, graph
re-index) into a single concrete brainstore.Store implementing
capture.BrainStore. The MCP brain_write/brain_update/brain_get handlers
are re-pointed at it, so there is one implementation, not two — the DRY
payoff #51 is named for. capture and MCP now share the exact same brain
write path and read-after-write contract.

The Server gains a *brainstore.Store, constructed in NewServer and given
the graph store in WithGraph. Embedding refresh stays out-of-band
(mtime-driven vectorstore.Sync), unchanged. Existing MCP brain_update/
brain_get/brain_write tests pass unmodified — behaviour and the
{id, path, content_hash} response contract are preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:21:43 +02:00

86 lines
2.6 KiB
Go

package brainstore_test
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/mathiasbq/hyperguild/ingestion/internal/brainstore"
"github.com/mathiasbq/hyperguild/ingestion/internal/capture"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStoreWriteReturnsHandle(t *testing.T) {
dir := t.TempDir()
s := brainstore.New(dir)
ref, err := s.Write(context.Background(), capture.Note{
Content: "# X\n\nbody\n", Filename: "x", Wing: "a", Hall: "facts",
})
require.NoError(t, err)
assert.Equal(t, "wiki/a/facts/x.md", ref.Path)
assert.Equal(t, ref.Path, ref.ID)
assert.NotEmpty(t, ref.ContentHash)
assert.False(t, ref.Superseded)
_, err = os.Stat(filepath.Join(dir, "wiki/a/facts/x.md"))
require.NoError(t, err)
}
func TestStoreUpdateSupersedes(t *testing.T) {
dir := t.TempDir()
s := brainstore.New(dir)
_, err := s.Write(context.Background(), capture.Note{
Content: "old\n", Filename: "n", Wing: "a", Hall: "facts",
})
require.NoError(t, err)
ref, err := s.Update(context.Background(), "n", capture.Note{
Content: "new\n", Wing: "a", Hall: "facts", Reason: "changed",
})
require.NoError(t, err)
assert.True(t, ref.Superseded)
assert.Equal(t, "wiki/a/facts/n.md", ref.Path)
got, _ := os.ReadFile(filepath.Join(dir, "wiki/a/facts/n.md"))
assert.Contains(t, string(got), "new")
assert.Contains(t, string(got), "supersede_reason: changed")
}
func TestStoreUpdateByFullPath(t *testing.T) {
dir := t.TempDir()
s := brainstore.New(dir)
_, err := s.Write(context.Background(), capture.Note{Content: "old\n", Filename: "n", Wing: "a", Hall: "facts"})
require.NoError(t, err)
ref, err := s.Update(context.Background(), "wiki/a/facts/n.md", capture.Note{Content: "fresh\n"})
require.NoError(t, err)
assert.Equal(t, "wiki/a/facts/n.md", ref.Path)
}
func TestStoreUpdateMissingErrors(t *testing.T) {
dir := t.TempDir()
s := brainstore.New(dir)
_, err := s.Update(context.Background(), "ghost", capture.Note{Content: "x\n", Wing: "a", Hall: "facts"})
require.Error(t, err)
_, statErr := os.Stat(filepath.Join(dir, "wiki/a/facts/ghost.md"))
assert.True(t, os.IsNotExist(statErr), "update must not create")
}
func TestStoreGetRoundTripsHash(t *testing.T) {
dir := t.TempDir()
s := brainstore.New(dir)
ref, err := s.Write(context.Background(), capture.Note{
Content: "# Body\n\ntext\n", Filename: "n", Wing: "a", Hall: "facts",
})
require.NoError(t, err)
note, err := s.Get(context.Background(), ref.ID)
require.NoError(t, err)
assert.Equal(t, ref.ContentHash, note.ContentHash, "write→get hash round-trips")
assert.Equal(t, "a", note.Frontmatter["wing"])
assert.Contains(t, note.Body, "# Body")
}