259 lines
6.2 KiB
Go
259 lines
6.2 KiB
Go
package proxy
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestWorkflowEndpointNotFound(t *testing.T) {
|
||
|
|
// Create a minimal config
|
||
|
|
cfg := &config.Config{
|
||
|
|
Routes: make(map[string]*config.Route),
|
||
|
|
Models: map[string]*config.ModelUpstream{
|
||
|
|
"reasoning": {
|
||
|
|
Address: "localhost:8001",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
handler := New(cfg)
|
||
|
|
|
||
|
|
// Test POST /workflows with unknown workflow
|
||
|
|
body := map[string]interface{}{
|
||
|
|
"workflow": "unknown-workflow",
|
||
|
|
"input": map[string]interface{}{},
|
||
|
|
}
|
||
|
|
|
||
|
|
bodyBytes, _ := json.Marshal(body)
|
||
|
|
req := httptest.NewRequest("POST", "/workflows", bytes.NewReader(bodyBytes))
|
||
|
|
req.Header.Set("Content-Type", "application/json")
|
||
|
|
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
handler.ServeHTTP(w, req)
|
||
|
|
|
||
|
|
if w.Code != http.StatusBadRequest {
|
||
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
||
|
|
}
|
||
|
|
|
||
|
|
var response map[string]interface{}
|
||
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
||
|
|
|
||
|
|
if response["type"] != "https://api.example.com/problems/unknown-workflow" {
|
||
|
|
t.Errorf("Expected unknown-workflow error, got %v", response["type"])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWorkflowEndpointMissingWorkflow(t *testing.T) {
|
||
|
|
cfg := &config.Config{
|
||
|
|
Routes: make(map[string]*config.Route),
|
||
|
|
Models: make(map[string]*config.ModelUpstream),
|
||
|
|
}
|
||
|
|
|
||
|
|
handler := New(cfg)
|
||
|
|
|
||
|
|
// Test POST /workflows with missing workflow field
|
||
|
|
body := map[string]interface{}{
|
||
|
|
"input": map[string]interface{}{},
|
||
|
|
}
|
||
|
|
|
||
|
|
bodyBytes, _ := json.Marshal(body)
|
||
|
|
req := httptest.NewRequest("POST", "/workflows", bytes.NewReader(bodyBytes))
|
||
|
|
req.Header.Set("Content-Type", "application/json")
|
||
|
|
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
handler.ServeHTTP(w, req)
|
||
|
|
|
||
|
|
if w.Code != http.StatusBadRequest {
|
||
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
||
|
|
}
|
||
|
|
|
||
|
|
var response map[string]interface{}
|
||
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
||
|
|
|
||
|
|
if response["type"] != "https://api.example.com/problems/missing-workflow" {
|
||
|
|
t.Errorf("Expected missing-workflow error, got %v", response["type"])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWorkflowEndpointInvalidMethod(t *testing.T) {
|
||
|
|
cfg := &config.Config{
|
||
|
|
Routes: make(map[string]*config.Route),
|
||
|
|
Models: make(map[string]*config.ModelUpstream),
|
||
|
|
}
|
||
|
|
|
||
|
|
handler := New(cfg)
|
||
|
|
|
||
|
|
// Test GET /workflows (should be 405)
|
||
|
|
req := httptest.NewRequest("GET", "/workflows", nil)
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
handler.ServeHTTP(w, req)
|
||
|
|
|
||
|
|
if w.Code != http.StatusMethodNotAllowed {
|
||
|
|
t.Errorf("Expected 405, got %d", w.Code)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWorkflowEndpointInvalidJSON(t *testing.T) {
|
||
|
|
cfg := &config.Config{
|
||
|
|
Routes: make(map[string]*config.Route),
|
||
|
|
Models: make(map[string]*config.ModelUpstream),
|
||
|
|
}
|
||
|
|
|
||
|
|
handler := New(cfg)
|
||
|
|
|
||
|
|
// Test POST /workflows with invalid JSON
|
||
|
|
req := httptest.NewRequest("POST", "/workflows", bytes.NewReader([]byte("not json")))
|
||
|
|
req.Header.Set("Content-Type", "application/json")
|
||
|
|
|
||
|
|
w := httptest.NewRecorder()
|
||
|
|
handler.ServeHTTP(w, req)
|
||
|
|
|
||
|
|
if w.Code != http.StatusBadRequest {
|
||
|
|
t.Errorf("Expected 400, got %d", w.Code)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetAvailableWorkflows(t *testing.T) {
|
||
|
|
cfg := &config.Config{
|
||
|
|
Routes: make(map[string]*config.Route),
|
||
|
|
Models: make(map[string]*config.ModelUpstream),
|
||
|
|
}
|
||
|
|
|
||
|
|
handler := New(cfg)
|
||
|
|
|
||
|
|
workflows := handler.getAvailableWorkflows()
|
||
|
|
|
||
|
|
expectedWorkflows := []string{
|
||
|
|
"chat-and-embed",
|
||
|
|
"multi-model-chat",
|
||
|
|
"rag-pipeline",
|
||
|
|
"batch-embeddings",
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(workflows) != len(expectedWorkflows) {
|
||
|
|
t.Errorf("Expected %d workflows, got %d", len(expectedWorkflows), len(workflows))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check that all expected workflows are present
|
||
|
|
for _, expected := range expectedWorkflows {
|
||
|
|
found := false
|
||
|
|
for _, actual := range workflows {
|
||
|
|
if actual == expected {
|
||
|
|
found = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !found {
|
||
|
|
t.Errorf("Expected workflow %q not found", expected)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetWorkflow(t *testing.T) {
|
||
|
|
cfg := &config.Config{
|
||
|
|
Routes: make(map[string]*config.Route),
|
||
|
|
Models: make(map[string]*config.ModelUpstream),
|
||
|
|
}
|
||
|
|
|
||
|
|
handler := New(cfg)
|
||
|
|
|
||
|
|
// Test getting a valid workflow
|
||
|
|
workflow, ok := handler.getWorkflow("chat-and-embed")
|
||
|
|
if !ok {
|
||
|
|
t.Error("Expected to find chat-and-embed workflow")
|
||
|
|
}
|
||
|
|
if workflow.Name != "chat-and-embed" {
|
||
|
|
t.Errorf("Expected workflow name chat-and-embed, got %s", workflow.Name)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test getting an invalid workflow
|
||
|
|
workflow, ok = handler.getWorkflow("invalid-workflow")
|
||
|
|
if ok {
|
||
|
|
t.Error("Expected not to find invalid-workflow")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGenerateWorkflowID(t *testing.T) {
|
||
|
|
id1 := generateWorkflowID()
|
||
|
|
id2 := generateWorkflowID()
|
||
|
|
|
||
|
|
if id1 == id2 {
|
||
|
|
t.Error("Generated workflow IDs should be unique")
|
||
|
|
}
|
||
|
|
|
||
|
|
if !bytes.HasPrefix([]byte(id1), []byte("wf_")) {
|
||
|
|
t.Errorf("Workflow ID should start with 'wf_', got %s", id1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestResponseCapture(t *testing.T) {
|
||
|
|
rc := &responseCapture{}
|
||
|
|
|
||
|
|
// Test Header
|
||
|
|
rc.Header().Set("X-Test", "value")
|
||
|
|
if rc.Header().Get("X-Test") != "value" {
|
||
|
|
t.Error("Header not set correctly")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test Write
|
||
|
|
n, err := rc.Write([]byte("test content"))
|
||
|
|
if err != nil {
|
||
|
|
t.Errorf("Unexpected error: %v", err)
|
||
|
|
}
|
||
|
|
if n != 12 {
|
||
|
|
t.Errorf("Expected 12 bytes written, got %d", n)
|
||
|
|
}
|
||
|
|
if rc.body.String() != "test content" {
|
||
|
|
t.Errorf("Expected 'test content', got %s", rc.body.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test WriteHeader
|
||
|
|
rc.WriteHeader(http.StatusOK)
|
||
|
|
if rc.status != http.StatusOK {
|
||
|
|
t.Errorf("Expected status 200, got %d", rc.status)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test WriteHeader doesn't override
|
||
|
|
rc.WriteHeader(http.StatusInternalServerError)
|
||
|
|
if rc.status != http.StatusOK {
|
||
|
|
t.Error("WriteHeader should not override existing status")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWorkflowResponseSerialization(t *testing.T) {
|
||
|
|
resp := WorkflowResponse{
|
||
|
|
ID: "wf_123",
|
||
|
|
Workflow: "test-workflow",
|
||
|
|
Status: "completed",
|
||
|
|
Output: map[string]interface{}{
|
||
|
|
"key": "value",
|
||
|
|
},
|
||
|
|
Error: "",
|
||
|
|
}
|
||
|
|
|
||
|
|
data, err := json.Marshal(resp)
|
||
|
|
if err != nil {
|
||
|
|
t.Errorf("Failed to marshal response: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
var unmarshaled WorkflowResponse
|
||
|
|
if err := json.Unmarshal(data, &unmarshaled); err != nil {
|
||
|
|
t.Errorf("Failed to unmarshal response: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if unmarshaled.ID != resp.ID {
|
||
|
|
t.Errorf("Expected ID %s, got %s", resp.ID, unmarshaled.ID)
|
||
|
|
}
|
||
|
|
if unmarshaled.Workflow != resp.Workflow {
|
||
|
|
t.Errorf("Expected Workflow %s, got %s", resp.Workflow, unmarshaled.Workflow)
|
||
|
|
}
|
||
|
|
if unmarshaled.Status != resp.Status {
|
||
|
|
t.Errorf("Expected Status %s, got %s", resp.Status, unmarshaled.Status)
|
||
|
|
}
|
||
|
|
}
|