gomog/internal/protocol/http/batch2_test.go

311 lines
8.1 KiB
Go

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