feat(hyperguild): brain query subcommand

Adds 'hyperguild brain query <topic>' against the brain HTTP REST
/query endpoint. Default human output prints path + score + title;
--json passes through the response envelope. --limit overrides the
default 5-result cap.

runBrainWrite remains a stub for Task 5.
This commit is contained in:
2026-05-03 21:37:39 +02:00
parent ed4966927c
commit cd5f3c0175
4 changed files with 142 additions and 3 deletions
+81
View File
@@ -0,0 +1,81 @@
package main
import (
"bytes"
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func brainQueryServer(t *testing.T, body string) *httptest.Server {
t.Helper()
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(body))
}))
}
func TestRunBrainQuery_Human(t *testing.T) {
srv := brainQueryServer(t, `{"results":[{"path":"knowledge/a.md","title":"A","excerpt":"...","score":9},{"path":"knowledge/b.md","title":"B","excerpt":"...","score":3}]}`)
defer srv.Close()
t.Setenv("BRAIN_URL", srv.URL)
var out, errBuf bytes.Buffer
err := runBrain(context.Background(), []string{"query", "topic"}, strings.NewReader(""), &out, &errBuf)
require.NoError(t, err)
got := out.String()
assert.Contains(t, got, "knowledge/a.md")
assert.Contains(t, got, "score=9")
assert.Contains(t, got, "knowledge/b.md")
}
func TestRunBrainQuery_JSON(t *testing.T) {
srv := brainQueryServer(t, `{"results":[{"path":"x.md","title":"X","excerpt":"e","score":5}]}`)
defer srv.Close()
t.Setenv("BRAIN_URL", srv.URL)
var out, errBuf bytes.Buffer
err := runBrain(context.Background(), []string{"query", "--json", "topic"}, strings.NewReader(""), &out, &errBuf)
require.NoError(t, err)
assert.Contains(t, out.String(), `"path": "x.md"`)
assert.Contains(t, out.String(), `"score": 5`)
}
func TestRunBrainQuery_Limit(t *testing.T) {
gotLimit := ""
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotLimit = r.URL.Query().Get("limit")
_, _ = w.Write([]byte(`{"results":[]}`))
}))
defer srv.Close()
t.Setenv("BRAIN_URL", srv.URL)
var out, errBuf bytes.Buffer
err := runBrain(context.Background(), []string{"query", "--limit", "12", "topic"}, strings.NewReader(""), &out, &errBuf)
require.NoError(t, err)
assert.Equal(t, "12", gotLimit)
}
func TestRunBrainQuery_MissingTopic(t *testing.T) {
var out, errBuf bytes.Buffer
err := runBrain(context.Background(), []string{"query"}, strings.NewReader(""), &out, &errBuf)
assert.Error(t, err)
}
func TestRunBrain_NoSubsubcommand(t *testing.T) {
var out, errBuf bytes.Buffer
err := runBrain(context.Background(), []string{}, strings.NewReader(""), &out, &errBuf)
assert.Error(t, err)
assert.Contains(t, err.Error(), "subcommand required")
}
func TestRunBrain_UnknownSubsubcommand(t *testing.T) {
var out, errBuf bytes.Buffer
err := runBrain(context.Background(), []string{"bogus"}, strings.NewReader(""), &out, &errBuf)
assert.Error(t, err)
}