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 := database.DefaultConfig() cfg.Driver = "mysql" cfg.DSN = os.Getenv("GO_OMNI_DB_DSN") 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) } }