369 lines
7.9 KiB
Go
369 lines
7.9 KiB
Go
package cassandra
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gocql/gocql"
|
|
"github.com/scylladb/gocqlx/v3/qb"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestInsert(t *testing.T) {
|
|
container, db := setupForTest(t)
|
|
defer func() {
|
|
_ = container.Container.Terminate(container.Ctx)
|
|
fmt.Println("[TEST] Container terminated")
|
|
}()
|
|
|
|
now := time.Now()
|
|
// 測試案例(可擴充)
|
|
tests := []struct {
|
|
name string
|
|
input MonkeyEntity
|
|
}{
|
|
{
|
|
name: "insert George",
|
|
input: MonkeyEntity{
|
|
ID: gocql.TimeUUID(),
|
|
Name: "George",
|
|
UpdateAt: now,
|
|
CreateAt: now,
|
|
},
|
|
},
|
|
{
|
|
name: "insert Bob",
|
|
input: MonkeyEntity{
|
|
ID: gocql.TimeUUID(),
|
|
Name: "Bob",
|
|
UpdateAt: now,
|
|
CreateAt: now,
|
|
},
|
|
},
|
|
{
|
|
name: "insert Alice",
|
|
input: MonkeyEntity{
|
|
ID: gocql.TimeUUID(),
|
|
Name: "Alice",
|
|
UpdateAt: now,
|
|
CreateAt: now,
|
|
},
|
|
},
|
|
}
|
|
|
|
// 執行測試
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := db.Insert(container.Ctx, &tc.input, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
|
|
// 驗證寫入
|
|
var name string
|
|
q := db.GetSession().Query("SELECT name FROM my_keyspace.monkey_entity WHERE id = ?", []string{"name"})
|
|
err = q.Bind(tc.input.ID).GetRelease(&name)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.input.Name, name)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGet(t *testing.T) {
|
|
container, db := setupForTest(t)
|
|
defer func() {
|
|
_ = container.Container.Terminate(container.Ctx)
|
|
fmt.Println("[TEST] Container terminated")
|
|
}()
|
|
now := time.Now()
|
|
monkey := MonkeyEntity{
|
|
ID: gocql.TimeUUID(),
|
|
Name: "George",
|
|
UpdateAt: now,
|
|
CreateAt: now,
|
|
}
|
|
|
|
// 插入一筆資料
|
|
err := db.Insert(container.Ctx, &monkey, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
filter MonkeyEntity
|
|
expect string
|
|
}{
|
|
{
|
|
name: "Get existing monkey",
|
|
filter: MonkeyEntity{ID: monkey.ID, Name: monkey.Name},
|
|
expect: "George",
|
|
},
|
|
{
|
|
name: "Get non-existent monkey",
|
|
filter: MonkeyEntity{ID: gocql.TimeUUID(), Name: "GG"},
|
|
expect: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.filter // 預設填入主鍵
|
|
err := db.Get(container.Ctx, &result, "my_keyspace")
|
|
|
|
if tc.expect == "" {
|
|
assert.Error(t, err, "expected error for missing record")
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expect, result.Name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
container, db := setupForTest(t)
|
|
defer func() {
|
|
_ = container.Container.Terminate(container.Ctx)
|
|
fmt.Println("[TEST] Container terminated")
|
|
}()
|
|
now := time.Now()
|
|
monkey := MonkeyEntity{
|
|
ID: gocql.TimeUUID(),
|
|
Name: "DeleteMe",
|
|
UpdateAt: now,
|
|
CreateAt: now,
|
|
}
|
|
|
|
// 插入資料
|
|
err := db.Insert(container.Ctx, &monkey, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
|
|
// 先確認有插入成功
|
|
verify := MonkeyEntity{ID: monkey.ID, Name: monkey.Name}
|
|
err = db.Get(container.Ctx, &verify, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "DeleteMe", verify.Name)
|
|
|
|
// 執行刪除
|
|
err = db.Delete(container.Ctx, &monkey, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
|
|
// 再查,應該查不到
|
|
result := MonkeyEntity{ID: monkey.ID, Name: monkey.Name}
|
|
err = db.Get(container.Ctx, &result, "my_keyspace")
|
|
assert.Error(t, err, "expected error because record should be deleted")
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
container, db := setupForTest(t)
|
|
defer func() {
|
|
_ = container.Container.Terminate(container.Ctx)
|
|
fmt.Println("[TEST] Container terminated")
|
|
}()
|
|
|
|
now := time.Now()
|
|
id := gocql.TimeUUID()
|
|
|
|
// Step 1: 插入初始資料
|
|
monkey := MonkeyEntity{
|
|
ID: id,
|
|
Name: "OldName",
|
|
UpdateAt: now,
|
|
CreateAt: now,
|
|
}
|
|
err := db.Insert(container.Ctx, &monkey, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
|
|
// Step 2: 更新 UpdateAt 欄位(模擬只更新一欄)
|
|
updatedTime := now.Add(10 * time.Minute)
|
|
updateDoc := MonkeyEntity{
|
|
ID: id,
|
|
Name: "OldName", // 主鍵
|
|
UpdateAt: updatedTime,
|
|
// CreateAt 是零值,不會被更新
|
|
}
|
|
err = db.Update(container.Ctx, &updateDoc, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
|
|
// Step 3: 查詢回來驗證更新
|
|
result := MonkeyEntity{
|
|
ID: id,
|
|
Name: "OldName",
|
|
}
|
|
err = db.Get(container.Ctx, &result, "my_keyspace")
|
|
assert.NoError(t, err)
|
|
assert.WithinDuration(t, updatedTime, result.UpdateAt, time.Second)
|
|
assert.WithinDuration(t, now, result.CreateAt, time.Second) // 未被更新
|
|
}
|
|
|
|
func insertSampleConsistency(t *testing.T, db *CassandraDB, ctx context.Context, keyspace string) *Consistency {
|
|
|
|
err := db.EnsureTable(`
|
|
CREATE TABLE IF NOT EXISTS my_keyspace.consistency (
|
|
id UUID,
|
|
consistency_name TEXT,
|
|
last_task_id TEXT,
|
|
target TEXT,
|
|
status TEXT,
|
|
consistency_type TEXT,
|
|
consistency_map TEXT,
|
|
create_at BIGINT,
|
|
update_at BIGINT,
|
|
PRIMARY KEY ((id))
|
|
);`)
|
|
assert.NoError(t, err)
|
|
|
|
c := &Consistency{
|
|
ID: gocql.TimeUUID(),
|
|
ConsistencyName: "query-test",
|
|
LastTaskID: "task-1",
|
|
Target: "test.csv",
|
|
Status: "Running",
|
|
ConsistencyType: "simple",
|
|
ConsistencyMap: `{"example": "value"}`,
|
|
CreateAT: time.Now().UnixNano(),
|
|
UpdateAT: time.Now().UnixNano(),
|
|
}
|
|
|
|
err = db.Insert(ctx, c, keyspace)
|
|
assert.NoError(t, err)
|
|
return c
|
|
}
|
|
|
|
func TestQueryBuilder_WithWhere(t *testing.T) {
|
|
container, db := setupForTest(t)
|
|
defer func() {
|
|
_ = container.Container.Terminate(container.Ctx)
|
|
fmt.Println("[TEST] Container terminated")
|
|
}()
|
|
|
|
saved := insertSampleConsistency(t, db, container.Ctx, "my_keyspace")
|
|
|
|
t.Run("query by id", func(t *testing.T) {
|
|
var results []*Consistency
|
|
e := &Consistency{}
|
|
field := GetCqlTag(e, &e.ID)
|
|
err := db.QueryBuilder(
|
|
container.Ctx,
|
|
&Consistency{},
|
|
&results,
|
|
"my_keyspace",
|
|
WithWhere(
|
|
[]qb.Cmp{qb.Eq(field)},
|
|
map[string]any{field: saved.ID.String()},
|
|
),
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, results)
|
|
|
|
found := false
|
|
for _, r := range results {
|
|
if r.ID == saved.ID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "should find inserted consistency")
|
|
})
|
|
|
|
t.Run("query with unmatched id", func(t *testing.T) {
|
|
var results []*Consistency
|
|
|
|
e := &Consistency{}
|
|
field := GetCqlTag(e, &e.ID)
|
|
err := db.QueryBuilder(
|
|
container.Ctx,
|
|
&Consistency{},
|
|
&results,
|
|
"my_keyspace",
|
|
WithWhere(
|
|
[]qb.Cmp{qb.Eq(field)},
|
|
map[string]any{field: "NonExist"},
|
|
),
|
|
)
|
|
|
|
assert.Error(t, err)
|
|
assert.Empty(t, results)
|
|
})
|
|
|
|
t.Run("query by in", func(t *testing.T) {
|
|
var results []*Consistency
|
|
e := &Consistency{}
|
|
field := GetCqlTag(e, &e.ID)
|
|
err := db.QueryBuilder(
|
|
container.Ctx,
|
|
&Consistency{},
|
|
&results,
|
|
"my_keyspace",
|
|
WithWhere(
|
|
[]qb.Cmp{qb.In(field)},
|
|
map[string]any{field: []gocql.UUID{saved.ID}},
|
|
),
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, results)
|
|
|
|
found := false
|
|
for _, r := range results {
|
|
if r.ID == saved.ID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "should find inserted consistency")
|
|
})
|
|
|
|
t.Run("query by one is not in", func(t *testing.T) {
|
|
var results []*Consistency
|
|
e := &Consistency{}
|
|
field := GetCqlTag(e, &e.ID)
|
|
err := db.QueryBuilder(
|
|
container.Ctx,
|
|
&Consistency{},
|
|
&results,
|
|
"my_keyspace",
|
|
WithWhere(
|
|
[]qb.Cmp{qb.In(field)},
|
|
map[string]any{field: []gocql.UUID{saved.ID, gocql.TimeUUID()}},
|
|
),
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, results)
|
|
|
|
found := false
|
|
for _, r := range results {
|
|
if r.ID == saved.ID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "should find inserted consistency")
|
|
})
|
|
|
|
t.Run("query get all", func(t *testing.T) {
|
|
var results []*Consistency
|
|
e := &Consistency{}
|
|
err := db.QueryBuilder(
|
|
container.Ctx,
|
|
e,
|
|
&results,
|
|
"my_keyspace",
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, results)
|
|
|
|
found := false
|
|
for _, r := range results {
|
|
if r.ID == saved.ID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "should find inserted consistency")
|
|
})
|
|
|
|
}
|