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) } }