package proxy import ( "bytes" "errors" "fmt" "io" "net/http" "net/http/httptest" "strings" "testing" "forgejo.riotpiao.com/rock/homelab-frontend/internal/config" ) // TestSystemoneRoutesByModel verifies POST /v1/systemone dispatches on the body // "model" field instead of falling through to path-based route lookup. Before // that path joined body dispatch, the request reached the route table and // failed with "no routes configured" when no routes were defined. func TestSystemoneRoutesByModel(t *testing.T) { clefCalled := false clefServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { clefCalled = true w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"decision":"allow"}`) })) defer clefServer.Close() clefAddr := strings.TrimPrefix(clefServer.URL, "http://") cfg := &config.Config{ Models: map[string]*config.ModelUpstream{ "clef-flash": { Name: "clef-flash", Address: clefAddr, }, }, Routes: make(map[string]*config.Route), } handler := New(cfg) defer handler.Close() server := httptest.NewServer(handler) defer server.Close() resp, err := http.Post( server.URL+"/v1/systemone", "application/json", bytes.NewReader([]byte(`{"model":"clef-flash","state":"x","questions":{}}`)), ) if err != nil { t.Fatalf("request failed: %v", err) } defer resp.Body.Close() if !clefCalled { t.Errorf("upstream clef-flash service was not called") } if resp.StatusCode != http.StatusOK { t.Errorf("expected 200 for known systemone model, got %d", resp.StatusCode) } body, readErr := io.ReadAll(resp.Body) if readErr != nil { t.Fatalf("failed to read response body: %v", readErr) } if strings.Contains(string(body), "no routes configured") { t.Errorf("systemone request fell through to route lookup: %s", string(body)) } if !strings.Contains(string(body), `"decision":"allow"`) { t.Errorf("expected upstream response body, got %s", string(body)) } } // TestSystemoneUnknownModelValidationError verifies an unknown model on // /v1/systemone returns the same modelValidationError kind as the other three // body-dispatched paths. func TestSystemoneUnknownModelValidationError(t *testing.T) { cfg := &config.Config{ Models: map[string]*config.ModelUpstream{ "clef-flash": { Name: "clef-flash", Address: "localhost:9000", }, }, Routes: make(map[string]*config.Route), } handler := New(cfg) defer handler.Close() paths := []string{ "/v1/chat/completions", "/v1/embeddings", "/v1/rerank", "/v1/systemone", } for _, path := range paths { req := httptest.NewRequest( "POST", path, strings.NewReader(`{"model":"unknown-model"}`), ) route, err := handler.RouteRequest(req) if route != nil { t.Errorf("%s: expected nil route for unknown model", path) } var validationErr *modelValidationError if !errors.As(err, &validationErr) { t.Fatalf("%s: expected modelValidationError, got %v", path, err) } if validationErr.Kind != "unknown_model" { t.Errorf("%s: expected kind unknown_model, got %s", path, validationErr.Kind) } } } // TestSystemoneUpstreamPathNotRewritten verifies routeByModel keeps the request // path for /v1/systemone: upstream must see /v1/systemone, not // /v1/chat/completions. /v1/rerank is the only endpoint with a path rewrite. func TestSystemoneUpstreamPathNotRewritten(t *testing.T) { upstreamPath := "" upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { upstreamPath = r.URL.Path w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"decision":"deny"}`) })) defer upstreamServer.Close() upstreamAddr := strings.TrimPrefix(upstreamServer.URL, "http://") cfg := &config.Config{ Models: map[string]*config.ModelUpstream{ "clef-flash": { Name: "clef-flash", Address: upstreamAddr, }, }, Routes: make(map[string]*config.Route), } handler := New(cfg) defer handler.Close() server := httptest.NewServer(handler) defer server.Close() resp, err := http.Post( server.URL+"/v1/systemone", "application/json", bytes.NewReader([]byte(`{"model":"clef-flash","state":"x","questions":{}}`)), ) if err != nil { t.Fatalf("request failed: %v", err) } defer resp.Body.Close() if upstreamPath != "/v1/systemone" { t.Errorf("expected upstream path /v1/systemone, got %s", upstreamPath) } if upstreamPath == "/v1/chat/completions" { t.Errorf("upstream path was rewritten to /v1/chat/completions") } }