521 lines
11 KiB
Go
521 lines
11 KiB
Go
|
|
package cassandra
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"testing"
|
|||
|
|
|
|||
|
|
"github.com/scylladb/gocqlx/v2/qb"
|
|||
|
|
"github.com/stretchr/testify/assert"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func TestEq(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
column string
|
|||
|
|
value any
|
|||
|
|
validate func(*testing.T, Condition)
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "string value",
|
|||
|
|
column: "name",
|
|||
|
|
value: "Alice",
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, "Alice", binds["name"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "int value",
|
|||
|
|
column: "age",
|
|||
|
|
value: 25,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 25, binds["age"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "nil value",
|
|||
|
|
column: "description",
|
|||
|
|
value: nil,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Nil(t, binds["description"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "empty string",
|
|||
|
|
column: "email",
|
|||
|
|
value: "",
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, "", binds["email"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "boolean value",
|
|||
|
|
column: "active",
|
|||
|
|
value: true,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, true, binds["active"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
cond := Eq(tt.column, tt.value)
|
|||
|
|
assert.NotNil(t, cond)
|
|||
|
|
tt.validate(t, cond)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestIn(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
column string
|
|||
|
|
values []any
|
|||
|
|
validate func(*testing.T, Condition)
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "string values",
|
|||
|
|
column: "status",
|
|||
|
|
values: []any{"active", "pending", "completed"},
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, []any{"active", "pending", "completed"}, binds["status"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "int values",
|
|||
|
|
column: "ids",
|
|||
|
|
values: []any{1, 2, 3, 4, 5},
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, []any{1, 2, 3, 4, 5}, binds["ids"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "empty slice",
|
|||
|
|
column: "tags",
|
|||
|
|
values: []any{},
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, []any{}, binds["tags"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "single value",
|
|||
|
|
column: "id",
|
|||
|
|
values: []any{1},
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, []any{1}, binds["id"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "mixed types",
|
|||
|
|
column: "values",
|
|||
|
|
values: []any{"string", 123, true},
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, []any{"string", 123, true}, binds["values"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
cond := In(tt.column, tt.values)
|
|||
|
|
assert.NotNil(t, cond)
|
|||
|
|
tt.validate(t, cond)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestGt(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
column string
|
|||
|
|
value any
|
|||
|
|
validate func(*testing.T, Condition)
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "int value",
|
|||
|
|
column: "age",
|
|||
|
|
value: 18,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 18, binds["age"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "float value",
|
|||
|
|
column: "price",
|
|||
|
|
value: 99.99,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 99.99, binds["price"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "zero value",
|
|||
|
|
column: "count",
|
|||
|
|
value: 0,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 0, binds["count"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
cond := Gt(tt.column, tt.value)
|
|||
|
|
assert.NotNil(t, cond)
|
|||
|
|
tt.validate(t, cond)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestLt(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
column string
|
|||
|
|
value any
|
|||
|
|
validate func(*testing.T, Condition)
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "int value",
|
|||
|
|
column: "age",
|
|||
|
|
value: 65,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 65, binds["age"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "float value",
|
|||
|
|
column: "price",
|
|||
|
|
value: 199.99,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 199.99, binds["price"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "negative value",
|
|||
|
|
column: "balance",
|
|||
|
|
value: -100,
|
|||
|
|
validate: func(t *testing.T, cond Condition) {
|
|||
|
|
cmp, binds := cond.Build()
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, -100, binds["balance"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
cond := Lt(tt.column, tt.value)
|
|||
|
|
assert.NotNil(t, cond)
|
|||
|
|
tt.validate(t, cond)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestCondition_Build(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
cond Condition
|
|||
|
|
validate func(*testing.T, qb.Cmp, map[string]any)
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "Eq condition",
|
|||
|
|
cond: Eq("name", "test"),
|
|||
|
|
validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, "test", binds["name"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "In condition",
|
|||
|
|
cond: In("ids", []any{1, 2, 3}),
|
|||
|
|
validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, []any{1, 2, 3}, binds["ids"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "Gt condition",
|
|||
|
|
cond: Gt("age", 18),
|
|||
|
|
validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 18, binds["age"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "Lt condition",
|
|||
|
|
cond: Lt("price", 100),
|
|||
|
|
validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
|
|||
|
|
assert.NotNil(t, cmp)
|
|||
|
|
assert.Equal(t, 100, binds["price"])
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
cmp, binds := tt.cond.Build()
|
|||
|
|
tt.validate(t, cmp, binds)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_Where(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
condition Condition
|
|||
|
|
validate func(*testing.T, *queryBuilder[testUser])
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "single condition",
|
|||
|
|
condition: Eq("name", "Alice"),
|
|||
|
|
validate: func(t *testing.T, qb *queryBuilder[testUser]) {
|
|||
|
|
assert.Len(t, qb.conditions, 1)
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "multiple conditions",
|
|||
|
|
condition: In("status", []any{"active", "pending"}),
|
|||
|
|
validate: func(t *testing.T, qb *queryBuilder[testUser]) {
|
|||
|
|
// 添加多個條件
|
|||
|
|
cond := In("status", []any{"active", "pending"})
|
|||
|
|
qb.Where(Eq("name", "test"))
|
|||
|
|
qb.Where(cond)
|
|||
|
|
assert.Len(t, qb.conditions, 2)
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要一個有效的 repository,但我們可以測試鏈式調用
|
|||
|
|
// 實際的執行需要資料庫連接
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_OrderBy(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
column string
|
|||
|
|
order Order
|
|||
|
|
validate func(*testing.T, *queryBuilder[testUser])
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "ASC order",
|
|||
|
|
column: "created_at",
|
|||
|
|
order: ASC,
|
|||
|
|
validate: func(t *testing.T, qb *queryBuilder[testUser]) {
|
|||
|
|
assert.Len(t, qb.orders, 1)
|
|||
|
|
assert.Equal(t, "created_at", qb.orders[0].column)
|
|||
|
|
assert.Equal(t, ASC, qb.orders[0].order)
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "DESC order",
|
|||
|
|
column: "updated_at",
|
|||
|
|
order: DESC,
|
|||
|
|
validate: func(t *testing.T, qb *queryBuilder[testUser]) {
|
|||
|
|
assert.Len(t, qb.orders, 1)
|
|||
|
|
assert.Equal(t, "updated_at", qb.orders[0].column)
|
|||
|
|
assert.Equal(t, DESC, qb.orders[0].order)
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "multiple orders",
|
|||
|
|
column: "name",
|
|||
|
|
order: ASC,
|
|||
|
|
validate: func(t *testing.T, qb *queryBuilder[testUser]) {
|
|||
|
|
qb.OrderBy("created_at", DESC)
|
|||
|
|
qb.OrderBy("name", ASC)
|
|||
|
|
assert.Len(t, qb.orders, 2)
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要一個有效的 repository
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_Limit(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
limit int
|
|||
|
|
expected int
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "positive limit",
|
|||
|
|
limit: 10,
|
|||
|
|
expected: 10,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "zero limit",
|
|||
|
|
limit: 0,
|
|||
|
|
expected: 0,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "large limit",
|
|||
|
|
limit: 1000,
|
|||
|
|
expected: 1000,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "negative limit",
|
|||
|
|
limit: -1,
|
|||
|
|
expected: -1,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要一個有效的 repository
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_Select(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
columns []string
|
|||
|
|
expected int
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "single column",
|
|||
|
|
columns: []string{"name"},
|
|||
|
|
expected: 1,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "multiple columns",
|
|||
|
|
columns: []string{"name", "email", "age"},
|
|||
|
|
expected: 3,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "empty columns",
|
|||
|
|
columns: []string{},
|
|||
|
|
expected: 0,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "duplicate columns",
|
|||
|
|
columns: []string{"name", "name"},
|
|||
|
|
expected: 2,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要一個有效的 repository
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_Chaining(t *testing.T) {
|
|||
|
|
t.Run("chain multiple methods", func(t *testing.T) {
|
|||
|
|
// 注意:這需要一個有效的 repository
|
|||
|
|
// 實際的執行需要資料庫連接
|
|||
|
|
// 這裡只是展示測試結構
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_Scan_ErrorCases(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
description string
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "nil destination",
|
|||
|
|
description: "should return error when destination is nil",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "invalid query",
|
|||
|
|
description: "should return error when query is invalid",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要 mock session 或實際的資料庫連接
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_One_ErrorCases(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
description string
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "no results",
|
|||
|
|
description: "should return ErrNotFound when no results found",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "query error",
|
|||
|
|
description: "should return error when query fails",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要 mock session 或實際的資料庫連接
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestQueryBuilder_Count_ErrorCases(t *testing.T) {
|
|||
|
|
tests := []struct {
|
|||
|
|
name string
|
|||
|
|
description string
|
|||
|
|
}{
|
|||
|
|
{
|
|||
|
|
name: "query error",
|
|||
|
|
description: "should return error when query fails",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: "ErrNotFound should return 0",
|
|||
|
|
description: "should return 0 when ErrNotFound",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, tt := range tests {
|
|||
|
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
|
// 注意:這需要 mock session 或實際的資料庫連接
|
|||
|
|
_ = tt
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|