test_configfile.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. # -*- coding: utf-8; -*-
  2. #
  3. # test/test_configfile.py
  4. # Part of ‘dput’, a Debian package upload toolkit.
  5. #
  6. # Copyright © 2015 Ben Finney <ben+python@benfinney.id.au>
  7. #
  8. # This is free software: you may copy, modify, and/or distribute this work
  9. # under the terms of the GNU General Public License as published by the
  10. # Free Software Foundation; version 3 of that license or any later version.
  11. # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
  12. """ Unit tests for config file behaviour. """
  13. from __future__ import (absolute_import, unicode_literals)
  14. import sys
  15. import os
  16. import os.path
  17. import tempfile
  18. import textwrap
  19. import doctest
  20. import testtools
  21. import testtools.matchers
  22. __package__ = str("test")
  23. __import__(__package__)
  24. sys.path.insert(1, os.path.dirname(os.path.dirname(__file__)))
  25. import dput.dput
  26. from .helper import (
  27. builtins,
  28. StringIO,
  29. configparser,
  30. mock,
  31. FakeSystemExit,
  32. EXIT_STATUS_FAILURE,
  33. patch_system_interfaces,
  34. FileDouble,
  35. setup_file_double_behaviour,
  36. )
  37. def make_config_from_stream(stream):
  38. """ Make a ConfigParser parsed configuration from the stream content.
  39. :param stream: Text stream content of a config file.
  40. :return: The resulting config if the content parses correctly,
  41. or ``None``.
  42. """
  43. config = configparser.ConfigParser(
  44. defaults={
  45. 'allow_unsigned_uploads': "false",
  46. },
  47. )
  48. config_file = StringIO(stream)
  49. try:
  50. config.readfp(config_file)
  51. except configparser.ParsingError:
  52. config = None
  53. return config
  54. def make_config_file_scenarios():
  55. """ Make a collection of scenarios for testing with config files.
  56. :return: A collection of scenarios for tests involving config files.
  57. The collection is a mapping from scenario name to a dictionary of
  58. scenario attributes.
  59. """
  60. runtime_config_file_path = tempfile.mktemp()
  61. global_config_file_path = os.path.join(os.path.sep, "etc", "dput.cf")
  62. user_config_file_path = os.path.join(os.path.expanduser("~"), ".dput.cf")
  63. fake_file_empty = StringIO()
  64. fake_file_bogus = StringIO("b0gUs")
  65. fake_file_minimal = StringIO(textwrap.dedent("""\
  66. [DEFAULT]
  67. """))
  68. fake_file_simple = StringIO(textwrap.dedent("""\
  69. [DEFAULT]
  70. hash = md5
  71. [foo]
  72. method = ftp
  73. fqdn = quux.example.com
  74. incoming = quux
  75. check_version = false
  76. allow_unsigned_uploads = false
  77. allowed_distributions =
  78. run_dinstall = false
  79. """))
  80. fake_file_simple_host_three = StringIO(textwrap.dedent("""\
  81. [DEFAULT]
  82. hash = md5
  83. [foo]
  84. method = ftp
  85. fqdn = quux.example.com
  86. incoming = quux
  87. check_version = false
  88. allow_unsigned_uploads = false
  89. allowed_distributions =
  90. run_dinstall = false
  91. [bar]
  92. fqdn = xyzzy.example.com
  93. incoming = xyzzy
  94. [baz]
  95. fqdn = chmrr.example.com
  96. incoming = chmrr
  97. """))
  98. fake_file_method_local = StringIO(textwrap.dedent("""\
  99. [foo]
  100. method = local
  101. incoming = quux
  102. """))
  103. fake_file_missing_fqdn = StringIO(textwrap.dedent("""\
  104. [foo]
  105. method = ftp
  106. incoming = quux
  107. """))
  108. fake_file_missing_incoming = StringIO(textwrap.dedent("""\
  109. [foo]
  110. method = ftp
  111. fqdn = quux.example.com
  112. """))
  113. fake_file_default_not_unsigned = StringIO(textwrap.dedent("""\
  114. [DEFAULT]
  115. allow_unsigned_uploads = false
  116. [foo]
  117. method = ftp
  118. fqdn = quux.example.com
  119. """))
  120. fake_file_default_distribution_only = StringIO(textwrap.dedent("""\
  121. [DEFAULT]
  122. default_host_main = consecteur
  123. [ftp-master]
  124. method = ftp
  125. fqdn = quux.example.com
  126. """))
  127. fake_file_distribution_none = StringIO(textwrap.dedent("""\
  128. [foo]
  129. method = ftp
  130. fqdn = quux.example.com
  131. distributions =
  132. """))
  133. fake_file_distribution_one = StringIO(textwrap.dedent("""\
  134. [foo]
  135. method = ftp
  136. fqdn = quux.example.com
  137. distributions = spam
  138. """))
  139. fake_file_distribution_three = StringIO(textwrap.dedent("""\
  140. [foo]
  141. method = ftp
  142. fqdn = quux.example.com
  143. distributions = spam,eggs,beans
  144. """))
  145. default_scenario_params = {
  146. 'runtime': {
  147. 'file_double_params': dict(
  148. path=runtime_config_file_path,
  149. fake_file=fake_file_minimal),
  150. 'open_scenario_name': 'okay',
  151. },
  152. 'global': {
  153. 'file_double_params': dict(
  154. path=global_config_file_path,
  155. fake_file=fake_file_minimal),
  156. 'open_scenario_name': 'okay',
  157. },
  158. 'user': {
  159. 'file_double_params': dict(
  160. path=user_config_file_path,
  161. fake_file=fake_file_minimal),
  162. 'open_scenario_name': 'okay',
  163. },
  164. }
  165. scenarios = {
  166. 'default': {
  167. 'configs_by_name': {
  168. 'runtime': None,
  169. },
  170. },
  171. 'not-exist': {
  172. 'configs_by_name': {
  173. 'runtime': {
  174. 'open_scenario_name': 'nonexist',
  175. },
  176. },
  177. },
  178. 'exist-read-denied': {
  179. 'configs_by_name': {
  180. 'runtime': {
  181. 'open_scenario_name': 'read_denied',
  182. },
  183. },
  184. },
  185. 'exist-empty': {
  186. 'configs_by_name': {
  187. 'runtime': {
  188. 'file_double_params': dict(
  189. path=runtime_config_file_path,
  190. fake_file=fake_file_empty),
  191. },
  192. },
  193. },
  194. 'exist-invalid': {
  195. 'configs_by_name': {
  196. 'runtime': {
  197. 'file_double_params': dict(
  198. path=runtime_config_file_path,
  199. fake_file=fake_file_bogus),
  200. },
  201. },
  202. },
  203. 'exist-minimal': {},
  204. 'exist-simple': {
  205. 'configs_by_name': {
  206. 'runtime': {
  207. 'file_double_params': dict(
  208. path=runtime_config_file_path,
  209. fake_file=fake_file_simple),
  210. 'test_section': "foo",
  211. },
  212. },
  213. },
  214. 'exist-simple-host-three': {
  215. 'configs_by_name': {
  216. 'runtime': {
  217. 'file_double_params': dict(
  218. path=runtime_config_file_path,
  219. fake_file=fake_file_simple_host_three),
  220. 'test_section': "foo",
  221. },
  222. },
  223. },
  224. 'exist-method-local': {
  225. 'configs_by_name': {
  226. 'runtime': {
  227. 'file_double_params': dict(
  228. path=runtime_config_file_path,
  229. fake_file=fake_file_method_local),
  230. 'test_section': "foo",
  231. },
  232. },
  233. },
  234. 'exist-missing-fqdn': {
  235. 'configs_by_name': {
  236. 'runtime': {
  237. 'file_double_params': dict(
  238. path=runtime_config_file_path,
  239. fake_file=fake_file_missing_fqdn),
  240. 'test_section': "foo",
  241. },
  242. },
  243. },
  244. 'exist-missing-incoming': {
  245. 'configs_by_name': {
  246. 'runtime': {
  247. 'file_double_params': dict(
  248. path=runtime_config_file_path,
  249. fake_file=fake_file_missing_incoming),
  250. 'test_section': "foo",
  251. },
  252. },
  253. },
  254. 'exist-default-not-unsigned': {
  255. 'configs_by_name': {
  256. 'runtime': {
  257. 'file_double_params': dict(
  258. path=runtime_config_file_path,
  259. fake_file=fake_file_default_not_unsigned),
  260. 'test_section': "foo",
  261. },
  262. },
  263. },
  264. 'exist-default-distribution-only': {
  265. 'configs_by_name': {
  266. 'runtime': {
  267. 'file_double_params': dict(
  268. path=runtime_config_file_path,
  269. fake_file=fake_file_default_distribution_only),
  270. 'test_section': "foo",
  271. },
  272. },
  273. },
  274. 'exist-distribution-none': {
  275. 'configs_by_name': {
  276. 'runtime': {
  277. 'file_double_params': dict(
  278. path=runtime_config_file_path,
  279. fake_file=fake_file_distribution_none),
  280. 'test_section': "foo",
  281. },
  282. },
  283. },
  284. 'exist-distribution-one': {
  285. 'configs_by_name': {
  286. 'runtime': {
  287. 'file_double_params': dict(
  288. path=runtime_config_file_path,
  289. fake_file=fake_file_distribution_one),
  290. 'test_section': "foo",
  291. },
  292. },
  293. },
  294. 'exist-distribution-three': {
  295. 'configs_by_name': {
  296. 'runtime': {
  297. 'file_double_params': dict(
  298. path=runtime_config_file_path,
  299. fake_file=fake_file_distribution_three),
  300. 'test_section': "foo",
  301. },
  302. },
  303. },
  304. 'global-config-not-exist': {
  305. 'configs_by_name': {
  306. 'global': {
  307. 'open_scenario_name': 'nonexist',
  308. },
  309. 'runtime': None,
  310. },
  311. },
  312. 'global-config-read-denied': {
  313. 'configs_by_name': {
  314. 'global': {
  315. 'open_scenario_name': 'read_denied',
  316. },
  317. 'runtime': None,
  318. },
  319. },
  320. 'user-config-not-exist': {
  321. 'configs_by_name': {
  322. 'user': {
  323. 'open_scenario_name': 'nonexist',
  324. },
  325. 'runtime': None,
  326. },
  327. },
  328. 'all-not-exist': {
  329. 'configs_by_name': {
  330. 'global': {
  331. 'open_scenario_name': 'nonexist',
  332. },
  333. 'user': {
  334. 'open_scenario_name': 'nonexist',
  335. },
  336. 'runtime': None,
  337. },
  338. },
  339. }
  340. for scenario in scenarios.values():
  341. scenario['empty_file'] = fake_file_empty
  342. if 'configs_by_name' not in scenario:
  343. scenario['configs_by_name'] = {}
  344. for (config_name, default_params) in default_scenario_params.items():
  345. if config_name not in scenario['configs_by_name']:
  346. params = default_params
  347. elif scenario['configs_by_name'][config_name] is None:
  348. continue
  349. else:
  350. params = default_params.copy()
  351. params.update(scenario['configs_by_name'][config_name])
  352. params['file_double'] = FileDouble(**params['file_double_params'])
  353. params['file_double'].set_open_scenario(
  354. params['open_scenario_name'])
  355. params['config'] = make_config_from_stream(
  356. params['file_double'].fake_file.getvalue())
  357. scenario['configs_by_name'][config_name] = params
  358. return scenarios
  359. def get_file_doubles_from_config_file_scenarios(scenarios):
  360. """ Get the `FileDouble` instances from config file scenarios.
  361. :param scenarios: Collection of config file scenarios.
  362. :return: Collection of `FileDouble` instances.
  363. """
  364. doubles = set()
  365. for scenario in scenarios:
  366. configs_by_name = scenario['configs_by_name']
  367. doubles.update(
  368. configs_by_name[config_name]['file_double']
  369. for config_name in ['global', 'user', 'runtime']
  370. if configs_by_name[config_name] is not None)
  371. return doubles
  372. def setup_config_file_fixtures(testcase):
  373. """ Set up fixtures for config file doubles. """
  374. scenarios = make_config_file_scenarios()
  375. testcase.config_file_scenarios = scenarios
  376. setup_file_double_behaviour(
  377. testcase,
  378. get_file_doubles_from_config_file_scenarios(scenarios.values()))
  379. def set_config(testcase, name):
  380. """ Set the config scenario for a specific test case. """
  381. scenarios = make_config_file_scenarios()
  382. testcase.config_scenario = scenarios[name]
  383. config = testcase.config_scenario['configs_by_name']['runtime']['config']
  384. mock_patcher = mock.patch.object(dput.dput, 'config', new=config)
  385. mock_patcher.start()
  386. testcase.addCleanup(mock_patcher.stop)
  387. def patch_runtime_config_options(testcase):
  388. """ Patch specific options in the runtime config. """
  389. config_params_by_name = testcase.config_scenario['configs_by_name']
  390. runtime_config_params = config_params_by_name['runtime']
  391. testcase.runtime_config_parser = runtime_config_params['config']
  392. def maybe_set_option(
  393. parser, section_name, option_name, value, default=""):
  394. if value is not None:
  395. if value is NotImplemented:
  396. # No specified value. Set a default.
  397. value = default
  398. parser.set(section_name, option_name, str(value))
  399. else:
  400. # Specifically requested *no* setting for ‘delayed’ option.
  401. pass
  402. if testcase.runtime_config_parser is not None:
  403. testcase.test_host = runtime_config_params.get(
  404. 'test_section', None)
  405. testcase.runtime_config_parser.set(
  406. 'DEFAULT', 'method',
  407. getattr(testcase, 'config_default_method', "ftp"))
  408. testcase.runtime_config_parser.set(
  409. 'DEFAULT', 'login',
  410. getattr(testcase, 'config_default_login', "username"))
  411. testcase.runtime_config_parser.set(
  412. 'DEFAULT', 'scp_compress',
  413. str(getattr(testcase, 'config_default_scp_compress', False)))
  414. testcase.runtime_config_parser.set(
  415. 'DEFAULT', 'ssh_config_options',
  416. getattr(testcase, 'config_default_ssh_config_options', ""))
  417. testcase.runtime_config_parser.set(
  418. 'DEFAULT', 'distributions',
  419. getattr(testcase, 'config_default_distributions', ""))
  420. testcase.runtime_config_parser.set(
  421. 'DEFAULT', 'incoming',
  422. getattr(testcase, 'config_default_incoming', "quux"))
  423. testcase.runtime_config_parser.set(
  424. 'DEFAULT', 'allow_dcut',
  425. str(getattr(testcase, 'config_default_allow_dcut', True)))
  426. config_default_default_host_main = getattr(
  427. testcase, 'config_default_default_host_main', NotImplemented)
  428. maybe_set_option(
  429. testcase.runtime_config_parser,
  430. 'DEFAULT', 'default_host_main',
  431. config_default_default_host_main,
  432. default="")
  433. config_default_delayed = getattr(
  434. testcase, 'config_default_delayed', NotImplemented)
  435. maybe_set_option(
  436. testcase.runtime_config_parser,
  437. 'DEFAULT', 'delayed', config_default_delayed,
  438. default=testcase.getUniqueInteger())
  439. for section_name in testcase.runtime_config_parser.sections():
  440. testcase.runtime_config_parser.set(
  441. section_name, 'method',
  442. getattr(testcase, 'config_method', "ftp"))
  443. testcase.runtime_config_parser.set(
  444. section_name, 'fqdn',
  445. getattr(testcase, 'config_fqdn', "quux.example.com"))
  446. testcase.runtime_config_parser.set(
  447. section_name, 'passive_ftp',
  448. str(getattr(testcase, 'config_passive_ftp', False)))
  449. testcase.runtime_config_parser.set(
  450. section_name, 'run_lintian',
  451. str(getattr(testcase, 'config_run_lintian', False)))
  452. testcase.runtime_config_parser.set(
  453. section_name, 'run_dinstall',
  454. str(getattr(testcase, 'config_run_dinstall', False)))
  455. testcase.runtime_config_parser.set(
  456. section_name, 'pre_upload_command',
  457. getattr(testcase, 'config_pre_upload_command', ""))
  458. testcase.runtime_config_parser.set(
  459. section_name, 'post_upload_command',
  460. getattr(testcase, 'config_post_upload_command', ""))
  461. testcase.runtime_config_parser.set(
  462. section_name, 'progress_indicator',
  463. str(getattr(testcase, 'config_progress_indicator', 0)))
  464. testcase.runtime_config_parser.set(
  465. section_name, 'allow_dcut',
  466. str(getattr(testcase, 'config_allow_dcut', True)))
  467. if hasattr(testcase, 'config_incoming'):
  468. testcase.runtime_config_parser.set(
  469. section_name, 'incoming', testcase.config_incoming)
  470. config_delayed = getattr(
  471. testcase, 'config_delayed', NotImplemented)
  472. maybe_set_option(
  473. testcase.runtime_config_parser,
  474. section_name, 'delayed', config_delayed,
  475. default=testcase.getUniqueInteger())
  476. for (section_type, options) in (
  477. getattr(testcase, 'config_extras', {}).items()):
  478. section_name = {
  479. 'default': "DEFAULT",
  480. 'host': testcase.test_host,
  481. }[section_type]
  482. for (option_name, option_value) in options.items():
  483. testcase.runtime_config_parser.set(
  484. section_name, option_name, option_value)
  485. class read_configs_TestCase(testtools.TestCase):
  486. """ Test cases for `read_config` function. """
  487. def setUp(self):
  488. """ Set up test fixtures. """
  489. super(read_configs_TestCase, self).setUp()
  490. patch_system_interfaces(self)
  491. setup_config_file_fixtures(self)
  492. self.test_configparser = configparser.ConfigParser()
  493. self.mock_configparser_class = mock.Mock(
  494. 'ConfigParser',
  495. return_value=self.test_configparser)
  496. patcher_class_configparser = mock.patch.object(
  497. configparser, 'ConfigParser',
  498. new=self.mock_configparser_class)
  499. patcher_class_configparser.start()
  500. self.addCleanup(patcher_class_configparser.stop)
  501. self.set_config_file_scenario('exist-minimal')
  502. self.set_test_args()
  503. def set_config_file_scenario(self, name):
  504. """ Set the configuration file scenario for this test case. """
  505. self.config_file_scenario = self.config_file_scenarios[name]
  506. self.configs_by_name = self.config_file_scenario['configs_by_name']
  507. for config_params in self.configs_by_name.values():
  508. if config_params is not None:
  509. config_params['file_double'].register_for_testcase(self)
  510. def get_path_for_runtime_config_file(self):
  511. """ Get the path to specify for runtime config file. """
  512. path = ""
  513. runtime_config_params = self.configs_by_name['runtime']
  514. if runtime_config_params is not None:
  515. runtime_config_file_double = runtime_config_params['file_double']
  516. path = runtime_config_file_double.path
  517. return path
  518. def set_test_args(self):
  519. """ Set the arguments for the test call to the function. """
  520. runtime_config_file_path = self.get_path_for_runtime_config_file()
  521. self.test_args = dict(
  522. extra_config=runtime_config_file_path,
  523. debug=False,
  524. )
  525. def test_creates_new_parser(self):
  526. """ Should create a new `ConfigParser` instance. """
  527. dput.dput.read_configs(**self.test_args)
  528. configparser.ConfigParser.assert_called_with()
  529. def test_assigns_global_config(self):
  530. """ Should assign global `config` name to new parser. """
  531. dput.dput.read_configs(**self.test_args)
  532. self.assertEqual(self.test_configparser, dput.dput.config)
  533. def test_sets_default_option_values(self):
  534. """ Should set values for options, in section 'DEFAULT'. """
  535. option_names = set([
  536. 'login',
  537. 'method',
  538. 'hash',
  539. 'allow_unsigned_uploads',
  540. 'allow_dcut',
  541. 'distributions',
  542. 'allowed_distributions',
  543. 'run_lintian',
  544. 'run_dinstall',
  545. 'check_version',
  546. 'scp_compress',
  547. 'default_host_main',
  548. 'post_upload_command',
  549. 'pre_upload_command',
  550. 'ssh_config_options',
  551. 'passive_ftp',
  552. 'progress_indicator',
  553. 'delayed',
  554. ])
  555. dput.dput.read_configs(**self.test_args)
  556. defaults = self.test_configparser.defaults()
  557. self.assertTrue(option_names.issubset(set(defaults.keys())))
  558. def test_opens_default_config_files(self):
  559. """ Should open the default config files. """
  560. self.set_config_file_scenario('default')
  561. self.set_test_args()
  562. dput.dput.read_configs(**self.test_args)
  563. expected_calls = [
  564. mock.call(
  565. self.configs_by_name[config_name]['file_double'].path)
  566. for config_name in ['global', 'user']]
  567. builtins.open.assert_has_calls(expected_calls)
  568. def test_opens_specified_config_file(self):
  569. """ Should open the specified config file. """
  570. dput.dput.read_configs(**self.test_args)
  571. builtins.open.assert_called_with(
  572. self.configs_by_name['runtime']['file_double'].path)
  573. def test_emits_debug_message_on_opening_config_file(self):
  574. """ Should emit a debug message when opening the config file. """
  575. self.test_args['debug'] = True
  576. config_file_double = self.configs_by_name['runtime']['file_double']
  577. dput.dput.read_configs(**self.test_args)
  578. expected_output = textwrap.dedent("""\
  579. D: Parsing Configuration File {path}
  580. """).format(path=config_file_double.path)
  581. self.assertIn(expected_output, sys.stdout.getvalue())
  582. def test_skips_file_if_not_exist(self):
  583. """ Should skip a config file if it doesn't exist. """
  584. self.set_config_file_scenario('global-config-not-exist')
  585. self.set_test_args()
  586. config_file_double = self.configs_by_name['global']['file_double']
  587. self.test_args['debug'] = True
  588. dput.dput.read_configs(**self.test_args)
  589. expected_calls = [
  590. mock.call(config_file_double.path)]
  591. expected_output = textwrap.dedent("""\
  592. No such file ...: {path}, skipping
  593. """).format(path=config_file_double.path)
  594. builtins.open.assert_has_calls(expected_calls)
  595. self.assertThat(
  596. sys.stderr.getvalue(),
  597. testtools.matchers.DocTestMatches(
  598. expected_output, flags=doctest.ELLIPSIS))
  599. def test_skips_file_if_permission_denied(self):
  600. """ Should skip a config file if read permission is denied.. """
  601. self.set_config_file_scenario('global-config-read-denied')
  602. self.set_test_args()
  603. config_file_double = self.configs_by_name['global']['file_double']
  604. self.test_args['debug'] = True
  605. dput.dput.read_configs(**self.test_args)
  606. expected_calls = [
  607. mock.call(config_file_double.path)]
  608. expected_output = textwrap.dedent("""\
  609. Read denied on ...: {path}, skipping
  610. """).format(path=config_file_double.path)
  611. builtins.open.assert_has_calls(expected_calls)
  612. self.assertThat(
  613. sys.stderr.getvalue(),
  614. testtools.matchers.DocTestMatches(
  615. expected_output, flags=doctest.ELLIPSIS))
  616. def test_calls_sys_exit_if_no_config_files(self):
  617. """ Should call `sys.exit` if unable to open any config files. """
  618. self.set_config_file_scenario('all-not-exist')
  619. self.set_test_args()
  620. with testtools.ExpectedException(FakeSystemExit):
  621. dput.dput.read_configs(**self.test_args)
  622. expected_output = textwrap.dedent("""\
  623. Error: Could not open any configfile, tried ...
  624. """)
  625. self.assertThat(
  626. sys.stderr.getvalue(),
  627. testtools.matchers.DocTestMatches(
  628. expected_output, flags=doctest.ELLIPSIS))
  629. sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
  630. def test_calls_sys_exit_if_config_parsing_error(self):
  631. """ Should call `sys.exit` if a parsing error occurs. """
  632. self.set_config_file_scenario('exist-invalid')
  633. self.set_test_args()
  634. self.test_args['debug'] = True
  635. with testtools.ExpectedException(FakeSystemExit):
  636. dput.dput.read_configs(**self.test_args)
  637. expected_output = textwrap.dedent("""\
  638. Error parsing config file:
  639. ...
  640. """)
  641. self.assertThat(
  642. sys.stderr.getvalue(),
  643. testtools.matchers.DocTestMatches(
  644. expected_output, flags=doctest.ELLIPSIS))
  645. sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
  646. def test_sets_fqdn_option_if_local_method(self):
  647. """ Should set “fqdn” option for “local” method. """
  648. self.set_config_file_scenario('exist-method-local')
  649. self.set_test_args()
  650. dput.dput.read_configs(**self.test_args)
  651. runtime_config_params = self.configs_by_name['runtime']
  652. test_section = runtime_config_params['test_section']
  653. self.assertTrue(dput.dput.config.has_option(test_section, "fqdn"))
  654. def test_exits_with_error_if_missing_fqdn(self):
  655. """ Should exit with error if config is missing 'fqdn'. """
  656. self.set_config_file_scenario('exist-missing-fqdn')
  657. self.set_test_args()
  658. with testtools.ExpectedException(FakeSystemExit):
  659. dput.dput.read_configs(**self.test_args)
  660. expected_output = textwrap.dedent("""\
  661. Config error: {host} must have a fqdn set
  662. """).format(host="foo")
  663. self.assertIn(expected_output, sys.stderr.getvalue())
  664. sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
  665. def test_exits_with_error_if_missing_incoming(self):
  666. """ Should exit with error if config is missing 'incoming'. """
  667. self.set_config_file_scenario('exist-missing-incoming')
  668. self.set_test_args()
  669. with testtools.ExpectedException(FakeSystemExit):
  670. dput.dput.read_configs(**self.test_args)
  671. expected_output = textwrap.dedent("""\
  672. Config error: {host} must have an incoming directory set
  673. """).format(host="foo")
  674. self.assertIn(expected_output, sys.stderr.getvalue())
  675. sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
  676. class print_config_TestCase(testtools.TestCase):
  677. """ Test cases for `print_config` function. """
  678. def setUp(self):
  679. """ Set up test fixtures. """
  680. super(print_config_TestCase, self).setUp()
  681. patch_system_interfaces(self)
  682. def test_invokes_config_write_to_stdout(self):
  683. """ Should invoke config's `write` method with `sys.stdout`. """
  684. test_config = make_config_from_stream("")
  685. mock_config = mock.Mock(test_config)
  686. dput.dput.print_config(mock_config, debug=False)
  687. mock_config.write.assert_called_with(sys.stdout)
  688. # Local variables:
  689. # coding: utf-8
  690. # mode: python
  691. # End:
  692. # vim: fileencoding=utf-8 filetype=python :