tx_test.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package sqlx
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "testing"
  7. "github.com/DATA-DOG/go-sqlmock"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/wuntsong-org/go-zero-plus/core/breaker"
  10. "github.com/wuntsong-org/go-zero-plus/core/stores/dbtest"
  11. )
  12. const (
  13. mockCommit = 1
  14. mockRollback = 2
  15. )
  16. type mockTx struct {
  17. status int
  18. }
  19. func (mt *mockTx) Commit() error {
  20. mt.status |= mockCommit
  21. return nil
  22. }
  23. func (mt *mockTx) Exec(_ string, _ ...any) (sql.Result, error) {
  24. return nil, nil
  25. }
  26. func (mt *mockTx) ExecCtx(_ context.Context, _ string, _ ...any) (sql.Result, error) {
  27. return nil, nil
  28. }
  29. func (mt *mockTx) Prepare(_ string) (StmtSession, error) {
  30. return nil, nil
  31. }
  32. func (mt *mockTx) PrepareCtx(_ context.Context, _ string) (StmtSession, error) {
  33. return nil, nil
  34. }
  35. func (mt *mockTx) QueryRow(_ any, _ string, _ ...any) error {
  36. return nil
  37. }
  38. func (mt *mockTx) QueryRowCtx(_ context.Context, _ any, _ string, _ ...any) error {
  39. return nil
  40. }
  41. func (mt *mockTx) QueryRowPartial(_ any, _ string, _ ...any) error {
  42. return nil
  43. }
  44. func (mt *mockTx) QueryRowPartialCtx(_ context.Context, _ any, _ string, _ ...any) error {
  45. return nil
  46. }
  47. func (mt *mockTx) QueryRows(_ any, _ string, _ ...any) error {
  48. return nil
  49. }
  50. func (mt *mockTx) QueryRowsCtx(_ context.Context, _ any, _ string, _ ...any) error {
  51. return nil
  52. }
  53. func (mt *mockTx) QueryRowsPartial(_ any, _ string, _ ...any) error {
  54. return nil
  55. }
  56. func (mt *mockTx) QueryRowsPartialCtx(_ context.Context, _ any, _ string, _ ...any) error {
  57. return nil
  58. }
  59. func (mt *mockTx) Rollback() error {
  60. mt.status |= mockRollback
  61. return nil
  62. }
  63. func beginMock(mock *mockTx) beginnable {
  64. return func(*sql.DB) (trans, error) {
  65. return mock, nil
  66. }
  67. }
  68. func TestTransactCommit(t *testing.T) {
  69. mock := &mockTx{}
  70. err := transactOnConn(context.Background(), nil, beginMock(mock),
  71. func(context.Context, Session) error {
  72. return nil
  73. })
  74. assert.Equal(t, mockCommit, mock.status)
  75. assert.Nil(t, err)
  76. }
  77. func TestTransactRollback(t *testing.T) {
  78. mock := &mockTx{}
  79. err := transactOnConn(context.Background(), nil, beginMock(mock),
  80. func(context.Context, Session) error {
  81. return errors.New("rollback")
  82. })
  83. assert.Equal(t, mockRollback, mock.status)
  84. assert.NotNil(t, err)
  85. }
  86. func TestTxExceptions(t *testing.T) {
  87. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  88. mock.ExpectBegin()
  89. mock.ExpectCommit()
  90. conn := NewSqlConnFromDB(db)
  91. assert.NoError(t, conn.Transact(func(session Session) error {
  92. return nil
  93. }))
  94. })
  95. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  96. conn := &commonSqlConn{
  97. connProv: func() (*sql.DB, error) {
  98. return nil, errors.New("foo")
  99. },
  100. beginTx: begin,
  101. onError: func(ctx context.Context, err error) {},
  102. brk: breaker.NewBreaker(),
  103. }
  104. assert.Error(t, conn.Transact(func(session Session) error {
  105. return nil
  106. }))
  107. })
  108. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  109. _, err := conn.RawDB()
  110. assert.Equal(t, errNoRawDBFromTx, err)
  111. assert.Equal(t, errCantNestTx, conn.Transact(nil))
  112. assert.Equal(t, errCantNestTx, conn.TransactCtx(context.Background(), nil))
  113. })
  114. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  115. mock.ExpectBegin()
  116. conn := NewSqlConnFromDB(db)
  117. assert.Error(t, conn.Transact(func(session Session) error {
  118. return errors.New("foo")
  119. }))
  120. })
  121. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  122. mock.ExpectBegin()
  123. mock.ExpectRollback().WillReturnError(errors.New("foo"))
  124. conn := NewSqlConnFromDB(db)
  125. assert.Error(t, conn.Transact(func(session Session) error {
  126. panic("foo")
  127. }))
  128. })
  129. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  130. mock.ExpectBegin()
  131. mock.ExpectRollback()
  132. conn := NewSqlConnFromDB(db)
  133. assert.Error(t, conn.Transact(func(session Session) error {
  134. panic(errors.New("foo"))
  135. }))
  136. })
  137. }
  138. func TestTxSession(t *testing.T) {
  139. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  140. mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
  141. res, err := conn.Exec("any")
  142. assert.NoError(t, err)
  143. last, err := res.LastInsertId()
  144. assert.NoError(t, err)
  145. assert.Equal(t, int64(2), last)
  146. affected, err := res.RowsAffected()
  147. assert.NoError(t, err)
  148. assert.Equal(t, int64(3), affected)
  149. mock.ExpectExec("any").WillReturnError(errors.New("foo"))
  150. _, err = conn.Exec("any")
  151. assert.Equal(t, "foo", err.Error())
  152. })
  153. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  154. mock.ExpectPrepare("any")
  155. stmt, err := conn.Prepare("any")
  156. assert.NoError(t, err)
  157. assert.NotNil(t, stmt)
  158. mock.ExpectPrepare("any").WillReturnError(errors.New("foo"))
  159. _, err = conn.Prepare("any")
  160. assert.Equal(t, "foo", err.Error())
  161. })
  162. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  163. rows := sqlmock.NewRows([]string{"col"}).AddRow("foo")
  164. mock.ExpectQuery("any").WillReturnRows(rows)
  165. var val string
  166. err := conn.QueryRow(&val, "any")
  167. assert.NoError(t, err)
  168. assert.Equal(t, "foo", val)
  169. mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
  170. err = conn.QueryRow(&val, "any")
  171. assert.Equal(t, "foo", err.Error())
  172. })
  173. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  174. rows := sqlmock.NewRows([]string{"col"}).AddRow("foo")
  175. mock.ExpectQuery("any").WillReturnRows(rows)
  176. var val string
  177. err := conn.QueryRowPartial(&val, "any")
  178. assert.NoError(t, err)
  179. assert.Equal(t, "foo", val)
  180. mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
  181. err = conn.QueryRowPartial(&val, "any")
  182. assert.Equal(t, "foo", err.Error())
  183. })
  184. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  185. rows := sqlmock.NewRows([]string{"col"}).AddRow("foo").AddRow("bar")
  186. mock.ExpectQuery("any").WillReturnRows(rows)
  187. var val []string
  188. err := conn.QueryRows(&val, "any")
  189. assert.NoError(t, err)
  190. assert.Equal(t, []string{"foo", "bar"}, val)
  191. mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
  192. err = conn.QueryRows(&val, "any")
  193. assert.Equal(t, "foo", err.Error())
  194. })
  195. runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
  196. rows := sqlmock.NewRows([]string{"col"}).AddRow("foo").AddRow("bar")
  197. mock.ExpectQuery("any").WillReturnRows(rows)
  198. var val []string
  199. err := conn.QueryRowsPartial(&val, "any")
  200. assert.NoError(t, err)
  201. assert.Equal(t, []string{"foo", "bar"}, val)
  202. mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
  203. err = conn.QueryRowsPartial(&val, "any")
  204. assert.Equal(t, "foo", err.Error())
  205. })
  206. }
  207. func TestTxRollback(t *testing.T) {
  208. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  209. mock.ExpectBegin()
  210. mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
  211. mock.ExpectQuery("foo").WillReturnError(errors.New("foo"))
  212. mock.ExpectRollback()
  213. conn := NewSqlConnFromDB(db)
  214. err := conn.Transact(func(session Session) error {
  215. c := NewSqlConnFromSession(session)
  216. _, err := c.Exec("any")
  217. assert.NoError(t, err)
  218. var val string
  219. return c.QueryRow(&val, "foo")
  220. })
  221. assert.Error(t, err)
  222. })
  223. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  224. mock.ExpectBegin()
  225. mock.ExpectExec("any").WillReturnError(errors.New("foo"))
  226. mock.ExpectRollback()
  227. conn := NewSqlConnFromDB(db)
  228. err := conn.Transact(func(session Session) error {
  229. c := NewSqlConnFromSession(session)
  230. if _, err := c.Exec("any"); err != nil {
  231. return err
  232. }
  233. var val string
  234. assert.NoError(t, c.QueryRow(&val, "foo"))
  235. return nil
  236. })
  237. assert.Error(t, err)
  238. })
  239. dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
  240. mock.ExpectBegin()
  241. mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
  242. mock.ExpectQuery("foo").WillReturnRows(sqlmock.NewRows([]string{"col"}).AddRow("bar"))
  243. mock.ExpectCommit()
  244. conn := NewSqlConnFromDB(db)
  245. err := conn.Transact(func(session Session) error {
  246. c := NewSqlConnFromSession(session)
  247. _, err := c.Exec("any")
  248. assert.NoError(t, err)
  249. var val string
  250. assert.NoError(t, c.QueryRow(&val, "foo"))
  251. assert.Equal(t, "bar", val)
  252. return nil
  253. })
  254. assert.NoError(t, err)
  255. })
  256. }
  257. func runTxTest(t *testing.T, f func(conn SqlConn, mock sqlmock.Sqlmock)) {
  258. dbtest.RunTxTest(t, func(tx *sql.Tx, mock sqlmock.Sqlmock) {
  259. sess := NewSessionFromTx(tx)
  260. conn := NewSqlConnFromSession(sess)
  261. f(conn, mock)
  262. })
  263. }