Files
ratatoskr-go/internal/opencode/client_test.go
Hermes 774ebf135a
All checks were successful
CI / test (push) Successful in 40s
CI / build-and-package (amd64, linux) (push) Successful in 35s
CI / build-and-package (amd64, windows) (push) Successful in 39s
fix(opencode): отправка сообщения — путь без /api (POST /session/:id/message)
Убран префикс /api из POST-запроса на отправку сообщения, чтобы
совпадать с фактическим роутом serve v1.18. GET чтения сообщений
остаётся на /api/session/:id/message.

- client.go: Send → POST /session/:id/message
- тесты: fakeAPIServer отвечает на POST /session/{id}/message (GET — /api)
2026-08-18 19:28:45 +05:00

116 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package opencode
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
)
// fakeAPIServer — минимальный фейк opencode serve HTTP API v1.18.
type fakeAPIServer struct {
messages []sessionMessage
failCreate bool
failVerify bool
}
func (f *fakeAPIServer) handler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/api/session", func(w http.ResponseWriter, r *http.Request) {
if f.failCreate {
http.Error(w, "boom", http.StatusInternalServerError)
return
}
writeJSON(w, map[string]any{"data": map[string]any{"id": "sess-fake"}})
})
mux.HandleFunc("/api/session/{id}/wait", func(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, map[string]any{})
})
mux.HandleFunc("/api/session/{id}/interrupt", func(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, map[string]any{})
})
// POST отправка промпта (v1.18) — путь БЕЗ /api.
mux.HandleFunc("/session/{id}/message", func(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{"data": map[string]any{}})
})
// GET чтение сообщений — путь с /api.
mux.HandleFunc("/api/session/{id}/message", func(w http.ResponseWriter, r *http.Request) {
if len(f.messages) == 0 {
writeJSON(w, map[string]any{"data": []sessionMessage{}})
return
}
writeJSON(w, map[string]any{"data": f.messages})
})
return mux
}
func writeJSON(w http.ResponseWriter, v any) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(v)
}
// textPart — анонимная text-часть assistant-сообщения.
func textPart(s string) struct {
Type string `json:"type"`
Text string `json:"text"`
} {
return struct {
Type string `json:"type"`
Text string `json:"text"`
}{Type: "text", Text: s}
}
func fakeClient(t *testing.T, f *fakeAPIServer) *Client {
t.Helper()
ts := httptest.NewServer(f.handler())
t.Cleanup(ts.Close)
return &Client{BaseURL: ts.URL}
}
func TestClient_CreateSession(t *testing.T) {
c := fakeClient(t, &fakeAPIServer{})
id, err := c.CreateSession(context.Background(), "ratatoskr-analyst")
if err != nil {
t.Fatalf("CreateSession err: %v", err)
}
if id != "sess-fake" {
t.Errorf("id = %q, want sess-fake", id)
}
}
func TestClient_CreateSessionFail(t *testing.T) {
c := fakeClient(t, &fakeAPIServer{failCreate: true})
if _, err := c.CreateSession(context.Background(), "x"); err == nil {
t.Fatal("CreateSession должен упасть при 500, а не nil")
}
}
func TestClient_Verdict(t *testing.T) {
c := fakeClient(t, &fakeAPIServer{
messages: []sessionMessage{{Type: "assistant", Content: []struct {
Type string `json:"type"`
Text string `json:"text"`
}{textPart(`{"phase":"ready"}`)}}},
})
vd, err := c.Verdict(context.Background(), "sess-fake")
if err != nil {
t.Fatalf("Verdict err: %v", err)
}
if vd != `{"phase":"ready"}` {
t.Errorf("verdict = %q, want вердикт модели", vd)
}
}
func TestClient_VerdictNoText(t *testing.T) {
c := fakeClient(t, &fakeAPIServer{}) // нет assistant-сообщения с text
if _, err := c.Verdict(context.Background(), "sess-fake"); err == nil {
t.Fatal("Verdict должен упасть, когда нет text-части")
} else {
var ce *ClientErr
if !errors.As(err, &ce) {
t.Errorf("ожидался *ClientErr, got %T", err)
}
}
}