package engine import ( "testing" "git.kingecg.top/kingecg/gomog/pkg/types" ) // TestProjectionElemMatch 测试 $elemMatch 投影操作符 func TestProjectionElemMatch(t *testing.T) { tests := []struct { name string data map[string]interface{} field string spec map[string]interface{} expected interface{} }{ { name: "elemMatch finds first matching element", data: map[string]interface{}{ "scores": []interface{}{ map[string]interface{}{"subject": "math", "score": 85}, map[string]interface{}{"subject": "english", "score": 92}, map[string]interface{}{"subject": "science", "score": 78}, }, }, field: "scores", spec: map[string]interface{}{ "$elemMatch": map[string]interface{}{ "score": map[string]interface{}{"$gte": float64(90)}, }, }, expected: map[string]interface{}{"subject": "english", "score": float64(92)}, }, { name: "elemMatch with no match returns nil", data: map[string]interface{}{ "scores": []interface{}{ map[string]interface{}{"subject": "math", "score": 65}, map[string]interface{}{"subject": "english", "score": 72}, }, }, field: "scores", spec: map[string]interface{}{ "$elemMatch": map[string]interface{}{ "score": map[string]interface{}{"$gte": float64(90)}, }, }, expected: nil, }, { name: "elemMatch with non-array field", data: map[string]interface{}{ "name": "Alice", }, field: "name", spec: map[string]interface{}{"$elemMatch": map[string]interface{}{}}, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := projectElemMatch(tt.data, tt.field, tt.spec) if !compareEq(result, tt.expected) { t.Errorf("projectElemMatch() = %v, want %v", result, tt.expected) } }) } } // TestProjectionSlice 测试 $slice 投影操作符 func TestProjectionSlice(t *testing.T) { tests := []struct { name string data map[string]interface{} field string sliceSpec interface{} expected interface{} }{ { name: "slice with positive limit - first N elements", data: map[string]interface{}{ "tags": []interface{}{"a", "b", "c", "d", "e"}, }, field: "tags", sliceSpec: float64(3), expected: []interface{}{"a", "b", "c"}, }, { name: "slice with negative limit - last N elements", data: map[string]interface{}{ "tags": []interface{}{"a", "b", "c", "d", "e"}, }, field: "tags", sliceSpec: float64(-2), expected: []interface{}{"d", "e"}, }, { name: "slice with skip and limit", data: map[string]interface{}{ "items": []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, }, field: "items", sliceSpec: []interface{}{float64(5), float64(3)}, expected: []interface{}{float64(6), float64(7), float64(8)}, }, { name: "slice with skip beyond array length", data: map[string]interface{}{ "items": []interface{}{1, 2, 3}, }, field: "items", sliceSpec: []interface{}{float64(10), float64(2)}, expected: []interface{}{}, }, { name: "slice with zero limit", data: map[string]interface{}{ "items": []interface{}{1, 2, 3}, }, field: "items", sliceSpec: float64(0), expected: []interface{}{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := projectSlice(tt.data, tt.field, tt.sliceSpec) if !compareEq(result, tt.expected) { t.Errorf("projectSlice() = %v, want %v", result, tt.expected) } }) } } // TestApplyProjection 测试投影应用 func TestApplyProjection(t *testing.T) { tests := []struct { name string docs []types.Document projection types.Projection expected int // expected number of documents }{ { name: "projection with inclusion mode", docs: []types.Document{ {ID: "1", Data: map[string]interface{}{"name": "Alice", "age": 25, "email": "alice@example.com"}}, {ID: "2", Data: map[string]interface{}{"name": "Bob", "age": 30, "email": "bob@example.com"}}, }, projection: types.Projection{ "name": 1, "age": 1, }, expected: 2, }, { name: "projection with exclusion mode", docs: []types.Document{ {ID: "1", Data: map[string]interface{}{"name": "Alice", "age": 25, "email": "alice@example.com"}}, }, projection: types.Projection{ "email": 0, }, expected: 1, }, { name: "projection excluding _id", docs: []types.Document{ {ID: "1", Data: map[string]interface{}{"name": "Alice", "age": 25}}, }, projection: types.Projection{ "name": 1, "_id": 0, }, expected: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := applyProjection(tt.docs, tt.projection) if len(result) != tt.expected { t.Errorf("applyProjection() returned %d documents, want %d", len(result), tt.expected) } }) } } // TestApplyProjectionToDoc 测试单个文档投影 func TestApplyProjectionToDoc(t *testing.T) { tests := []struct { name string data map[string]interface{} projection types.Projection checkField string expectHas bool }{ { name: "include specific fields", data: map[string]interface{}{ "name": "Alice", "age": 25, "email": "alice@example.com", }, projection: types.Projection{ "name": 1, "age": 1, }, checkField: "name", expectHas: true, }, { name: "exclude specific fields", data: map[string]interface{}{ "name": "Alice", "age": 25, "email": "alice@example.com", }, projection: types.Projection{ "email": 0, }, checkField: "email", expectHas: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := applyProjectionToDoc(tt.data, tt.projection) _, has := result[tt.checkField] if has != tt.expectHas { t.Errorf("applyProjectionToDoc() has field %s = %v, want %v", tt.checkField, has, tt.expectHas) } }) } }