説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

test_leobject.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. """
  2. Tests for _LeObject and LeObject
  3. """
  4. import unittest
  5. from unittest import TestCase
  6. from unittest.mock import patch
  7. import EditorialModel
  8. import leobject
  9. import leobject.test.utils
  10. from leobject.leobject import _LeObject
  11. ## Testing static methods that don't need the generated code
  12. class _LeObjectTestCase(TestCase):
  13. def test_split_query_filter(self):
  14. """ Tests the _split_filter() classmethod """
  15. query_results = {
  16. 'Hello = world' : ('Hello', '=', 'world'),
  17. 'hello <= "world"': ('hello', '<=', '"world"'),
  18. '_he42_ll-o >= \'world"': ('_he42_ll-o', '>=', '\'world"'),
  19. 'foo in ["foo", 42, \'bar\']': ('foo', ' in ', '["foo", 42, \'bar\']'),
  20. ' bar42 < 42': ('bar42', '<', '42'),
  21. ' _hidden > 1337': ('_hidden', '>', '1337'),
  22. '_42 not in foobar': ('_42', ' not in ', 'foobar'),
  23. 'hello in foo':('hello', ' in ', 'foo'),
  24. "\t\t\thello\t\t\nin\nfoo\t\t\n\t":('hello', ' in ', 'foo'),
  25. "hello \nnot\tin \nfoo":('hello', ' not in ', 'foo'),
  26. 'hello != bar':('hello', '!=', 'bar'),
  27. 'hello = "world>= <= != in not in"': ('hello', '=', '"world>= <= != in not in"'),
  28. 'superior.parent = 13': ('superior.parent', '=', '13'),
  29. }
  30. for query, result in query_results.items():
  31. res = _LeObject._split_filter(query)
  32. self.assertEqual(res, result, "When parsing the query : '%s' the returned value is different from the expected '%s'"%(query, result))
  33. def test_invalid_split_query_filter(self):
  34. """ Testing the _split_filter() method with invalid queries """
  35. invalid_queries = [
  36. '42 = 42',
  37. '4hello = foo',
  38. 'foo == bar',
  39. 'hello >> world',
  40. 'hello = ',
  41. ' = world',
  42. '=',
  43. '42',
  44. '"hello" = world',
  45. 'foo.bar = 15',
  46. ]
  47. for query in invalid_queries:
  48. with self.assertRaises(ValueError, msg='But the query was not valid : "%s"'%query):
  49. _LeObject._split_filter(query)
  50. ## Testing methods that need the generated code
  51. class LeObjectTestCase(TestCase):
  52. @classmethod
  53. def setUpClass(cls):
  54. """ Write the generated code in a temporary directory and import it """
  55. cls.tmpdir = leobject.test.utils.tmp_load_factory_code()
  56. @classmethod
  57. def tearDownClass(cls):
  58. """ Remove the temporary directory created at class setup """
  59. leobject.test.utils.cleanup(cls.tmpdir)
  60. def test_uid2leobj(self):
  61. """ Testing _Leobject.uid2leobj() """
  62. import dyncode
  63. for i in dyncode.LeObject._me_uid.keys():
  64. cls = dyncode.LeObject.uid2leobj(i)
  65. if leobject.letype.LeType in cls.__bases__:
  66. self.assertEqual(i, cls._type_id)
  67. elif leobject.leclass.LeClass in cls.__bases__:
  68. self.assertEqual(i, cls._class_id)
  69. else:
  70. self.fail("Bad value returned : '%s'"%cls)
  71. i=10
  72. while i in dyncode.LeObject._me_uid.keys():
  73. i+=1
  74. with self.assertRaises(KeyError):
  75. dyncode.LeObject.uid2leobj(i)
  76. def test_prepare_targets(self):
  77. """ Testing _prepare_targets() method """
  78. from dyncode import Publication, Numero, LeObject
  79. test_v = {
  80. (None, None) : (None, None),
  81. (Publication, Numero): (Publication, Numero),
  82. (Publication, None): (Publication, None),
  83. (None, Numero): (Publication, Numero),
  84. (Publication,'Numero'): (Publication, Numero),
  85. ('Publication', Numero): (Publication, Numero),
  86. ('Publication', 'Numero'): (Publication, Numero),
  87. ('Publication', None): (Publication, None),
  88. (None, 'Numero'): (Publication, Numero),
  89. }
  90. for (leclass, letype), (rleclass, rletype) in test_v.items():
  91. self.assertEqual((rletype,rleclass), LeObject._prepare_targets(letype, leclass))
  92. def test_invalid_prepare_targets(self):
  93. """ Testing _prepare_targets() method with invalid arguments """
  94. from dyncode import Publication, Numero, LeObject, Personnes
  95. test_v = [
  96. ('',''),
  97. (Personnes, Numero),
  98. (leobject.leclass.LeClass, Numero),
  99. (Publication, leobject.letype.LeType),
  100. ('foobar', Numero),
  101. (Publication, 'foobar'),
  102. (Numero, Numero),
  103. (Publication, Publication),
  104. (None, Publication),
  105. ('foobar', 'foobar'),
  106. (42,1337),
  107. (type, Numero),
  108. (LeObject, Numero),
  109. (LeObject, LeObject),
  110. (Publication, LeObject),
  111. ]
  112. for (leclass, letype) in test_v:
  113. with self.assertRaises(ValueError):
  114. LeObject._prepare_targets(letype, leclass)
  115. def test_check_fields(self):
  116. """ Testing the _check_fields() method """
  117. from dyncode import Publication, Numero, LeObject, Personnes
  118. #Valid fields given
  119. LeObject._check_fields(None, Publication, Publication._fieldtypes.keys())
  120. LeObject._check_fields(Numero, None, Numero._fields)
  121. #Specials fields
  122. LeObject._check_fields(Numero, Publication, ['lodel_id'])
  123. #Common fields
  124. LeObject._check_fields(None, None, EditorialModel.classtypes.common_fields.keys())
  125. #Invalid fields
  126. with self.assertRaises(leobject.leobject.LeObjectQueryError):
  127. LeObject._check_fields(None, None, Numero._fields)
  128. def test_prepare_filters(self):
  129. """ Testing the _prepare_filters() method """
  130. from dyncode import Publication, Numero, LeObject, Personnes
  131. #Simple filters
  132. filters = [
  133. 'lodel_id = 1',
  134. 'superior.parent > 2'
  135. ]
  136. filt, rfilt = LeObject._prepare_filters(filters, Numero, None)
  137. self.assertEqual(filt, [('lodel_id', '=', '1')])
  138. self.assertEqual(rfilt, [((leobject.leobject.REL_SUP,'parent'), '>', '2')])
  139. #All fields, no relationnal and class given
  140. filters = []
  141. res_filt = []
  142. for field in Numero._fields:
  143. filters.append('%s=1'%field)
  144. res_filt.append((field, '=', '1'))
  145. filt, rfilt = LeObject._prepare_filters(filters, None, Publication)
  146. self.assertEqual(rfilt, [])
  147. self.assertEqual(filt, res_filt)
  148. #Mixed type filters (tuple and string)
  149. filters = [
  150. ('lodel_id', '<=', '0'),
  151. 'subordinate.parent = 2',
  152. ]
  153. filt, rfilt = LeObject._prepare_filters(filters, Numero, None)
  154. self.assertEqual(filt, [('lodel_id', '<=', '0')])
  155. self.assertEqual(rfilt, [((leobject.leobject.REL_SUB,'parent'), '=', '2')])
  156. def test_prepare_filters_invalid(self):
  157. """ Testing the _prepare_filters() method """
  158. from dyncode import Publication, Numero, LeObject, Personnes
  159. #Numero fields filters but no letype nor leclass given
  160. filters = []
  161. res_filt = []
  162. for field in Numero._fields:
  163. filters.append('%s=1'%field)
  164. res_filt.append((field, '=', '1'))
  165. with self.assertRaises(leobject.leobject.LeObjectQueryError):
  166. LeObject._prepare_filters(filters, None, None)
  167. #simply invalid filters
  168. filters = ['hello world !']
  169. with self.assertRaises(ValueError):
  170. LeObject._prepare_filters(filters, None, None)
  171. class LeObjectMockDatasourceTestCase(TestCase):
  172. """ Testing _LeObject using a mock on the datasource """
  173. @classmethod
  174. def setUpClass(cls):
  175. """ Write the generated code in a temporary directory and import it """
  176. cls.tmpdir = leobject.test.utils.tmp_load_factory_code()
  177. @classmethod
  178. def tearDownClass(cls):
  179. """ Remove the temporary directory created at class setup """
  180. leobject.test.utils.cleanup(cls.tmpdir)
  181. @patch('leobject.datasources.dummy.DummyDatasource.insert')
  182. def test_insert(self, dsmock):
  183. from dyncode import Publication, Numero, LeObject
  184. ndatas = [
  185. [{'titre' : 'FooBar'}],
  186. [{'titre':'hello'},{'titre':'world'}],
  187. ]
  188. for ndats in ndatas:
  189. LeObject.insert(Numero,ndats)
  190. dsmock.assert_called_once_with(Numero, Publication, ndats)
  191. dsmock.reset_mock()
  192. LeObject.insert('Numero',ndats)
  193. dsmock.assert_called_once_with(Numero, Publication, ndats)
  194. dsmock.reset_mock()
  195. @patch('leobject.datasources.dummy.DummyDatasource.update')
  196. def test_update(self, dsmock):
  197. from dyncode import Publication, Numero, LeObject
  198. args = [
  199. ( ['lodel_id = 1'],
  200. {'titre':'foobar'},
  201. [('lodel_id','=','1')],
  202. []
  203. ),
  204. ( ['superior.parent in [1,2,3,4,5,6]', 'titre != "FooBar"'],
  205. {'titre':'FooBar'},
  206. [( 'titre','!=','"FooBar"')],
  207. [( (leobject.leobject.REL_SUP, 'parent') ,' in ', '[1,2,3,4,5,6]')]
  208. ),
  209. ]
  210. for filters, datas, ds_filters, ds_relfilters in args:
  211. LeObject.update(Numero, filters, datas)
  212. dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
  213. dsmock.reset_mock()
  214. LeObject.update('Numero', filters, datas)
  215. dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
  216. dsmock.reset_mock()
  217. @patch('leobject.datasources.dummy.DummyDatasource.delete')
  218. def test_delete(self, dsmock):
  219. from dyncode import Publication, Numero, LeObject
  220. args = [
  221. (
  222. ['lodel_id=1'],
  223. [('lodel_id', '=', '1')],
  224. []
  225. ),
  226. (
  227. ['subordinate.parent not in [1,2,3]', 'titre = "titre nul"'],
  228. [('titre','=', '"titre nul"')],
  229. [( (leobject.leobject.REL_SUB, 'parent'), ' not in ', '[1,2,3]')]
  230. ),
  231. ]
  232. for filters, ds_filters, ds_relfilters in args:
  233. LeObject.delete(Numero, filters)
  234. dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters)
  235. dsmock.reset_mock()
  236. LeObject.delete('Numero', filters)
  237. dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters)
  238. dsmock.reset_mock()
  239. @patch('leobject.datasources.dummy.DummyDatasource.get')
  240. def test_get(self, dsmock):
  241. from dyncode import Publication, Numero, LeObject
  242. args = [
  243. (
  244. ['lodel_id', 'superior.parent'],
  245. ['titre != "foobar"'],
  246. ['lodel_id', (leobject.leobject.REL_SUP, 'parent')],
  247. [('titre','!=', '"foobar"')],
  248. []
  249. ),
  250. (
  251. ['lodel_id', 'titre', 'superior.parent', 'subordinate.translation'],
  252. ['superior.parent in [1,2,3,4,5]'],
  253. ['lodel_id', 'titre', (leobject.leobject.REL_SUP,'parent'), (leobject.leobject.REL_SUB, 'translation')],
  254. [],
  255. [( (leobject.leobject.REL_SUP, 'parent'), ' in ', '[1,2,3,4,5]')]
  256. ),
  257. (
  258. [],
  259. [],
  260. Numero._fields,
  261. [],
  262. []
  263. ),
  264. ]
  265. for field_list, filters, fl_ds, filters_ds, rfilters_ds in args:
  266. LeObject.get(filters, field_list, Numero)
  267. dsmock.assert_called_with(Publication, Numero, fl_ds, filters_ds, rfilters_ds)
  268. dsmock.reset_mock()
  269. @patch('leobject.datasources.dummy.DummyDatasource.get')
  270. def test_get_incomplete_targer(self, dsmock):
  271. """ Testing LeObject.get() method with partial target specifier """
  272. from dyncode import Publication, Numero, LeObject
  273. args = [
  274. (
  275. ['lodel_id'],
  276. [],
  277. None,
  278. None,
  279. ['lodel_id', 'type_id'],
  280. [],
  281. [],
  282. None,
  283. None
  284. ),
  285. (
  286. [],
  287. [],
  288. None,
  289. None,
  290. list(EditorialModel.classtypes.common_fields.keys()),
  291. [],
  292. [],
  293. None,
  294. None
  295. ),
  296. (
  297. ['lodel_id'],
  298. [],
  299. None,
  300. Publication,
  301. ['lodel_id', 'type_id'],
  302. [],
  303. [],
  304. None,
  305. Publication
  306. ),
  307. (
  308. [],
  309. [],
  310. Numero,
  311. None,
  312. Numero._fields,
  313. [],
  314. [],
  315. Numero,
  316. Publication
  317. ),
  318. ]
  319. for field_list, filters, letype, leclass, fl_ds, filters_ds, rfilters_ds, letype_ds, leclass_ds in args:
  320. LeObject.get(filters, field_list, letype, leclass)
  321. dsmock.assert_called_with(leclass_ds, letype_ds, fl_ds, filters_ds, rfilters_ds)
  322. dsmock.reset_mock()