PageRenderTime 68ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/trac/db/tests/api.py

https://github.com/mikkorantalainen/trac
Python | 347 lines | 300 code | 40 blank | 7 comment | 40 complexity | 0c580a89c2ba77dcd994b59686748c70 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import unittest
  4. from trac.db.api import _parse_db_str, with_transaction
  5. from trac.test import EnvironmentStub, Mock
  6. class Connection(object):
  7. committed = False
  8. rolledback = False
  9. def commit(self):
  10. self.committed = True
  11. def rollback(self):
  12. self.rolledback = True
  13. class Error(Exception):
  14. pass
  15. class WithTransactionTest(unittest.TestCase):
  16. def test_successful_transaction(self):
  17. db = Connection()
  18. env = Mock(get_db_cnx=lambda: db)
  19. @with_transaction(env)
  20. def do_transaction(db):
  21. self.assertTrue(not db.committed and not db.rolledback)
  22. self.assertTrue(db.committed and not db.rolledback)
  23. def test_failed_transaction(self):
  24. db = Connection()
  25. env = Mock(get_db_cnx=lambda: db)
  26. try:
  27. @with_transaction(env)
  28. def do_transaction(db):
  29. self.assertTrue(not db.committed and not db.rolledback)
  30. raise Error()
  31. self.fail()
  32. except Error:
  33. pass
  34. self.assertTrue(not db.committed and db.rolledback)
  35. def test_implicit_nesting_success(self):
  36. env = Mock(get_db_cnx=lambda: Connection())
  37. dbs = [None, None]
  38. @with_transaction(env)
  39. def level0(db):
  40. dbs[0] = db
  41. @with_transaction(env)
  42. def level1(db):
  43. dbs[1] = db
  44. self.assertTrue(not db.committed and not db.rolledback)
  45. self.assertTrue(not db.committed and not db.rolledback)
  46. self.assertTrue(dbs[0] is not None)
  47. self.assertTrue(dbs[0] is dbs[1])
  48. self.assertTrue(dbs[0].committed and not dbs[0].rolledback)
  49. def test_implicit_nesting_failure(self):
  50. env = Mock(get_db_cnx=lambda: Connection())
  51. dbs = [None, None]
  52. try:
  53. @with_transaction(env)
  54. def level0(db):
  55. dbs[0] = db
  56. try:
  57. @with_transaction(env)
  58. def level1(db):
  59. dbs[1] = db
  60. self.assertTrue(not db.committed and not db.rolledback)
  61. raise Error()
  62. self.fail()
  63. except Error:
  64. self.assertTrue(not db.committed and not db.rolledback)
  65. raise
  66. self.fail()
  67. except Error:
  68. pass
  69. self.assertTrue(dbs[0] is not None)
  70. self.assertTrue(dbs[0] is dbs[1])
  71. self.assertTrue(not dbs[0].committed and dbs[0].rolledback)
  72. def test_explicit_success(self):
  73. db = Connection()
  74. env = Mock(get_db_cnx=lambda: None)
  75. @with_transaction(env, db)
  76. def do_transaction(idb):
  77. self.assertTrue(idb is db)
  78. self.assertTrue(not db.committed and not db.rolledback)
  79. self.assertTrue(not db.committed and not db.rolledback)
  80. def test_explicit_failure(self):
  81. db = Connection()
  82. env = Mock(get_db_cnx=lambda: None)
  83. try:
  84. @with_transaction(env, db)
  85. def do_transaction(idb):
  86. self.assertTrue(idb is db)
  87. self.assertTrue(not db.committed and not db.rolledback)
  88. raise Error()
  89. self.fail()
  90. except Error:
  91. pass
  92. self.assertTrue(not db.committed and not db.rolledback)
  93. def test_implicit_in_explicit_success(self):
  94. db = Connection()
  95. env = Mock(get_db_cnx=lambda: Connection())
  96. dbs = [None, None]
  97. @with_transaction(env, db)
  98. def level0(db):
  99. dbs[0] = db
  100. @with_transaction(env)
  101. def level1(db):
  102. dbs[1] = db
  103. self.assertTrue(not db.committed and not db.rolledback)
  104. self.assertTrue(not db.committed and not db.rolledback)
  105. self.assertTrue(dbs[0] is not None)
  106. self.assertTrue(dbs[0] is dbs[1])
  107. self.assertTrue(not dbs[0].committed and not dbs[0].rolledback)
  108. def test_implicit_in_explicit_failure(self):
  109. db = Connection()
  110. env = Mock(get_db_cnx=lambda: Connection())
  111. dbs = [None, None]
  112. try:
  113. @with_transaction(env, db)
  114. def level0(db):
  115. dbs[0] = db
  116. @with_transaction(env)
  117. def level1(db):
  118. dbs[1] = db
  119. self.assertTrue(not db.committed and not db.rolledback)
  120. raise Error()
  121. self.fail()
  122. self.fail()
  123. except Error:
  124. pass
  125. self.assertTrue(dbs[0] is not None)
  126. self.assertTrue(dbs[0] is dbs[1])
  127. self.assertTrue(not dbs[0].committed and not dbs[0].rolledback)
  128. def test_explicit_in_implicit_success(self):
  129. db = Connection()
  130. env = Mock(get_db_cnx=lambda: Connection())
  131. dbs = [None, None]
  132. @with_transaction(env)
  133. def level0(db):
  134. dbs[0] = db
  135. @with_transaction(env, db)
  136. def level1(db):
  137. dbs[1] = db
  138. self.assertTrue(not db.committed and not db.rolledback)
  139. self.assertTrue(not db.committed and not db.rolledback)
  140. self.assertTrue(dbs[0] is not None)
  141. self.assertTrue(dbs[0] is dbs[1])
  142. self.assertTrue(dbs[0].committed and not dbs[0].rolledback)
  143. def test_explicit_in_implicit_failure(self):
  144. db = Connection()
  145. env = Mock(get_db_cnx=lambda: Connection())
  146. dbs = [None, None]
  147. try:
  148. @with_transaction(env)
  149. def level0(db):
  150. dbs[0] = db
  151. @with_transaction(env, db)
  152. def level1(db):
  153. dbs[1] = db
  154. self.assertTrue(not db.committed and not db.rolledback)
  155. raise Error()
  156. self.fail()
  157. self.fail()
  158. except Error:
  159. pass
  160. self.assertTrue(dbs[0] is not None)
  161. self.assertTrue(dbs[0] is dbs[1])
  162. self.assertTrue(not dbs[0].committed and dbs[0].rolledback)
  163. def test_invalid_nesting(self):
  164. env = Mock(get_db_cnx=lambda: Connection())
  165. try:
  166. @with_transaction(env)
  167. def level0(db):
  168. @with_transaction(env, Connection())
  169. def level1(db):
  170. raise Error()
  171. raise Error()
  172. raise Error()
  173. except AssertionError:
  174. pass
  175. class ParseConnectionStringTestCase(unittest.TestCase):
  176. def test_sqlite_relative(self):
  177. # Default syntax for specifying DB path relative to the environment
  178. # directory
  179. self.assertEqual(('sqlite', {'path': 'db/trac.db'}),
  180. _parse_db_str('sqlite:db/trac.db'))
  181. def test_sqlite_absolute(self):
  182. # Standard syntax
  183. self.assertEqual(('sqlite', {'path': '/var/db/trac.db'}),
  184. _parse_db_str('sqlite:///var/db/trac.db'))
  185. # Legacy syntax
  186. self.assertEqual(('sqlite', {'path': '/var/db/trac.db'}),
  187. _parse_db_str('sqlite:/var/db/trac.db'))
  188. def test_sqlite_with_timeout_param(self):
  189. # In-memory database
  190. self.assertEqual(('sqlite', {'path': 'db/trac.db',
  191. 'params': {'timeout': '10000'}}),
  192. _parse_db_str('sqlite:db/trac.db?timeout=10000'))
  193. def test_sqlite_windows_path(self):
  194. # In-memory database
  195. os_name = os.name
  196. try:
  197. os.name = 'nt'
  198. self.assertEqual(('sqlite', {'path': 'C:/project/db/trac.db'}),
  199. _parse_db_str('sqlite:C|/project/db/trac.db'))
  200. finally:
  201. os.name = os_name
  202. def test_postgres_simple(self):
  203. self.assertEqual(('postgres', {'host': 'localhost', 'path': '/trac'}),
  204. _parse_db_str('postgres://localhost/trac'))
  205. def test_postgres_with_port(self):
  206. self.assertEqual(('postgres', {'host': 'localhost', 'port': 9431,
  207. 'path': '/trac'}),
  208. _parse_db_str('postgres://localhost:9431/trac'))
  209. def test_postgres_with_creds(self):
  210. self.assertEqual(('postgres', {'user': 'john', 'password': 'letmein',
  211. 'host': 'localhost', 'port': 9431,
  212. 'path': '/trac'}),
  213. _parse_db_str('postgres://john:letmein@localhost:9431/trac'))
  214. def test_postgres_with_quoted_password(self):
  215. self.assertEqual(('postgres', {'user': 'john', 'password': ':@/',
  216. 'host': 'localhost', 'path': '/trac'}),
  217. _parse_db_str('postgres://john:%3a%40%2f@localhost/trac'))
  218. def test_mysql_simple(self):
  219. self.assertEqual(('mysql', {'host': 'localhost', 'path': '/trac'}),
  220. _parse_db_str('mysql://localhost/trac'))
  221. def test_mysql_with_creds(self):
  222. self.assertEqual(('mysql', {'user': 'john', 'password': 'letmein',
  223. 'host': 'localhost', 'port': 3306,
  224. 'path': '/trac'}),
  225. _parse_db_str('mysql://john:letmein@localhost:3306/trac'))
  226. class StringsTestCase(unittest.TestCase):
  227. def setUp(self):
  228. self.env = EnvironmentStub()
  229. def test_insert_unicode(self):
  230. db = self.env.get_db_cnx()
  231. cursor = db.cursor()
  232. cursor.execute('INSERT INTO system (name,value) VALUES (%s,%s)',
  233. ('test-unicode', u'ünicöde'))
  234. db.commit()
  235. cursor = db.cursor()
  236. cursor.execute("SELECT value FROM system WHERE name='test-unicode'")
  237. self.assertEqual([(u'ünicöde',)], cursor.fetchall())
  238. def test_insert_empty(self):
  239. from trac.util.text import empty
  240. db = self.env.get_db_cnx()
  241. cursor = db.cursor()
  242. cursor.execute('INSERT INTO system (name,value) VALUES (%s,%s)',
  243. ('test-empty', empty))
  244. db.commit()
  245. cursor = db.cursor()
  246. cursor.execute("SELECT value FROM system WHERE name='test-empty'")
  247. self.assertEqual([(u'',)], cursor.fetchall())
  248. def test_insert_markup(self):
  249. from genshi.core import Markup
  250. db = self.env.get_db_cnx()
  251. cursor = db.cursor()
  252. cursor.execute('INSERT INTO system (name,value) VALUES (%s,%s)',
  253. ('test-markup', Markup(u'<em>märkup</em>')))
  254. db.commit()
  255. cursor = db.cursor()
  256. cursor.execute("SELECT value FROM system WHERE name='test-markup'")
  257. self.assertEqual([(u'<em>märkup</em>',)], cursor.fetchall())
  258. class ConnectionTestCase(unittest.TestCase):
  259. def setUp(self):
  260. self.env = EnvironmentStub()
  261. self.db = self.env.get_db_cnx()
  262. def tearDown(self):
  263. self.env.reset_db()
  264. def test_get_last_id(self):
  265. c = self.db.cursor()
  266. q = "INSERT INTO report (author) VALUES ('anonymous')"
  267. c.execute(q)
  268. # Row ID correct before...
  269. id1 = self.db.get_last_id(c, 'report')
  270. self.assertNotEqual(0, id1)
  271. self.db.commit()
  272. c.execute(q)
  273. self.db.commit()
  274. # ... and after commit()
  275. id2 = self.db.get_last_id(c, 'report')
  276. self.assertEqual(id1 + 1, id2)
  277. def test_update_sequence(self):
  278. cursor = self.db.cursor()
  279. cursor.execute("""
  280. INSERT INTO report (id, author) VALUES (42, 'anonymous')
  281. """)
  282. self.db.commit()
  283. self.db.update_sequence(cursor, 'report', 'id')
  284. self.db.commit()
  285. cursor.execute("INSERT INTO report (author) VALUES ('next-id')")
  286. self.db.commit()
  287. cursor.execute("SELECT id FROM report WHERE author='next-id'")
  288. self.assertEqual(43, cursor.fetchall()[0][0])
  289. def suite():
  290. suite = unittest.TestSuite()
  291. suite.addTest(unittest.makeSuite(ParseConnectionStringTestCase, 'test'))
  292. suite.addTest(unittest.makeSuite(StringsTestCase, 'test'))
  293. suite.addTest(unittest.makeSuite(ConnectionTestCase, 'test'))
  294. suite.addTest(unittest.makeSuite(WithTransactionTest, 'test'))
  295. return suite
  296. if __name__ == '__main__':
  297. unittest.main(defaultTest='suite')