summaryrefslogtreecommitdiff
path: root/src/mailman/bin/tests
diff options
context:
space:
mode:
authorBarry Warsaw2017-07-22 03:02:05 +0000
committerBarry Warsaw2017-07-22 03:02:05 +0000
commitf00b94f18e1d82d1488cbcee6053f03423bc2f49 (patch)
tree1a8e56dff0eab71e58e5fc9ecc5f3c614d7edca7 /src/mailman/bin/tests
parentf54c045519300f6f70947d1114f46c2b8ae0d368 (diff)
downloadmailman-f00b94f18e1d82d1488cbcee6053f03423bc2f49.tar.gz
mailman-f00b94f18e1d82d1488cbcee6053f03423bc2f49.tar.zst
mailman-f00b94f18e1d82d1488cbcee6053f03423bc2f49.zip
Diffstat (limited to 'src/mailman/bin/tests')
-rw-r--r--src/mailman/bin/tests/test_mailman.py62
-rw-r--r--src/mailman/bin/tests/test_master.py74
2 files changed, 99 insertions, 37 deletions
diff --git a/src/mailman/bin/tests/test_mailman.py b/src/mailman/bin/tests/test_mailman.py
index 49a64fb5c..54ee54bce 100644
--- a/src/mailman/bin/tests/test_mailman.py
+++ b/src/mailman/bin/tests/test_mailman.py
@@ -19,9 +19,8 @@
import unittest
-from contextlib import ExitStack
+from click.testing import CliRunner
from datetime import timedelta
-from io import StringIO
from mailman.app.lifecycle import create_list
from mailman.bin.mailman import main
from mailman.config import config
@@ -34,17 +33,31 @@ from unittest.mock import patch
class TestMailmanCommand(unittest.TestCase):
layer = ConfigLayer
+ def setUp(self):
+ self._command = CliRunner()
+
def test_mailman_command_without_subcommand_prints_help(self):
# Issue #137: Running `mailman` without a subcommand raises an
# AttributeError.
- testargs = ['mailman']
- output = StringIO()
- with patch('sys.argv', testargs), patch('sys.stdout', output):
- with self.assertRaises(SystemExit):
- main()
- self.assertIn('usage', output.getvalue())
+ result = self._command.invoke(main)
+ lines = result.output.splitlines()
+ # "main" instead of "mailman" because of the way the click runner
+ # works. It does actually show the correct program when run from the
+ # command line.
+ self.assertEqual(lines[0], 'Usage: main [OPTIONS] COMMAND [ARGS]...')
+
+ def test_mailman_command_with_bad_subcommand_prints_help(self):
+ # Issue #137: Running `mailman` without a subcommand raises an
+ # AttributeError.
+ result = self._command.invoke(main, ('not-a-subcommand',))
+ lines = result.output.splitlines()
+ # "main" instead of "mailman" because of the way the click runner
+ # works. It does actually show the correct program when run from the
+ # command line.
+ self.assertEqual(lines[0], 'Usage: main [OPTIONS] COMMAND [ARGS]...')
- def test_transaction_commit_after_successful_subcommand(self):
+ @patch('mailman.bin.mailman.initialize')
+ def test_transaction_commit_after_successful_subcommand(self, mock):
# Issue #223: Subcommands which change the database need to commit or
# abort the transaction.
with transaction():
@@ -52,40 +65,23 @@ class TestMailmanCommand(unittest.TestCase):
mlist.volume = 5
mlist.next_digest_number = 3
mlist.digest_last_sent_at = now() - timedelta(days=60)
- testargs = ['mailman', 'digests', '-b', '-l', 'ant@example.com']
- output = StringIO()
- with ExitStack() as resources:
- enter = resources.enter_context
- enter(patch('sys.argv', testargs))
- enter(patch('sys.stdout', output))
- # Everything is already initialized.
- enter(patch('mailman.bin.mailman.initialize'))
- main()
+ self._command.invoke(main, ('digests', '-b', '-l', 'ant@example.com'))
# Clear the current transaction to force a database reload.
config.db.abort()
self.assertEqual(mlist.volume, 6)
self.assertEqual(mlist.next_digest_number, 1)
- def test_transaction_abort_after_failing_subcommand(self):
+ @patch('mailman.bin.mailman.initialize')
+ @patch('mailman.commands.cli_digests.maybe_send_digest_now',
+ side_effect=RuntimeError)
+ def test_transaction_abort_after_failing_subcommand(self, mock1, mock2):
with transaction():
mlist = create_list('ant@example.com')
mlist.volume = 5
mlist.next_digest_number = 3
mlist.digest_last_sent_at = now() - timedelta(days=60)
- testargs = ['mailman', 'digests', '-b', '-l', 'ant@example.com',
- '--send']
- output = StringIO()
- with ExitStack() as resources:
- enter = resources.enter_context
- enter(patch('sys.argv', testargs))
- enter(patch('sys.stdout', output))
- # Force an exception in the subcommand.
- enter(patch('mailman.commands.cli_digests.maybe_send_digest_now',
- side_effect=RuntimeError))
- # Everything is already initialized.
- enter(patch('mailman.bin.mailman.initialize'))
- with self.assertRaises(RuntimeError):
- main()
+ self._command.invoke(
+ main, ('digests', '-b', '-l', 'ant@example.com', '--send'))
# Clear the current transaction to force a database reload.
config.db.abort()
# The volume and number haven't changed.
diff --git a/src/mailman/bin/tests/test_master.py b/src/mailman/bin/tests/test_master.py
index fb045f58f..27ea6d559 100644
--- a/src/mailman/bin/tests/test_master.py
+++ b/src/mailman/bin/tests/test_master.py
@@ -21,13 +21,28 @@ import os
import tempfile
import unittest
-from contextlib import suppress
+from click.testing import CliRunner
+from contextlib import ExitStack, suppress
from datetime import timedelta
-from flufl.lock import Lock
+from flufl.lock import Lock, TimeOutError
+from io import StringIO
from mailman.bin import master
+from mailman.config import config
+from mailman.testing.layers import ConfigLayer
+from pkg_resources import resource_filename
+from unittest.mock import patch
-class TestMasterLock(unittest.TestCase):
+class FakeLock:
+ details = ('host.example.com', 9999, '/tmp/whatever')
+
+ def unlock(self):
+ pass
+
+
+class TestMaster(unittest.TestCase):
+ layer = ConfigLayer
+
def setUp(self):
fd, self.lock_file = tempfile.mkstemp()
os.close(fd)
@@ -59,4 +74,55 @@ class TestMasterLock(unittest.TestCase):
finally:
my_lock.unlock()
self.assertEqual(state, master.WatcherState.conflict)
- # XXX test stale_lock and host_mismatch states.
+
+ def test_acquire_lock_timeout_reason_unknown(self):
+ stderr = StringIO()
+ with ExitStack() as resources:
+ resources.enter_context(patch(
+ 'mailman.bin.master.acquire_lock_1',
+ side_effect=TimeOutError))
+ resources.enter_context(patch(
+ 'mailman.bin.master.master_state',
+ return_value=(master.WatcherState.none, FakeLock())))
+ resources.enter_context(patch(
+ 'mailman.bin.master.sys.stderr', stderr))
+ with self.assertRaises(SystemExit) as cm:
+ master.acquire_lock(False)
+ self.assertEqual(cm.exception.code, 1)
+ self.assertEqual(stderr.getvalue(), """\
+For unknown reasons, the master lock could not be acquired.
+
+Lock file: {}
+Lock host: host.example.com
+
+Exiting.
+""".format(config.LOCK_FILE))
+
+ def test_main_cli(self):
+ command = CliRunner()
+ fake_lock = FakeLock()
+ with ExitStack() as resources:
+ config_file = resource_filename(
+ 'mailman.testing', 'testing.cfg')
+ init_mock = resources.enter_context(patch(
+ 'mailman.bin.master.initialize'))
+ lock_mock = resources.enter_context(patch(
+ 'mailman.bin.master.acquire_lock',
+ return_value=fake_lock))
+ start_mock = resources.enter_context(patch.object(
+ master.Loop, 'start_runners'))
+ loop_mock = resources.enter_context(patch.object(
+ master.Loop, 'loop'))
+ command.invoke(
+ master.main,
+ ('-C', config_file,
+ '--no-restart', '--force',
+ '-r', 'in:1:1', '--verbose'))
+ # We got initialized with the custom configuration file and the
+ # verbose flag.
+ init_mock.assert_called_once_with(config_file, True)
+ # We returned a lock that was force-acquired.
+ lock_mock.assert_called_once_with(True)
+ # We created a non-restartable loop.
+ start_mock.assert_called_once_with([('in', 1, 1)])
+ loop_mock.assert_called_once_with()