292 lines
6.8 KiB
Go
292 lines
6.8 KiB
Go
|
package cassandra
|
|||
|
|
|||
|
import (
|
|||
|
"github.com/gocql/gocql"
|
|||
|
"github.com/stretchr/testify/assert"
|
|||
|
"testing"
|
|||
|
)
|
|||
|
|
|||
|
type TE struct {
|
|||
|
ID gocql.UUID `cql:"id" partition:"true"`
|
|||
|
Name string `cql:"name"`
|
|||
|
}
|
|||
|
|
|||
|
func (m *TE) TableName() string {
|
|||
|
return "test_entity"
|
|||
|
}
|
|||
|
|
|||
|
func TestNewEZTransactionInsert(t *testing.T) {
|
|||
|
ctx, cassandraContainer, host, port := setupCassandraContainer(t)
|
|||
|
defer cassandraContainer.Terminate(ctx)
|
|||
|
|
|||
|
// 連線
|
|||
|
hosts := []string{host}
|
|||
|
db, err := NewCassandraDB(
|
|||
|
hosts,
|
|||
|
WithPort(port),
|
|||
|
WithConsistency(gocql.One),
|
|||
|
WithNumConns(2),
|
|||
|
)
|
|||
|
assert.NoError(t, err)
|
|||
|
assert.NotNil(t, db)
|
|||
|
|
|||
|
// 建立 keyspace + table
|
|||
|
err = db.EnsureTable("CREATE KEYSPACE my_keyspace\nWITH replication = {\n 'class': 'SimpleStrategy',\n 'replication_factor': 1\n};\n")
|
|||
|
assert.NoError(t, err, "should success ensure table")
|
|||
|
|
|||
|
err = db.EnsureTable(`
|
|||
|
CREATE TABLE IF NOT EXISTS my_keyspace.test_entity (
|
|||
|
id UUID PRIMARY KEY,
|
|||
|
name TEXT
|
|||
|
);`)
|
|||
|
assert.NoError(t, err)
|
|||
|
|
|||
|
// 定義 table-driven 測試案例
|
|||
|
tests := []struct {
|
|||
|
name string
|
|||
|
doc TE
|
|||
|
}{
|
|||
|
{
|
|||
|
name: "insert_record_alice",
|
|||
|
doc: TE{
|
|||
|
ID: gocql.TimeUUID(),
|
|||
|
Name: "Alice",
|
|||
|
},
|
|||
|
},
|
|||
|
{
|
|||
|
name: "insert_record_bob",
|
|||
|
doc: TE{
|
|||
|
ID: gocql.TimeUUID(),
|
|||
|
Name: "Bob",
|
|||
|
},
|
|||
|
},
|
|||
|
{
|
|||
|
name: "insert_record_empty_name",
|
|||
|
doc: TE{
|
|||
|
ID: gocql.TimeUUID(),
|
|||
|
Name: "",
|
|||
|
},
|
|||
|
},
|
|||
|
}
|
|||
|
|
|||
|
for _, tt := range tests {
|
|||
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
// 每個子案例都使用新的 transaction
|
|||
|
tx := NewEZTransaction(ctx, "my_keyspace", db)
|
|||
|
|
|||
|
// 1. 呼叫 Insert
|
|||
|
err := tx.Insert(ctx, &tt.doc)
|
|||
|
assert.NoError(t, err, "Insert() 應該不會錯誤")
|
|||
|
|
|||
|
// 2. 呼叫 Commit,真正寫入 Cassandra
|
|||
|
err = tx.Commit()
|
|||
|
assert.NoError(t, err, "Commit() 應該不會錯誤")
|
|||
|
|
|||
|
// 3. 從 Cassandra 查回資料,驗證
|
|||
|
var got TE
|
|||
|
got.ID = tt.doc.ID
|
|||
|
|
|||
|
err = db.Get(ctx, &got, "my_keyspace")
|
|||
|
assert.NoError(t, err)
|
|||
|
// 驗證欄位值符合
|
|||
|
assert.Equal(t, tt.doc.ID, got.ID, "ID 應一致")
|
|||
|
assert.Equal(t, tt.doc.Name, got.Name, "Name 應一致")
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func TestNewEZTransactionDelete(t *testing.T) {
|
|||
|
ctx, cassandraContainer, host, port := setupCassandraContainer(t)
|
|||
|
defer cassandraContainer.Terminate(ctx)
|
|||
|
|
|||
|
// 連線
|
|||
|
hosts := []string{host}
|
|||
|
db, err := NewCassandraDB(
|
|||
|
hosts,
|
|||
|
WithPort(port),
|
|||
|
WithConsistency(gocql.One),
|
|||
|
WithNumConns(2),
|
|||
|
)
|
|||
|
assert.NoError(t, err)
|
|||
|
assert.NotNil(t, db)
|
|||
|
|
|||
|
// 建立 keyspace + table
|
|||
|
err = db.EnsureTable("CREATE KEYSPACE my_keyspace\nWITH replication = {\n 'class': 'SimpleStrategy',\n 'replication_factor': 1\n};\n")
|
|||
|
assert.NoError(t, err, "should success ensure table")
|
|||
|
|
|||
|
err = db.EnsureTable(`
|
|||
|
CREATE TABLE IF NOT EXISTS my_keyspace.test_entity (
|
|||
|
id UUID PRIMARY KEY,
|
|||
|
name TEXT
|
|||
|
);`)
|
|||
|
assert.NoError(t, err)
|
|||
|
|
|||
|
// 定義 table-driven 測試案例
|
|||
|
tests := []struct {
|
|||
|
name string
|
|||
|
doc TE
|
|||
|
}{
|
|||
|
{
|
|||
|
name: "ok",
|
|||
|
doc: TE{
|
|||
|
ID: gocql.TimeUUID(),
|
|||
|
Name: "Alice",
|
|||
|
},
|
|||
|
},
|
|||
|
}
|
|||
|
|
|||
|
for _, tt := range tests {
|
|||
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
// 每個子案例都使用新的 transaction
|
|||
|
tx := NewEZTransaction(ctx, "my_keyspace", db)
|
|||
|
|
|||
|
// 1. 呼叫 Delete
|
|||
|
err := tx.Insert(ctx, &tt.doc)
|
|||
|
assert.NoError(t, err, "Insert() 應該不會錯誤")
|
|||
|
|
|||
|
// 2. 呼叫 Delete
|
|||
|
err = tx.Delete(ctx, &tt.doc)
|
|||
|
assert.NoError(t, err, "Delete() 應該不會錯誤")
|
|||
|
|
|||
|
// 3. 呼叫 Commit,真正寫入 Cassandra
|
|||
|
err = tx.Commit()
|
|||
|
assert.NoError(t, err, "Commit() 應該不會錯誤")
|
|||
|
//
|
|||
|
// 4. 從 Cassandra 查回資料,驗證
|
|||
|
var got TE
|
|||
|
got.ID = tt.doc.ID
|
|||
|
|
|||
|
err = db.Get(ctx, &got, "my_keyspace")
|
|||
|
assert.Equal(t, err, gocql.ErrNotFound)
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func TestNewEZTransactionUpdate(t *testing.T) {
|
|||
|
ctx, cassandraContainer, host, port := setupCassandraContainer(t)
|
|||
|
t.Cleanup(func() { cassandraContainer.Terminate(ctx) })
|
|||
|
|
|||
|
// 1. 連線並建立 keyspace + table
|
|||
|
db, err := NewCassandraDB(
|
|||
|
[]string{host},
|
|||
|
WithPort(port),
|
|||
|
WithConsistency(gocql.One),
|
|||
|
WithNumConns(2),
|
|||
|
)
|
|||
|
assert.NoError(t, err)
|
|||
|
assert.NotNil(t, db)
|
|||
|
|
|||
|
assert.NoError(t, db.EnsureTable(`
|
|||
|
CREATE KEYSPACE IF NOT EXISTS my_keyspace
|
|||
|
WITH replication = {'class':'SimpleStrategy','replication_factor':1};
|
|||
|
`))
|
|||
|
assert.NoError(t, db.EnsureTable(`
|
|||
|
CREATE TABLE IF NOT EXISTS my_keyspace.test_entity (
|
|||
|
id UUID PRIMARY KEY,
|
|||
|
name TEXT
|
|||
|
);
|
|||
|
`))
|
|||
|
|
|||
|
// 2. 插入初始資料
|
|||
|
id := gocql.TimeUUID()
|
|||
|
before := TE{ID: id, Name: "Before"}
|
|||
|
assert.NoError(t, db.Insert(ctx, &before, "my_keyspace"))
|
|||
|
|
|||
|
// 定義多組更新案例
|
|||
|
tests := []struct {
|
|||
|
name string
|
|||
|
newName string
|
|||
|
wantErr bool
|
|||
|
}{
|
|||
|
{name: "update_to_Alice", newName: "Alice"},
|
|||
|
{name: "update_to_empty", newName: "", wantErr: true},
|
|||
|
{name: "update_to_Bob", newName: "Bob"},
|
|||
|
}
|
|||
|
|
|||
|
for _, tt := range tests {
|
|||
|
t.Run(tt.name, func(t *testing.T) {
|
|||
|
// 為每個案例都重置為 Before
|
|||
|
// 重新 insert 一次(覆蓋舊值)
|
|||
|
assert.NoError(t, db.Insert(ctx, &before, "my_keyspace"))
|
|||
|
|
|||
|
// 3. 建立 transaction 並呼叫 Update
|
|||
|
tx := NewEZTransaction(ctx, "my_keyspace", db)
|
|||
|
updateDoc := TE{ID: id, Name: tt.newName}
|
|||
|
err := tx.Update(ctx, &updateDoc)
|
|||
|
if tt.wantErr {
|
|||
|
assert.Error(t, err, "Update() 應該會出錯")
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
assert.NoError(t, err, "Update() 不應出錯")
|
|||
|
|
|||
|
// 4. Commit 實際寫入
|
|||
|
err = tx.Commit()
|
|||
|
assert.NoError(t, err, "Commit() 不應出錯")
|
|||
|
|
|||
|
// 5. 查詢並驗證
|
|||
|
var got TE
|
|||
|
got.ID = id
|
|||
|
err = db.Get(ctx, &got, "my_keyspace")
|
|||
|
assert.NoError(t, err, "db.Get() 應成功")
|
|||
|
|
|||
|
assert.Equal(t, id, got.ID, "ID 應一致")
|
|||
|
assert.Equal(t, tt.newName, got.Name, "Name 應被更新為最新值")
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func Test_Rollback(t *testing.T) {
|
|||
|
ctx, cassandraContainer, host, port := setupCassandraContainer(t)
|
|||
|
t.Cleanup(func() { cassandraContainer.Terminate(ctx) })
|
|||
|
|
|||
|
// 1. 連線並建立 keyspace + table
|
|||
|
db, err := NewCassandraDB(
|
|||
|
[]string{host},
|
|||
|
WithPort(port),
|
|||
|
WithConsistency(gocql.One),
|
|||
|
WithNumConns(2),
|
|||
|
)
|
|||
|
assert.NoError(t, err)
|
|||
|
assert.NotNil(t, db)
|
|||
|
|
|||
|
assert.NoError(t, db.EnsureTable(`
|
|||
|
CREATE KEYSPACE IF NOT EXISTS my_keyspace
|
|||
|
WITH replication = {'class':'SimpleStrategy','replication_factor':1};
|
|||
|
`))
|
|||
|
assert.NoError(t, db.EnsureTable(`
|
|||
|
CREATE TABLE IF NOT EXISTS my_keyspace.test_entity (
|
|||
|
id UUID PRIMARY KEY,
|
|||
|
name TEXT
|
|||
|
);
|
|||
|
`))
|
|||
|
|
|||
|
// 3. 用 Transaction 插入一筆資料,並 Commit
|
|||
|
id := gocql.TimeUUID()
|
|||
|
doc := TE{ID: id, Name: "Alice"}
|
|||
|
tx := NewEZTransaction(ctx, "my_keyspace", db)
|
|||
|
err = tx.Insert(ctx, &doc)
|
|||
|
assert.NoError(t, err)
|
|||
|
err = tx.Commit()
|
|||
|
assert.NoError(t, err)
|
|||
|
// 4. Query 確認資料已存在
|
|||
|
var got TE
|
|||
|
got.ID = id
|
|||
|
err = db.Get(ctx, &got, "my_keyspace")
|
|||
|
assert.NoError(t, err)
|
|||
|
assert.Equal(t, got.Name, doc.Name)
|
|||
|
|
|||
|
// 5. 呼叫 Rollback,應自動刪除剛剛那筆
|
|||
|
err = tx.Rollback()
|
|||
|
assert.NoError(t, err)
|
|||
|
|
|||
|
var afterGot TE
|
|||
|
afterGot.ID = id
|
|||
|
err = db.Get(ctx, &afterGot, "my_keyspace")
|
|||
|
assert.Error(t, err)
|
|||
|
|
|||
|
// Output:
|
|||
|
// after commit: Alice
|
|||
|
// after rollback: not found
|
|||
|
}
|