Files
go-omni/examples/userService/internal/repository/user/transaction_test.go

85 lines
2.1 KiB
Go

package user
import (
"context"
"errors"
"os"
"testing"
"example.com/userService/internal/model"
"git.yuntuo.hk/k2safe/go-omni/database"
)
// This suite runs only against the disposable database created by verifyAPIWorkflow.
func TestTransactions(t *testing.T) {
if os.Getenv("USER_SERVICE_TEST_DATABASE") != "1" {
t.Skip("isolated MySQL required")
}
cfg, err := database.ConfigFromEnv("")
if err != nil {
t.Fatal(err)
}
db, err := database.Open(context.Background(), cfg)
if err != nil {
t.Fatal(err)
}
defer db.Close()
store := NewStore(db)
for _, kind := range []string{"error", "panic", "cancel", "commit"} {
t.Run(kind, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
email := kind + "@transaction.example"
var recovered any
func() {
defer func() { recovered = recover() }()
err = store.WithTx(ctx, func(r UserRepository, audit func(string, int64, string) error) error {
u, e := r.Create(ctx, model.UserInput{Name: "Transaction", Email: email})
if e != nil {
return e
}
if e = audit("created", u.ID, "transactionCheck"); e != nil {
return e
}
switch kind {
case "error":
return errors.New("rollback sentinel")
case "panic":
panic("rollback sentinel")
case "cancel":
cancel()
}
return nil
})
}()
if kind == "panic" && recovered == nil {
t.Fatal("panic lost")
}
if kind == "commit" && err != nil {
t.Fatal(err)
}
if kind != "commit" && kind != "panic" && err == nil {
t.Fatal("failed transaction committed")
}
var count int
if e := db.QueryRow("SELECT COUNT(*) FROM users WHERE email=?", email).Scan(&count); e != nil {
t.Fatal(e)
}
want := 0
if kind == "commit" {
want = 1
}
if count != want {
t.Fatalf("rows=%d want=%d", count, want)
}
})
}
var audits int
if err = db.QueryRow("SELECT COUNT(*) FROM user_audit WHERE request_id='transactionCheck'").Scan(&audits); err != nil {
t.Fatal(err)
}
if audits != 1 {
t.Fatalf("audit transaction mismatch: %d", audits)
}
}