test_decorators.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. # -*- coding: utf-8 -*-
  2. """
  3. :codeauthor: Bo Maryniuk (bo@suse.de)
  4. unit.utils.decorators_test
  5. """
  6. # Import Python libs
  7. from __future__ import absolute_import, print_function, unicode_literals
  8. # Import Salt libs
  9. import salt.utils.decorators as decorators
  10. from salt.exceptions import CommandExecutionError, SaltConfigurationError
  11. from salt.version import SaltStackVersion
  12. from tests.support.mock import patch
  13. from tests.support.unit import TestCase
  14. class DummyLogger(object):
  15. """
  16. Dummy logger accepts everything and simply logs
  17. """
  18. def __init__(self, messages):
  19. self._messages = messages
  20. def __getattr__(self, item):
  21. return self._log
  22. def _log(self, msg):
  23. self._messages.append(msg)
  24. class DecoratorsTest(TestCase):
  25. """
  26. Testing decorators.
  27. """
  28. def old_function(self):
  29. return "old"
  30. def new_function(self):
  31. return "new"
  32. def _new_function(self):
  33. return "old"
  34. def _mk_version(self, name):
  35. """
  36. Make a version
  37. :return:
  38. """
  39. return name, SaltStackVersion.from_name(name)
  40. def setUp(self):
  41. """
  42. Setup a test
  43. :return:
  44. """
  45. self.globs = {
  46. "__virtualname__": "test",
  47. "__opts__": {},
  48. "__pillar__": {},
  49. "old_function": self.old_function,
  50. "new_function": self.new_function,
  51. "_new_function": self._new_function,
  52. }
  53. self.addCleanup(delattr, self, "globs")
  54. self.messages = list()
  55. self.addCleanup(delattr, self, "messages")
  56. patcher = patch.object(decorators, "log", DummyLogger(self.messages))
  57. patcher.start()
  58. self.addCleanup(patcher.stop)
  59. def test_is_deprecated_version_eol(self):
  60. """
  61. Use of is_deprecated will result to the exception,
  62. if the expiration version is lower than the current version.
  63. A successor function is not pointed out.
  64. :return:
  65. """
  66. depr = decorators.is_deprecated(self.globs, "Helium")
  67. depr._curr_version = self._mk_version("Beryllium")[1]
  68. with self.assertRaises(CommandExecutionError):
  69. depr(self.old_function)()
  70. self.assertEqual(
  71. self.messages, ['The lifetime of the function "old_function" expired.']
  72. )
  73. def test_is_deprecated_with_successor_eol(self):
  74. """
  75. Use of is_deprecated will result to the exception,
  76. if the expiration version is lower than the current version.
  77. A successor function is pointed out.
  78. :return:
  79. """
  80. depr = decorators.is_deprecated(
  81. self.globs, "Helium", with_successor="new_function"
  82. )
  83. depr._curr_version = self._mk_version("Beryllium")[1]
  84. with self.assertRaises(CommandExecutionError):
  85. depr(self.old_function)()
  86. self.assertEqual(
  87. self.messages,
  88. [
  89. 'The lifetime of the function "old_function" expired. '
  90. 'Please use its successor "new_function" instead.'
  91. ],
  92. )
  93. def test_is_deprecated(self):
  94. """
  95. Use of is_deprecated will result to the log message,
  96. if the expiration version is higher than the current version.
  97. A successor function is not pointed out.
  98. :return:
  99. """
  100. depr = decorators.is_deprecated(self.globs, "Beryllium")
  101. depr._curr_version = self._mk_version("Helium")[1]
  102. self.assertEqual(depr(self.old_function)(), self.old_function())
  103. self.assertEqual(
  104. self.messages,
  105. [
  106. 'The function "old_function" is deprecated '
  107. 'and will expire in version "Beryllium".'
  108. ],
  109. )
  110. def test_is_deprecated_with_successor(self):
  111. """
  112. Use of is_deprecated will result to the log message,
  113. if the expiration version is higher than the current version.
  114. A successor function is pointed out.
  115. :return:
  116. """
  117. depr = decorators.is_deprecated(
  118. self.globs, "Beryllium", with_successor="old_function"
  119. )
  120. depr._curr_version = self._mk_version("Helium")[1]
  121. self.assertEqual(depr(self.old_function)(), self.old_function())
  122. self.assertEqual(
  123. self.messages,
  124. [
  125. 'The function "old_function" is deprecated '
  126. 'and will expire in version "Beryllium". '
  127. 'Use successor "old_function" instead.'
  128. ],
  129. )
  130. def test_with_deprecated_notfound(self):
  131. """
  132. Test with_deprecated should raise an exception, if a same name
  133. function with the "_" prefix not implemented.
  134. :return:
  135. """
  136. del self.globs["_new_function"]
  137. self.globs["__opts__"]["use_deprecated"] = ["test.new_function"]
  138. depr = decorators.with_deprecated(self.globs, "Beryllium")
  139. depr._curr_version = self._mk_version("Helium")[1]
  140. with self.assertRaises(CommandExecutionError):
  141. depr(self.new_function)()
  142. self.assertEqual(
  143. self.messages,
  144. [
  145. 'The function "test.new_function" is using its deprecated '
  146. 'version and will expire in version "Beryllium".'
  147. ],
  148. )
  149. def test_with_deprecated_notfound_in_pillar(self):
  150. """
  151. Test with_deprecated should raise an exception, if a same name
  152. function with the "_" prefix not implemented.
  153. :return:
  154. """
  155. del self.globs["_new_function"]
  156. self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"]
  157. depr = decorators.with_deprecated(self.globs, "Beryllium")
  158. depr._curr_version = self._mk_version("Helium")[1]
  159. with self.assertRaises(CommandExecutionError):
  160. depr(self.new_function)()
  161. self.assertEqual(
  162. self.messages,
  163. [
  164. 'The function "test.new_function" is using its deprecated '
  165. 'version and will expire in version "Beryllium".'
  166. ],
  167. )
  168. def test_with_deprecated_found(self):
  169. """
  170. Test with_deprecated should not raise an exception, if a same name
  171. function with the "_" prefix is implemented, but should use
  172. an old version instead, if "use_deprecated" is requested.
  173. :return:
  174. """
  175. self.globs["__opts__"]["use_deprecated"] = ["test.new_function"]
  176. self.globs["_new_function"] = self.old_function
  177. depr = decorators.with_deprecated(self.globs, "Beryllium")
  178. depr._curr_version = self._mk_version("Helium")[1]
  179. self.assertEqual(depr(self.new_function)(), self.old_function())
  180. log_msg = [
  181. 'The function "test.new_function" is using its deprecated version '
  182. 'and will expire in version "Beryllium".'
  183. ]
  184. self.assertEqual(self.messages, log_msg)
  185. def test_with_deprecated_found_in_pillar(self):
  186. """
  187. Test with_deprecated should not raise an exception, if a same name
  188. function with the "_" prefix is implemented, but should use
  189. an old version instead, if "use_deprecated" is requested.
  190. :return:
  191. """
  192. self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"]
  193. self.globs["_new_function"] = self.old_function
  194. depr = decorators.with_deprecated(self.globs, "Beryllium")
  195. depr._curr_version = self._mk_version("Helium")[1]
  196. self.assertEqual(depr(self.new_function)(), self.old_function())
  197. log_msg = [
  198. 'The function "test.new_function" is using its deprecated version '
  199. 'and will expire in version "Beryllium".'
  200. ]
  201. self.assertEqual(self.messages, log_msg)
  202. def test_with_deprecated_found_eol(self):
  203. """
  204. Test with_deprecated should raise an exception, if a same name
  205. function with the "_" prefix is implemented, "use_deprecated" is requested
  206. and EOL is reached.
  207. :return:
  208. """
  209. self.globs["__opts__"]["use_deprecated"] = ["test.new_function"]
  210. self.globs["_new_function"] = self.old_function
  211. depr = decorators.with_deprecated(self.globs, "Helium")
  212. depr._curr_version = self._mk_version("Beryllium")[1]
  213. with self.assertRaises(CommandExecutionError):
  214. depr(self.new_function)()
  215. self.assertEqual(
  216. self.messages,
  217. [
  218. 'Although function "new_function" is called, an alias "new_function" '
  219. "is configured as its deprecated version. The lifetime of the function "
  220. '"new_function" expired. Please use its successor "new_function" instead.'
  221. ],
  222. )
  223. def test_with_deprecated_found_eol_in_pillar(self):
  224. """
  225. Test with_deprecated should raise an exception, if a same name
  226. function with the "_" prefix is implemented, "use_deprecated" is requested
  227. and EOL is reached.
  228. :return:
  229. """
  230. self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"]
  231. self.globs["_new_function"] = self.old_function
  232. depr = decorators.with_deprecated(self.globs, "Helium")
  233. depr._curr_version = self._mk_version("Beryllium")[1]
  234. with self.assertRaises(CommandExecutionError):
  235. depr(self.new_function)()
  236. self.assertEqual(
  237. self.messages,
  238. [
  239. 'Although function "new_function" is called, an alias "new_function" '
  240. "is configured as its deprecated version. The lifetime of the function "
  241. '"new_function" expired. Please use its successor "new_function" instead.'
  242. ],
  243. )
  244. def test_with_deprecated_no_conf(self):
  245. """
  246. Test with_deprecated should not raise an exception, if a same name
  247. function with the "_" prefix is implemented, but should use
  248. a new version instead, if "use_deprecated" is not requested.
  249. :return:
  250. """
  251. self.globs["_new_function"] = self.old_function
  252. depr = decorators.with_deprecated(self.globs, "Beryllium")
  253. depr._curr_version = self._mk_version("Helium")[1]
  254. self.assertEqual(depr(self.new_function)(), self.new_function())
  255. self.assertFalse(self.messages)
  256. def test_with_deprecated_with_name(self):
  257. """
  258. Test with_deprecated should not raise an exception, if a different name
  259. function is implemented and specified with the "with_name" parameter,
  260. but should use an old version instead and log a warning log message.
  261. :return:
  262. """
  263. self.globs["__opts__"]["use_deprecated"] = ["test.new_function"]
  264. depr = decorators.with_deprecated(
  265. self.globs, "Beryllium", with_name="old_function"
  266. )
  267. depr._curr_version = self._mk_version("Helium")[1]
  268. self.assertEqual(depr(self.new_function)(), self.old_function())
  269. self.assertEqual(
  270. self.messages,
  271. [
  272. 'The function "old_function" is deprecated and will expire in version "Beryllium". '
  273. 'Use its successor "new_function" instead.'
  274. ],
  275. )
  276. def test_with_deprecated_with_name_eol(self):
  277. """
  278. Test with_deprecated should raise an exception, if a different name
  279. function is implemented and specified with the "with_name" parameter
  280. and EOL is reached.
  281. :return:
  282. """
  283. self.globs["__opts__"]["use_deprecated"] = ["test.new_function"]
  284. depr = decorators.with_deprecated(
  285. self.globs, "Helium", with_name="old_function"
  286. )
  287. depr._curr_version = self._mk_version("Beryllium")[1]
  288. with self.assertRaises(CommandExecutionError):
  289. depr(self.new_function)()
  290. self.assertEqual(
  291. self.messages,
  292. [
  293. 'Although function "new_function" is called, '
  294. 'an alias "old_function" is configured as its deprecated version. '
  295. 'The lifetime of the function "old_function" expired. '
  296. 'Please use its successor "new_function" instead.'
  297. ],
  298. )
  299. def test_with_deprecated_opt_in_default(self):
  300. """
  301. Test with_deprecated using opt-in policy,
  302. where newer function is not used, unless configured.
  303. :return:
  304. """
  305. depr = decorators.with_deprecated(
  306. self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN
  307. )
  308. depr._curr_version = self._mk_version("Helium")[1]
  309. assert depr(self.new_function)() == self.old_function()
  310. assert self.messages == [
  311. 'The function "test.new_function" is using its '
  312. 'deprecated version and will expire in version "Beryllium".'
  313. ]
  314. def test_with_deprecated_opt_in_use_superseded(self):
  315. """
  316. Test with_deprecated using opt-in policy,
  317. where newer function is used as per configuration.
  318. :return:
  319. """
  320. self.globs["__opts__"]["use_superseded"] = ["test.new_function"]
  321. depr = decorators.with_deprecated(
  322. self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN
  323. )
  324. depr._curr_version = self._mk_version("Helium")[1]
  325. assert depr(self.new_function)() == self.new_function()
  326. assert not self.messages
  327. def test_with_deprecated_opt_in_use_superseded_in_pillar(self):
  328. """
  329. Test with_deprecated using opt-in policy,
  330. where newer function is used as per configuration.
  331. :return:
  332. """
  333. self.globs["__pillar__"]["use_superseded"] = ["test.new_function"]
  334. depr = decorators.with_deprecated(
  335. self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN
  336. )
  337. depr._curr_version = self._mk_version("Helium")[1]
  338. assert depr(self.new_function)() == self.new_function()
  339. assert not self.messages
  340. def test_with_deprecated_opt_in_use_superseded_and_deprecated(self):
  341. """
  342. Test with_deprecated misconfiguration.
  343. :return:
  344. """
  345. self.globs["__opts__"]["use_deprecated"] = ["test.new_function"]
  346. self.globs["__opts__"]["use_superseded"] = ["test.new_function"]
  347. depr = decorators.with_deprecated(self.globs, "Beryllium")
  348. depr._curr_version = self._mk_version("Helium")[1]
  349. with self.assertRaises(SaltConfigurationError):
  350. assert depr(self.new_function)() == self.new_function()
  351. def test_with_deprecated_opt_in_use_superseded_and_deprecated_in_pillar(self):
  352. """
  353. Test with_deprecated misconfiguration.
  354. :return:
  355. """
  356. self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"]
  357. self.globs["__pillar__"]["use_superseded"] = ["test.new_function"]
  358. depr = decorators.with_deprecated(self.globs, "Beryllium")
  359. depr._curr_version = self._mk_version("Helium")[1]
  360. with self.assertRaises(SaltConfigurationError):
  361. assert depr(self.new_function)() == self.new_function()
  362. def test_with_depreciated_should_wrap_function(self):
  363. wrapped = decorators.with_deprecated({}, "Beryllium")(self.old_function)
  364. assert wrapped.__module__ == self.old_function.__module__
  365. def test_is_deprecated_should_wrap_function(self):
  366. wrapped = decorators.is_deprecated({}, "Beryllium")(self.old_function)
  367. assert wrapped.__module__ == self.old_function.__module__
  368. def test_ensure_unicode_args_should_wrap_function(self):
  369. wrapped = decorators.ensure_unicode_args(self.old_function)
  370. assert wrapped.__module__ == self.old_function.__module__
  371. def test_ignores_kwargs_should_wrap_function(self):
  372. wrapped = decorators.ignores_kwargs("foo", "bar")(self.old_function)
  373. assert wrapped.__module__ == self.old_function.__module__
  374. def test_memoize_should_wrap_function(self):
  375. wrapped = decorators.memoize(self.old_function)
  376. assert wrapped.__module__ == self.old_function.__module__
  377. def timing_should_wrap_function(self):
  378. wrapped = decorators.timing(self.old_function)
  379. assert wrapped.__module__ == self.old_function.__module__