package engine import ( "testing" "git.kingecg.top/kingecg/gomog/pkg/types" ) // TestApplyUpdateSetOnInsert 测试 $setOnInsert 更新操作符 func TestApplyUpdateSetOnInsert(t *testing.T) { tests := []struct { name string data map[string]interface{} update types.Update isUpsertInsert bool expected map[string]interface{} }{ { name: "setOnInsert with upsert insert", data: map[string]interface{}{}, update: types.Update{ Set: map[string]interface{}{ "status": "active", }, SetOnInsert: map[string]interface{}{ "createdAt": "2024-01-01T00:00:00Z", "createdBy": "system", }, }, isUpsertInsert: true, expected: map[string]interface{}{ "status": "active", "createdAt": "2024-01-01T00:00:00Z", "createdBy": "system", }, }, { name: "setOnInsert without upsert insert", data: map[string]interface{}{ "_id": "existing", "status": "inactive", }, update: types.Update{ Set: map[string]interface{}{ "status": "active", }, SetOnInsert: map[string]interface{}{ "createdAt": "2024-01-01T00:00:00Z", }, }, isUpsertInsert: false, expected: map[string]interface{}{ "_id": "existing", "status": "active", // createdAt should NOT be set }, }, { name: "setOnInsert only applies on insert", data: map[string]interface{}{}, update: types.Update{ SetOnInsert: map[string]interface{}{ "initialValue": 100, }, }, isUpsertInsert: true, expected: map[string]interface{}{ "initialValue": 100, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := applyUpdateWithFilters(tt.data, tt.update, tt.isUpsertInsert, nil) for k, v := range tt.expected { if result[k] != v { t.Errorf("applyUpdateWithFilters()[%s] = %v, want %v", k, result[k], v) } } // Verify setOnInsert doesn't appear when not in upsert insert mode if !tt.isUpsertInsert { if _, exists := result["createdAt"]; exists { t.Error("setOnInsert should not apply when isUpsertInsert is false") } } }) } } // TestArrayPositionalOperators 测试数组位置操作符 func TestArrayPositionalOperators(t *testing.T) { tests := []struct { name string data map[string]interface{} field string value interface{} filters []map[string]interface{} expected map[string]interface{} }{ { name: "$[] update all elements", data: map[string]interface{}{ "scores": []interface{}{80, 90, 100}, }, field: "scores.$[]", value: 95, expected: map[string]interface{}{ "scores": []interface{}{95, 95, 95}, }, }, { name: "$[identifier] with filter", data: map[string]interface{}{ "students": []interface{}{ map[string]interface{}{"name": "Alice", "score": 85}, map[string]interface{}{"name": "Bob", "score": 95}, map[string]interface{}{"name": "Charlie", "score": 75}, }, }, field: "students.$[elem].grade", value: "A", filters: []map[string]interface{}{ { "identifier": "elem", "score": map[string]interface{}{"$gte": float64(90)}, }, }, expected: map[string]interface{}{ "students": []interface{}{ map[string]interface{}{"name": "Alice", "score": 85}, map[string]interface{}{"name": "Bob", "score": 95, "grade": "A"}, map[string]interface{}{"name": "Charlie", "score": 75}, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { updateArrayElement(tt.data, tt.field, tt.value, tt.filters) for k, v := range tt.expected { if result := getNestedValue(tt.data, k); !compareEq(result, v) { t.Errorf("updateArrayElement()[%s] = %v, want %v", k, result, v) } } }) } } // TestUpdateArrayAtPath 测试数组路径更新 func TestUpdateArrayAtPath(t *testing.T) { tests := []struct { name string data map[string]interface{} parts []string index int value interface{} filters []map[string]interface{} success bool expected interface{} }{ { name: "$[] operator updates all", data: map[string]interface{}{ "items": []interface{}{1, 2, 3}, }, parts: []string{"items", "$[]"}, index: 1, value: 100, success: true, expected: []interface{}{100, 100, 100}, }, { name: "$ operator updates first (simplified)", data: map[string]interface{}{ "tags": []interface{}{"a", "b", "c"}, }, parts: []string{"tags", "$"}, index: 1, value: "updated", success: true, expected: []interface{}{"updated", "b", "c"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := updateArrayAtPath(tt.data, tt.parts, tt.index, tt.value, tt.filters) if result != tt.success { t.Errorf("updateArrayAtPath() success = %v, want %v", result, tt.success) } if tt.success { arrField := tt.parts[0] if arr := getNestedValue(tt.data, arrField); !compareEq(arr, tt.expected) { t.Errorf("updateArrayAtPath() array = %v, want %v", arr, tt.expected) } } }) } } // TestConvertFiltersToMaps 测试过滤器转换 func TestConvertFiltersToMaps(t *testing.T) { tests := []struct { name string filters []types.Filter expected int }{ { name: "nil filters", filters: nil, expected: 0, }, { name: "empty filters", filters: []types.Filter{}, expected: 0, }, { name: "single filter", filters: []types.Filter{ {"score": map[string]interface{}{"$gte": 90}}, }, expected: 1, }, { name: "multiple filters", filters: []types.Filter{ {"score": map[string]interface{}{"$gte": 90}}, {"grade": map[string]interface{}{"$eq": "A"}}, }, expected: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := convertFiltersToMaps(tt.filters) if len(result) != tt.expected { t.Errorf("convertFiltersToMaps() length = %d, want %d", len(result), tt.expected) } }) } }