Files
hyperguild/internal/brain/client_test.go
mathiasandClaude Opus 5.5 99fedeaa98 fix(ingestion): authenticate the REST API as a whole, not route by route
Only /mcp was wrapped in BearerMiddleware; /query, /write, /ingest*,
/promote, /pending and the rest on the same mux had no auth, and
infra#517 found them on the internet (mitigated there by narrowing the
ingress). They are still open on the tailnet and LAN NodePort 30330.

mountREST puts the REST routes on their own mux, mounted at "/" behind
the same bearer middleware and credential as /mcp. Routes registered on
the outer mux stay explicitly public (/mcp has its own auth, /capture,
the HMAC webhook, OAuth metadata, /metrics); anything else, including a
route added later, is authenticated by default.

The hyperguild CLI (cmd/hyperguild and internal/brain) now sends
BRAIN_MCP_TOKEN, which it never did. brain-ui and brain-ingest-ntfy
already send theirs.

Refs #149

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 16:17:45 +02:00

83 lines
2.6 KiB
Go

package brain_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"git.d-ma.be/mathias/hyperguild/internal/brain"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestQueryEmptyBaseURL(t *testing.T) {
result, err := brain.Query(context.Background(), "", "tdd patterns", 3)
require.NoError(t, err)
assert.Empty(t, result)
}
func TestQueryEmptyQuery(t *testing.T) {
result, err := brain.Query(context.Background(), "http://localhost:9999", "", 3)
require.NoError(t, err)
assert.Empty(t, result)
}
func TestQueryFormatsResults(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/query", r.URL.Path)
var req map[string]any
require.NoError(t, json.NewDecoder(r.Body).Decode(&req))
assert.Equal(t, "tdd patterns", req["query"])
json.NewEncoder(w).Encode(map[string]any{ //nolint:errcheck
"results": []map[string]any{
{"path": "knowledge/tdd.md", "title": "TDD Guide", "excerpt": "Always write tests first.", "score": 5},
{"path": "knowledge/go.md", "title": "Go Conventions", "excerpt": "Use table-driven tests.", "score": 3},
},
})
}))
defer srv.Close()
result, err := brain.Query(context.Background(), srv.URL, "tdd patterns", 3)
require.NoError(t, err)
assert.Contains(t, result, "## Relevant knowledge")
assert.Contains(t, result, "TDD Guide")
assert.Contains(t, result, "Always write tests first.")
assert.Contains(t, result, "Go Conventions")
}
func TestQueryEmptyResults(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{"results": []any{}}) //nolint:errcheck
}))
defer srv.Close()
result, err := brain.Query(context.Background(), srv.URL, "obscure query", 3)
require.NoError(t, err)
assert.Empty(t, result)
}
func TestQueryUnavailableServerReturnsEmpty(t *testing.T) {
// Brain unavailable — should degrade gracefully, no error
result, err := brain.Query(context.Background(), "http://127.0.0.1:19999", "query", 3)
require.NoError(t, err)
assert.Empty(t, result)
}
// hyperguild#149: the REST API requires a bearer token.
func TestQuerySendsBrainMCPToken(t *testing.T) {
var auth string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth = r.Header.Get("Authorization")
_, _ = w.Write([]byte(`{"results":[]}`))
}))
defer srv.Close()
t.Setenv("BRAIN_MCP_TOKEN", "lib-test-token")
_, _ = brain.Query(context.Background(), srv.URL, "x", 1)
if auth != "Bearer lib-test-token" {
t.Fatalf("Authorization = %q, want the bearer token", auth)
}
}