gomog/internal/engine/projection_test.go

239 lines
5.9 KiB
Go

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