backend/pkg/library/cassandra/query_test.go

521 lines
11 KiB
Go
Raw Normal View History

2025-11-19 05:33:06 +00:00
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
})
}
}