mirror of
https://github.com/Warky-Devs/vecna.git
synced 2026-08-07 10:47:38 +00:00
* implement proxy handler for various OpenAI API routes * add error handling for request body and response streaming * introduce new error response format for API compatibility * add tests for recover middleware to handle panics gracefully
89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/uptrace/bunrouter"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// TestRecoverMiddlewarePanicBeforeResponse verifies a panic in a handler that
|
|
// hasn't written anything yet is converted into a 500 JSON error instead of
|
|
// crashing the request goroutine (which would otherwise surface to the client
|
|
// as a connection reset).
|
|
func TestRecoverMiddlewarePanicBeforeResponse(t *testing.T) {
|
|
mw := recoverMiddleware(zap.NewNop())
|
|
handler := mw(func(w http.ResponseWriter, req bunrouter.Request) error {
|
|
panic("boom")
|
|
})
|
|
|
|
req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/v1/models", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
err := handler(w, bunrouter.Request{Request: req})
|
|
if err != nil {
|
|
t.Fatalf("expected recoverMiddleware to swallow the panic and return nil, got %v", err)
|
|
}
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Fatalf("expected status 500, got %d", w.Code)
|
|
}
|
|
|
|
var body apiErrorEnvelope
|
|
if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
|
|
t.Fatalf("expected a valid JSON error body, got decode error: %v (body=%q)", err, w.Body.String())
|
|
}
|
|
if body.Error.Type != "internal_error" {
|
|
t.Fatalf("expected error type internal_error, got %q", body.Error.Type)
|
|
}
|
|
}
|
|
|
|
// TestRecoverMiddlewarePanicAfterResponse verifies a panic that happens after
|
|
// the response has already been committed (e.g. mid-stream) doesn't attempt a
|
|
// second WriteHeader — it just surfaces as an error for the logger.
|
|
func TestRecoverMiddlewarePanicAfterResponse(t *testing.T) {
|
|
mw := recoverMiddleware(zap.NewNop())
|
|
handler := mw(func(w http.ResponseWriter, req bunrouter.Request) error {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte("partial"))
|
|
panic("boom mid-stream")
|
|
})
|
|
|
|
req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/v1/chat/completions", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
err := handler(w, bunrouter.Request{Request: req})
|
|
if err == nil {
|
|
t.Fatal("expected an error to be returned once the response was already committed")
|
|
}
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected the original 200 to stand, got %d", w.Code)
|
|
}
|
|
if w.Body.String() != "partial" {
|
|
t.Fatalf("expected body to be left as-is, got %q", w.Body.String())
|
|
}
|
|
}
|
|
|
|
// TestRecoverMiddlewareNoPanic verifies the happy path is unaffected.
|
|
func TestRecoverMiddlewareNoPanic(t *testing.T) {
|
|
mw := recoverMiddleware(zap.NewNop())
|
|
handler := mw(func(w http.ResponseWriter, req bunrouter.Request) error {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte("ok"))
|
|
return nil
|
|
})
|
|
|
|
req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/v1/models", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
if err := handler(w, bunrouter.Request{Request: req}); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if w.Code != http.StatusOK || w.Body.String() != "ok" {
|
|
t.Fatalf("unexpected response: status=%d body=%q", w.Code, w.Body.String())
|
|
}
|
|
}
|