250 lines
5.9 KiB
Go
250 lines
5.9 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|