311 lines
8.1 KiB
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"])
|
|
}
|
|
}
|