backend/pkg/library/cassandra/repository_test.go

548 lines
12 KiB
Go
Raw Normal View History

2025-11-19 05:33:06 +00:00
package cassandra
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestContains(t *testing.T) {
tests := []struct {
name string
list []string
target string
want bool
}{
{
name: "target exists in list",
list: []string{"a", "b", "c"},
target: "b",
want: true,
},
{
name: "target at beginning",
list: []string{"a", "b", "c"},
target: "a",
want: true,
},
{
name: "target at end",
list: []string{"a", "b", "c"},
target: "c",
want: true,
},
{
name: "target not in list",
list: []string{"a", "b", "c"},
target: "d",
want: false,
},
{
name: "empty list",
list: []string{},
target: "a",
want: false,
},
{
name: "empty target",
list: []string{"a", "b", "c"},
target: "",
want: false,
},
{
name: "target in single element list",
list: []string{"a"},
target: "a",
want: true,
},
{
name: "case sensitive",
list: []string{"A", "B", "C"},
target: "a",
want: false,
},
{
name: "duplicate values",
list: []string{"a", "b", "a", "c"},
target: "a",
want: true,
},
{
name: "long list",
list: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"},
target: "j",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := contains(tt.list, tt.target)
assert.Equal(t, tt.want, result)
})
}
}
func TestIsZero(t *testing.T) {
tests := []struct {
name string
value any
expected bool
skip bool
}{
{
name: "nil pointer",
value: (*string)(nil),
expected: true,
skip: false,
},
{
name: "non-nil pointer",
value: stringPtr("test"),
expected: false,
skip: false,
},
{
name: "nil slice",
value: []string(nil),
expected: true,
skip: false,
},
{
name: "empty slice",
value: []string{},
expected: false, // 空 slice 不是 nil
skip: false,
},
{
name: "nil map",
value: map[string]int(nil),
expected: true,
skip: false,
},
{
name: "empty map",
value: map[string]int{},
expected: false, // 空 map 不是 nil
skip: false,
},
{
name: "zero int",
value: 0,
expected: true,
skip: false,
},
{
name: "non-zero int",
value: 42,
expected: false,
skip: false,
},
{
name: "zero int64",
value: int64(0),
expected: true,
skip: false,
},
{
name: "non-zero int64",
value: int64(42),
expected: false,
skip: false,
},
{
name: "zero float64",
value: 0.0,
expected: true,
skip: false,
},
{
name: "non-zero float64",
value: 3.14,
expected: false,
skip: false,
},
{
name: "empty string",
value: "",
expected: true,
skip: false,
},
{
name: "non-empty string",
value: "test",
expected: false,
skip: false,
},
{
name: "false bool",
value: false,
expected: true,
skip: false,
},
{
name: "true bool",
value: true,
expected: false,
skip: false,
},
{
name: "struct with zero values",
value: testUser{},
expected: true, // 所有欄位都是零值,應該返回 true
skip: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.skip {
t.Skip("Skipping test")
return
}
// 使用 reflect.ValueOf 來獲取 reflect.Value
v := reflect.ValueOf(tt.value)
// 檢查是否為零值nil interface 會導致 zero Value
if !v.IsValid() {
// 對於 nil interface直接返回 true
assert.True(t, tt.expected)
return
}
result := isZero(v)
assert.Equal(t, tt.expected, result)
})
}
}
func TestNewRepository(t *testing.T) {
tests := []struct {
name string
keyspace string
wantErr bool
validate func(*testing.T, Repository[testUser], *DB)
}{
{
name: "valid keyspace",
keyspace: "test_keyspace",
wantErr: false,
validate: func(t *testing.T, repo Repository[testUser], db *DB) {
assert.NotNil(t, repo)
},
},
{
name: "empty keyspace uses default",
keyspace: "",
wantErr: false,
validate: func(t *testing.T, repo Repository[testUser], db *DB) {
assert.NotNil(t, repo)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要一個有效的 DB 實例
// 在實際測試中,需要使用 mock 或 testcontainers
_ = tt
})
}
}
func TestRepository_Insert(t *testing.T) {
tests := []struct {
name string
description string
}{
{
name: "successful insert",
description: "should insert document successfully",
},
{
name: "duplicate key",
description: "should return error on duplicate key",
},
{
name: "invalid document",
description: "should return error for invalid document",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要 mock session 或實際的資料庫連接
_ = tt
})
}
}
func TestRepository_Get(t *testing.T) {
tests := []struct {
name string
pk any
description string
wantErr bool
}{
{
name: "found with string key",
pk: "test-id",
description: "should return document when found",
wantErr: false,
},
{
name: "not found",
pk: "non-existent",
description: "should return ErrNotFound when not found",
wantErr: true,
},
{
name: "invalid primary key structure",
pk: "single-key",
description: "should return error for invalid key structure",
wantErr: true,
},
{
name: "struct primary key",
pk: testUser{ID: "test-id"},
description: "should work with struct primary key",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要 mock session 或實際的資料庫連接
_ = tt
})
}
}
func TestRepository_Update(t *testing.T) {
tests := []struct {
name string
description string
wantErr bool
}{
{
name: "successful update",
description: "should update document successfully",
wantErr: false,
},
{
name: "not found",
description: "should return error when document not found",
wantErr: true,
},
{
name: "no fields to update",
description: "should return error when no fields to update",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要 mock session 或實際的資料庫連接
_ = tt
})
}
}
func TestRepository_Delete(t *testing.T) {
tests := []struct {
name string
pk any
description string
wantErr bool
}{
{
name: "successful delete",
pk: "test-id",
description: "should delete document successfully",
wantErr: false,
},
{
name: "not found",
pk: "non-existent",
description: "should not return error when not found (idempotent)",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要 mock session 或實際的資料庫連接
_ = tt
})
}
}
func TestRepository_InsertMany(t *testing.T) {
tests := []struct {
name string
docs []testUser
description string
wantErr bool
}{
{
name: "empty slice",
docs: []testUser{},
description: "should return nil for empty slice",
wantErr: false,
},
{
name: "single document",
docs: []testUser{{ID: "1", Name: "Alice"}},
description: "should insert single document",
wantErr: false,
},
{
name: "multiple documents",
docs: []testUser{{ID: "1", Name: "Alice"}, {ID: "2", Name: "Bob"}},
description: "should insert multiple documents",
wantErr: false,
},
{
name: "large batch",
docs: make([]testUser, 100),
description: "should handle large batch",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要 mock session 或實際的資料庫連接
_ = tt
})
}
}
func TestRepository_Query(t *testing.T) {
t.Run("should return QueryBuilder", func(t *testing.T) {
// 注意:這需要一個有效的 repository
// 實際的執行需要資料庫連接
})
}
func TestBuildUpdateStatement(t *testing.T) {
tests := []struct {
name string
setCols []string
whereCols []string
table string
validate func(*testing.T, string, []string)
}{
{
name: "single set column, single where column",
setCols: []string{"name"},
whereCols: []string{"id"},
table: "users",
validate: func(t *testing.T, stmt string, names []string) {
assert.Contains(t, stmt, "UPDATE")
assert.Contains(t, stmt, "users")
assert.Contains(t, stmt, "SET")
assert.Contains(t, stmt, "WHERE")
assert.Len(t, names, 2) // name, id
},
},
{
name: "multiple set columns, single where column",
setCols: []string{"name", "email", "age"},
whereCols: []string{"id"},
table: "users",
validate: func(t *testing.T, stmt string, names []string) {
assert.Contains(t, stmt, "UPDATE")
assert.Contains(t, stmt, "users")
assert.Len(t, names, 4) // name, email, age, id
},
},
{
name: "single set column, multiple where columns",
setCols: []string{"status"},
whereCols: []string{"user_id", "account_id"},
table: "accounts",
validate: func(t *testing.T, stmt string, names []string) {
assert.Contains(t, stmt, "UPDATE")
assert.Contains(t, stmt, "accounts")
assert.Len(t, names, 3) // status, user_id, account_id
},
},
{
name: "multiple set and where columns",
setCols: []string{"name", "email"},
whereCols: []string{"id", "version"},
table: "users",
validate: func(t *testing.T, stmt string, names []string) {
assert.Contains(t, stmt, "UPDATE")
assert.Len(t, names, 4) // name, email, id, version
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 創建一個臨時的 repository 來測試 buildUpdateStatement
// 注意:這需要一個有效的 metadata
// 使用 testUser 的 metadata
var zero testUser
metadata, err := generateMetadata(zero, "test_keyspace")
require.NoError(t, err)
repo := &repository[testUser]{
table: tt.table,
metadata: metadata,
}
stmt, names := repo.buildUpdateStatement(tt.setCols, tt.whereCols)
tt.validate(t, stmt, names)
})
}
}
func TestBuildUpdateFields(t *testing.T) {
tests := []struct {
name string
doc testUser
includeZero bool
wantErr bool
validate func(*testing.T, *updateFields)
}{
{
name: "update with includeZero false",
doc: testUser{ID: "1", Name: "Alice", Email: "alice@example.com"},
includeZero: false,
wantErr: false,
validate: func(t *testing.T, fields *updateFields) {
assert.NotEmpty(t, fields.setCols)
assert.Contains(t, fields.whereCols, "id")
},
},
{
name: "update with includeZero true",
doc: testUser{ID: "1", Name: "", Email: ""},
includeZero: true,
wantErr: false,
validate: func(t *testing.T, fields *updateFields) {
assert.NotEmpty(t, fields.setCols)
},
},
{
name: "no fields to update",
doc: testUser{ID: "1"},
includeZero: false,
wantErr: true,
validate: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 注意:這需要一個有效的 repository 和 metadata
// 在實際測試中,需要使用 mock 或 testcontainers
_ = tt
})
}
}