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") }) }