package http package engine import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "git.kingecg.top/kingecg/gomog/pkg/types" ) // TestHTTPUpdateWithUpsert 测试 HTTP API 的 upsert 功能 func TestHTTPUpdateWithUpsert(t *testing.T) { store := NewMemoryStore(nil) crud := &CRUDHandler{store: store} agg := &AggregationEngine{store: store} handler := NewRequestHandler(store, crud, agg) // Create test collection collection := "test.http_upsert" store.collections[collection] = &Collection{ name: collection, documents: make(map[string]types.Document), } // Test upsert request updateReq := types.UpdateRequest{ Updates: []types.UpdateOperation{ { Q: types.Filter{"_id": "new_user"}, U: types.Update{ Set: map[string]interface{}{ "name": "New User", "status": "active", }, SetOnInsert: map[string]interface{}{ "createdAt": "2024-01-01T00:00:00Z", }, }, Upsert: true, }, }, } body, _ := json.Marshal(updateReq) req := httptest.NewRequest(http.MethodPost, "/api/v1/test/http_upsert/update", bytes.NewReader(body)) w := httptest.NewRecorder() handler.HandleUpdate(w, req, "test", "http_upsert") if w.Code != http.StatusOK { t.Errorf("HandleUpdate() status = %d, want %d", w.Code, http.StatusOK) } var response types.UpdateResult if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if response.N != 1 { t.Errorf("Expected 1 document affected, got %d", response.N) } } // TestHTTPUpdateWithArrayFilters 测试 HTTP API 的 arrayFilters 功能 func TestHTTPUpdateWithArrayFilters(t *testing.T) { store := NewMemoryStore(nil) crud := &CRUDHandler{store: store} agg := &AggregationEngine{store: store} handler := NewRequestHandler(store, crud, agg) collection := "test.http_array_filters" store.collections[collection] = &Collection{ name: collection, documents: map[string]types.Document{ "doc1": { ID: "doc1", Data: map[string]interface{}{ "name": "Product", "grades": []interface{}{ map[string]interface{}{"subject": "math", "score": float64(95)}, map[string]interface{}{"subject": "english", "score": float64(75)}, }, }, }, }, } updateReq := types.UpdateRequest{ Updates: []types.UpdateOperation{ { Q: types.Filter{"name": "Product"}, U: types.Update{ Set: map[string]interface{}{ "grades.$[elem].passed": true, }, }, ArrayFilters: []types.Filter{ { "identifier": "elem", "score": map[string]interface{}{"$gte": float64(90)}, }, }, }, }, } body, _ := json.Marshal(updateReq) req := httptest.NewRequest(http.MethodPost, "/api/v1/test/http_array_filters/update", bytes.NewReader(body)) w := httptest.NewRecorder() handler.HandleUpdate(w, req, "test", "http_array_filters") if w.Code != http.StatusOK { t.Errorf("HandleUpdate() status = %d, want %d", w.Code, http.StatusOK) } // Verify the update was applied doc := store.collections[collection].documents["doc1"] grades, _ := doc.Data["grades"].([]interface{}) foundPassed := false for _, grade := range grades { g, _ := grade.(map[string]interface{}) if subject, ok := g["subject"].(string); ok && subject == "math" { if passed, ok := g["passed"].(bool); ok && passed { foundPassed = true break } } } if !foundPassed { t.Error("Expected math grade to have passed=true") } } // TestHTTPFindWithProjection 测试 HTTP API 的投影功能 func TestHTTPFindWithProjection(t *testing.T) { store := NewMemoryStore(nil) crud := &CRUDHandler{store: store} agg := &AggregationEngine{store: store} handler := NewRequestHandler(store, crud, agg) collection := "test.http_projection" store.collections[collection] = &Collection{ name: collection, documents: map[string]types.Document{ "doc1": { ID: "doc1", Data: map[string]interface{}{ "name": "Alice", "age": 25, "email": "alice@example.com", }, }, }, } findReq := types.FindRequest{ Filter: types.Filter{}, Projection: types.Projection{ "name": 1, "age": 1, "_id": 0, }, } body, _ := json.Marshal(findReq) req := httptest.NewRequest(http.MethodPost, "/api/v1/test/http_projection/find", bytes.NewReader(body)) w := httptest.NewRecorder() handler.HandleFind(w, req, "test", "http_projection") if w.Code != http.StatusOK { t.Errorf("HandleFind() status = %d, want %d", w.Code, http.StatusOK) } var response types.Response if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if len(response.Cursor.FirstBatch) != 1 { t.Errorf("Expected 1 document, got %d", len(response.Cursor.FirstBatch)) } // Check that only name and age are included (email should be excluded) doc := response.Cursor.FirstBatch[0].Data if _, exists := doc["name"]; !exists { t.Error("Expected 'name' field in projection") } if _, exists := doc["age"]; !exists { t.Error("Expected 'age' field in projection") } if _, exists := doc["email"]; exists { t.Error("Did not expect 'email' field in projection") } } // TestHTTPAggregateWithSwitch 测试 HTTP API 的 $switch 聚合 func TestHTTPAggregateWithSwitch(t *testing.T) { store := NewMemoryStore(nil) crud := &CRUDHandler{store: store} agg := &AggregationEngine{store: store} handler := NewRequestHandler(store, crud, agg) collection := "test.http_switch" store.collections[collection] = &Collection{ name: collection, documents: map[string]types.Document{ "doc1": {ID: "doc1", Data: map[string]interface{}{"score": float64(95)}}, "doc2": {ID: "doc2", Data: map[string]interface{}{"score": float64(85)}}, "doc3": {ID: "doc3", Data: map[string]interface{}{"score": float64(70)}}, }, } aggregateReq := types.AggregateRequest{ Pipeline: []types.AggregateStage{ { Stage: "$project", Spec: map[string]interface{}{ "grade": map[string]interface{}{ "$switch": map[string]interface{}{ "branches": []interface{}{ map[string]interface{}{ "case": map[string]interface{}{ "$gte": []interface{}{"$score", float64(90)}, }, "then": "A", }, map[string]interface{}{ "case": map[string]interface{}{ "$gte": []interface{}{"$score", float64(80)}, }, "then": "B", }, }, "default": "C", }, }, }, }, }, } body, _ := json.Marshal(aggregateReq) req := httptest.NewRequest(http.MethodPost, "/api/v1/test/http_switch/aggregate", bytes.NewReader(body)) w := httptest.NewRecorder() handler.HandleAggregate(w, req, "test", "http_switch") if w.Code != http.StatusOK { t.Errorf("HandleAggregate() status = %d, want %d", w.Code, http.StatusOK) } var response types.AggregateResult if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if len(response.Result) != 3 { t.Errorf("Expected 3 results, got %d", len(response.Result)) } // Verify grades are assigned correctly gradeCount := map[string]int{"A": 0, "B": 0, "C": 0} for _, doc := range response.Result { if grade, ok := doc.Data["grade"].(string); ok { gradeCount[grade]++ } } if gradeCount["A"] != 1 || gradeCount["B"] != 1 || gradeCount["C"] != 1 { t.Errorf("Grade distribution incorrect: %v", gradeCount) } } // TestHTTPHealthCheck 测试健康检查端点 func TestHTTPHealthCheck(t *testing.T) { store := NewMemoryStore(nil) crud := &CRUDHandler{store: store} agg := &AggregationEngine{store: store} server := NewHTTPServer(":0", NewRequestHandler(store, crud, agg)) req := httptest.NewRequest(http.MethodGet, "/health", nil) w := httptest.NewRecorder() server.mux.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Health check status = %d, want %d", w.Code, http.StatusOK) } var response map[string]interface{} if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("Failed to parse response: %v", err) } if response["status"] != "healthy" { t.Errorf("Expected healthy status, got %v", response["status"]) } }