/vendor/github.com/DATA-DOG/go-sqlmock/sqlmock.go

https://gitlab.com/sheket/server · Go · 454 lines · 337 code · 66 blank · 51 comment · 85 complexity · 8c95654fa35c41febbcd25a594e1c407 MD5 · raw file

  1. /*
  2. Package sqlmock is a mock library implementing sql driver. Which has one and only
  3. purpose - to simulate any sql driver behavior in tests, without needing a real
  4. database connection. It helps to maintain correct **TDD** workflow.
  5. It does not require any modifications to your source code in order to test
  6. and mock database operations. Supports concurrency and multiple database mocking.
  7. The driver allows to mock any sql driver method behavior.
  8. */
  9. package sqlmock
  10. import (
  11. "database/sql"
  12. "database/sql/driver"
  13. "fmt"
  14. "regexp"
  15. )
  16. // Sqlmock interface serves to create expectations
  17. // for any kind of database action in order to mock
  18. // and test real database behavior.
  19. type Sqlmock interface {
  20. // ExpectClose queues an expectation for this database
  21. // action to be triggered. the *ExpectedClose allows
  22. // to mock database response
  23. ExpectClose() *ExpectedClose
  24. // ExpectationsWereMet checks whether all queued expectations
  25. // were met in order. If any of them was not met - an error is returned.
  26. ExpectationsWereMet() error
  27. // ExpectPrepare expects Prepare() to be called with sql query
  28. // which match sqlRegexStr given regexp.
  29. // the *ExpectedPrepare allows to mock database response.
  30. // Note that you may expect Query() or Exec() on the *ExpectedPrepare
  31. // statement to prevent repeating sqlRegexStr
  32. ExpectPrepare(sqlRegexStr string) *ExpectedPrepare
  33. // ExpectQuery expects Query() or QueryRow() to be called with sql query
  34. // which match sqlRegexStr given regexp.
  35. // the *ExpectedQuery allows to mock database response.
  36. ExpectQuery(sqlRegexStr string) *ExpectedQuery
  37. // ExpectExec expects Exec() to be called with sql query
  38. // which match sqlRegexStr given regexp.
  39. // the *ExpectedExec allows to mock database response
  40. ExpectExec(sqlRegexStr string) *ExpectedExec
  41. // ExpectBegin expects *sql.DB.Begin to be called.
  42. // the *ExpectedBegin allows to mock database response
  43. ExpectBegin() *ExpectedBegin
  44. // ExpectCommit expects *sql.Tx.Commit to be called.
  45. // the *ExpectedCommit allows to mock database response
  46. ExpectCommit() *ExpectedCommit
  47. // ExpectRollback expects *sql.Tx.Rollback to be called.
  48. // the *ExpectedRollback allows to mock database response
  49. ExpectRollback() *ExpectedRollback
  50. // MatchExpectationsInOrder gives an option whether to match all
  51. // expectations in the order they were set or not.
  52. //
  53. // By default it is set to - true. But if you use goroutines
  54. // to parallelize your query executation, that option may
  55. // be handy.
  56. MatchExpectationsInOrder(bool)
  57. }
  58. type sqlmock struct {
  59. ordered bool
  60. dsn string
  61. opened int
  62. drv *mockDriver
  63. expected []expectation
  64. }
  65. func (c *sqlmock) open() (*sql.DB, Sqlmock, error) {
  66. db, err := sql.Open("sqlmock", c.dsn)
  67. if err != nil {
  68. return db, c, err
  69. }
  70. return db, c, db.Ping()
  71. }
  72. func (c *sqlmock) ExpectClose() *ExpectedClose {
  73. e := &ExpectedClose{}
  74. c.expected = append(c.expected, e)
  75. return e
  76. }
  77. func (c *sqlmock) MatchExpectationsInOrder(b bool) {
  78. c.ordered = b
  79. }
  80. // Close a mock database driver connection. It may or may not
  81. // be called depending on the sircumstances, but if it is called
  82. // there must be an *ExpectedClose expectation satisfied.
  83. // meets http://golang.org/pkg/database/sql/driver/#Conn interface
  84. func (c *sqlmock) Close() error {
  85. c.drv.Lock()
  86. defer c.drv.Unlock()
  87. c.opened--
  88. if c.opened == 0 {
  89. delete(c.drv.conns, c.dsn)
  90. }
  91. var expected *ExpectedClose
  92. var fulfilled int
  93. var ok bool
  94. for _, next := range c.expected {
  95. next.Lock()
  96. if next.fulfilled() {
  97. next.Unlock()
  98. fulfilled++
  99. continue
  100. }
  101. if expected, ok = next.(*ExpectedClose); ok {
  102. break
  103. }
  104. next.Unlock()
  105. if c.ordered {
  106. return fmt.Errorf("call to database Close, was not expected, next expectation is: %s", next)
  107. }
  108. }
  109. if expected == nil {
  110. msg := "call to database Close was not expected"
  111. if fulfilled == len(c.expected) {
  112. msg = "all expectations were already fulfilled, " + msg
  113. }
  114. return fmt.Errorf(msg)
  115. }
  116. expected.triggered = true
  117. expected.Unlock()
  118. return expected.err
  119. }
  120. func (c *sqlmock) ExpectationsWereMet() error {
  121. for _, e := range c.expected {
  122. if !e.fulfilled() {
  123. return fmt.Errorf("there is a remaining expectation which was not matched: %s", e)
  124. }
  125. }
  126. return nil
  127. }
  128. // Begin meets http://golang.org/pkg/database/sql/driver/#Conn interface
  129. func (c *sqlmock) Begin() (driver.Tx, error) {
  130. var expected *ExpectedBegin
  131. var ok bool
  132. var fulfilled int
  133. for _, next := range c.expected {
  134. next.Lock()
  135. if next.fulfilled() {
  136. next.Unlock()
  137. fulfilled++
  138. continue
  139. }
  140. if expected, ok = next.(*ExpectedBegin); ok {
  141. break
  142. }
  143. next.Unlock()
  144. if c.ordered {
  145. return nil, fmt.Errorf("call to database transaction Begin, was not expected, next expectation is: %s", next)
  146. }
  147. }
  148. if expected == nil {
  149. msg := "call to database transaction Begin was not expected"
  150. if fulfilled == len(c.expected) {
  151. msg = "all expectations were already fulfilled, " + msg
  152. }
  153. return nil, fmt.Errorf(msg)
  154. }
  155. expected.triggered = true
  156. expected.Unlock()
  157. return c, expected.err
  158. }
  159. func (c *sqlmock) ExpectBegin() *ExpectedBegin {
  160. e := &ExpectedBegin{}
  161. c.expected = append(c.expected, e)
  162. return e
  163. }
  164. // Exec meets http://golang.org/pkg/database/sql/driver/#Execer
  165. func (c *sqlmock) Exec(query string, args []driver.Value) (res driver.Result, err error) {
  166. query = stripQuery(query)
  167. var expected *ExpectedExec
  168. var fulfilled int
  169. var ok bool
  170. for _, next := range c.expected {
  171. next.Lock()
  172. if next.fulfilled() {
  173. next.Unlock()
  174. fulfilled++
  175. continue
  176. }
  177. if c.ordered {
  178. if expected, ok = next.(*ExpectedExec); ok {
  179. break
  180. }
  181. next.Unlock()
  182. return nil, fmt.Errorf("call to exec query '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
  183. }
  184. if exec, ok := next.(*ExpectedExec); ok {
  185. if err := exec.attemptMatch(query, args); err == nil {
  186. expected = exec
  187. break
  188. }
  189. }
  190. next.Unlock()
  191. }
  192. if expected == nil {
  193. msg := "call to exec '%s' query with args %+v was not expected"
  194. if fulfilled == len(c.expected) {
  195. msg = "all expectations were already fulfilled, " + msg
  196. }
  197. return nil, fmt.Errorf(msg, query, args)
  198. }
  199. defer expected.Unlock()
  200. if !expected.queryMatches(query) {
  201. return nil, fmt.Errorf("exec query '%s', does not match regex '%s'", query, expected.sqlRegex.String())
  202. }
  203. if err := expected.argsMatches(args); err != nil {
  204. return nil, fmt.Errorf("exec query '%s', arguments do not match: %s", query, err)
  205. }
  206. expected.triggered = true
  207. if expected.err != nil {
  208. return nil, expected.err // mocked to return error
  209. }
  210. if expected.result == nil {
  211. return nil, fmt.Errorf("exec query '%s' with args %+v, must return a database/sql/driver.result, but it was not set for expectation %T as %+v", query, args, expected, expected)
  212. }
  213. return expected.result, err
  214. }
  215. func (c *sqlmock) ExpectExec(sqlRegexStr string) *ExpectedExec {
  216. e := &ExpectedExec{}
  217. e.sqlRegex = regexp.MustCompile(sqlRegexStr)
  218. c.expected = append(c.expected, e)
  219. return e
  220. }
  221. // Prepare meets http://golang.org/pkg/database/sql/driver/#Conn interface
  222. func (c *sqlmock) Prepare(query string) (driver.Stmt, error) {
  223. var expected *ExpectedPrepare
  224. var fulfilled int
  225. var ok bool
  226. for _, next := range c.expected {
  227. next.Lock()
  228. if next.fulfilled() {
  229. next.Unlock()
  230. fulfilled++
  231. continue
  232. }
  233. if expected, ok = next.(*ExpectedPrepare); ok {
  234. break
  235. }
  236. next.Unlock()
  237. if c.ordered {
  238. return nil, fmt.Errorf("call to Prepare statement with query '%s', was not expected, next expectation is: %s", query, next)
  239. }
  240. }
  241. query = stripQuery(query)
  242. if expected == nil {
  243. msg := "call to Prepare '%s' query was not expected"
  244. if fulfilled == len(c.expected) {
  245. msg = "all expectations were already fulfilled, " + msg
  246. }
  247. return nil, fmt.Errorf(msg, query)
  248. }
  249. expected.triggered = true
  250. expected.Unlock()
  251. return &statement{c, query, expected.closeErr}, expected.err
  252. }
  253. func (c *sqlmock) ExpectPrepare(sqlRegexStr string) *ExpectedPrepare {
  254. e := &ExpectedPrepare{sqlRegex: regexp.MustCompile(sqlRegexStr), mock: c}
  255. c.expected = append(c.expected, e)
  256. return e
  257. }
  258. // Query meets http://golang.org/pkg/database/sql/driver/#Queryer
  259. func (c *sqlmock) Query(query string, args []driver.Value) (rw driver.Rows, err error) {
  260. query = stripQuery(query)
  261. var expected *ExpectedQuery
  262. var fulfilled int
  263. var ok bool
  264. for _, next := range c.expected {
  265. next.Lock()
  266. if next.fulfilled() {
  267. next.Unlock()
  268. fulfilled++
  269. continue
  270. }
  271. if c.ordered {
  272. if expected, ok = next.(*ExpectedQuery); ok {
  273. break
  274. }
  275. next.Unlock()
  276. return nil, fmt.Errorf("call to query '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
  277. }
  278. if qr, ok := next.(*ExpectedQuery); ok {
  279. if err := qr.attemptMatch(query, args); err == nil {
  280. expected = qr
  281. break
  282. }
  283. }
  284. next.Unlock()
  285. }
  286. if expected == nil {
  287. msg := "call to query '%s' with args %+v was not expected"
  288. if fulfilled == len(c.expected) {
  289. msg = "all expectations were already fulfilled, " + msg
  290. }
  291. return nil, fmt.Errorf(msg, query, args)
  292. }
  293. defer expected.Unlock()
  294. if !expected.queryMatches(query) {
  295. return nil, fmt.Errorf("query '%s', does not match regex [%s]", query, expected.sqlRegex.String())
  296. }
  297. if err := expected.argsMatches(args); err != nil {
  298. return nil, fmt.Errorf("exec query '%s', arguments do not match: %s", query, err)
  299. }
  300. expected.triggered = true
  301. if expected.err != nil {
  302. return nil, expected.err // mocked to return error
  303. }
  304. if expected.rows == nil {
  305. return nil, fmt.Errorf("query '%s' with args %+v, must return a database/sql/driver.rows, but it was not set for expectation %T as %+v", query, args, expected, expected)
  306. }
  307. return expected.rows, err
  308. }
  309. func (c *sqlmock) ExpectQuery(sqlRegexStr string) *ExpectedQuery {
  310. e := &ExpectedQuery{}
  311. e.sqlRegex = regexp.MustCompile(sqlRegexStr)
  312. c.expected = append(c.expected, e)
  313. return e
  314. }
  315. func (c *sqlmock) ExpectCommit() *ExpectedCommit {
  316. e := &ExpectedCommit{}
  317. c.expected = append(c.expected, e)
  318. return e
  319. }
  320. func (c *sqlmock) ExpectRollback() *ExpectedRollback {
  321. e := &ExpectedRollback{}
  322. c.expected = append(c.expected, e)
  323. return e
  324. }
  325. // Commit meets http://golang.org/pkg/database/sql/driver/#Tx
  326. func (c *sqlmock) Commit() error {
  327. var expected *ExpectedCommit
  328. var fulfilled int
  329. var ok bool
  330. for _, next := range c.expected {
  331. next.Lock()
  332. if next.fulfilled() {
  333. next.Unlock()
  334. fulfilled++
  335. continue
  336. }
  337. if expected, ok = next.(*ExpectedCommit); ok {
  338. break
  339. }
  340. next.Unlock()
  341. if c.ordered {
  342. return fmt.Errorf("call to commit transaction, was not expected, next expectation is: %s", next)
  343. }
  344. }
  345. if expected == nil {
  346. msg := "call to commit transaction was not expected"
  347. if fulfilled == len(c.expected) {
  348. msg = "all expectations were already fulfilled, " + msg
  349. }
  350. return fmt.Errorf(msg)
  351. }
  352. expected.triggered = true
  353. expected.Unlock()
  354. return expected.err
  355. }
  356. // Rollback meets http://golang.org/pkg/database/sql/driver/#Tx
  357. func (c *sqlmock) Rollback() error {
  358. var expected *ExpectedRollback
  359. var fulfilled int
  360. var ok bool
  361. for _, next := range c.expected {
  362. next.Lock()
  363. if next.fulfilled() {
  364. next.Unlock()
  365. fulfilled++
  366. continue
  367. }
  368. if expected, ok = next.(*ExpectedRollback); ok {
  369. break
  370. }
  371. next.Unlock()
  372. if c.ordered {
  373. return fmt.Errorf("call to rollback transaction, was not expected, next expectation is: %s", next)
  374. }
  375. }
  376. if expected == nil {
  377. msg := "call to rollback transaction was not expected"
  378. if fulfilled == len(c.expected) {
  379. msg = "all expectations were already fulfilled, " + msg
  380. }
  381. return fmt.Errorf(msg)
  382. }
  383. expected.triggered = true
  384. expected.Unlock()
  385. return expected.err
  386. }