239 lines
6.0 KiB
Go
239 lines
6.0 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": float64(85)},
|
|
map[string]interface{}{"subject": "english", "score": float64(92)},
|
|
map[string]interface{}{"subject": "science", "score": float64(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{}{float64(1), float64(2), float64(3), float64(4), float64(5), float64(6), float64(7), float64(8), float64(9), float64(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)
|
|
}
|
|
})
|
|
}
|
|
}
|