From 317ec203920a995c4c811af93cfab7cff0f7201e Mon Sep 17 00:00:00 2001 From: Mathias Bergqvist Date: Sun, 3 May 2026 21:59:17 +0200 Subject: [PATCH] fix(hyperguild): brain Query uses POST /query with JSON body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The brain HTTP REST /query endpoint accepts POST with JSON {query, limit}, not GET with URL query string. Surfaced by Task 7 smoke testing — GET returned 405 Method Not Allowed. The response shape ({results:[...]}) is unchanged; only the request side flips to POST + JSON body. brainClient.Write was already using POST + JSON body and is unaffected. Tests updated to assert POST + JSON body on the Query path. --- cmd/hyperguild/brain_test.go | 12 +++++++++--- cmd/hyperguild/http.go | 22 +++++++++++++--------- cmd/hyperguild/http_test.go | 13 +++++++++++-- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/cmd/hyperguild/brain_test.go b/cmd/hyperguild/brain_test.go index 8cb3aa8..08f687d 100644 --- a/cmd/hyperguild/brain_test.go +++ b/cmd/hyperguild/brain_test.go @@ -49,9 +49,15 @@ func TestRunBrainQuery_JSON(t *testing.T) { } func TestRunBrainQuery_Limit(t *testing.T) { - gotLimit := "" + gotLimit := -1 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - gotLimit = r.URL.Query().Get("limit") + body, _ := io.ReadAll(r.Body) + var p struct { + Query string `json:"query"` + Limit int `json:"limit"` + } + _ = json.Unmarshal(body, &p) + gotLimit = p.Limit _, _ = w.Write([]byte(`{"results":[]}`)) })) defer srv.Close() @@ -60,7 +66,7 @@ func TestRunBrainQuery_Limit(t *testing.T) { 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) + assert.Equal(t, 12, gotLimit) } func TestRunBrainQuery_MissingTopic(t *testing.T) { diff --git a/cmd/hyperguild/http.go b/cmd/hyperguild/http.go index 36fefda..6dc1f3a 100644 --- a/cmd/hyperguild/http.go +++ b/cmd/hyperguild/http.go @@ -7,9 +7,7 @@ import ( "fmt" "io" "net/http" - "net/url" "os" - "strconv" "time" ) @@ -49,23 +47,29 @@ type QueryResult struct { } func (c *brainClient) Query(ctx context.Context, topic string, limit int) (*QueryResult, error) { - q := url.Values{} - q.Set("q", topic) - q.Set("limit", strconv.Itoa(limit)) - u := c.baseURL + "/query?" + q.Encode() + payload, err := json.Marshal(struct { + Query string `json:"query"` + Limit int `json:"limit"` + }{Query: topic, Limit: limit}) + if err != nil { + return nil, fmt.Errorf("marshal payload: %w", err) + } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) + u := c.baseURL + "/query" + req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, bytes.NewReader(payload)) if err != nil { return nil, fmt.Errorf("build request: %w", err) } + req.Header.Set("Content-Type", "application/json") + resp, err := c.http.Do(req) if err != nil { - return nil, fmt.Errorf("brain GET /query: %w", err) + return nil, fmt.Errorf("brain POST /query: %w", err) } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("brain GET /query: status %d: %s", resp.StatusCode, string(body)) + return nil, fmt.Errorf("brain POST /query: status %d: %s", resp.StatusCode, string(body)) } var out QueryResult if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { diff --git a/cmd/hyperguild/http_test.go b/cmd/hyperguild/http_test.go index c697dc6..d3c77d9 100644 --- a/cmd/hyperguild/http_test.go +++ b/cmd/hyperguild/http_test.go @@ -15,9 +15,18 @@ import ( func TestBrainClient_Query_Success(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "/query", r.URL.Path) - assert.Equal(t, "find-h", r.URL.Query().Get("q")) - assert.Equal(t, "3", r.URL.Query().Get("limit")) + + body, _ := io.ReadAll(r.Body) + var got struct { + Query string `json:"query"` + Limit int `json:"limit"` + } + require.NoError(t, json.Unmarshal(body, &got)) + assert.Equal(t, "find-h", got.Query) + assert.Equal(t, 3, got.Limit) + w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"results":[{"path":"knowledge/x.md","title":"x","excerpt":"...","score":7}]}`)) }))