Merge authors:
Aurélien Bompard (abompard)
Related merge proposals:
https://code.launchpad.net/~abompard/mailman.client/mockingclient/+merge/242185
proposed by: Aurélien Bompard (abompard)
------------------------------------------------------------
revno: 58 [merge]
committer: Florian Fuchs <[email protected]>
branch nick: mailman.client
timestamp: Mon 2014-11-24 11:25:10 +0100
message:
Addedd an improved test harness using WebTest. Contributed by Aurélien
Abompard.
added:
src/mailmanclient/tests/utils.py
modified:
bin/test
setup.py
src/mailmanclient/NEWS.txt
src/mailmanclient/docs/using.txt
src/mailmanclient/tests/test_docs.py
--
lp:mailman.client
https://code.launchpad.net/~mailman-coders/mailman.client/trunk
Your team Mailman Coders is subscribed to branch lp:mailman.client.
To unsubscribe from this branch go to
https://code.launchpad.net/~mailman-coders/mailman.client/trunk/+edit-subscription
=== modified file 'bin/test'
--- bin/test 2013-03-15 23:02:04 +0000
+++ bin/test 2014-11-24 10:25:10 +0000
@@ -30,10 +30,6 @@
if __name__ == '__main__':
- if len(sys.argv) == 1:
- print 'Please provide the path to your mailman bin directory.'
- sys.exit(1)
- os.environ['MAILMAN_TEST_BINDIR'] = sys.argv[1]
suite = additional_tests()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
=== modified file 'setup.py'
--- setup.py 2014-04-22 14:38:40 +0000
+++ setup.py 2014-11-19 11:03:32 +0000
@@ -44,5 +44,5 @@
# Auto-conversion to Python 3.
use_2to3=True,
convert_2to3_doctests=find_doctests(),
- install_requires=['httplib2', 'mock', ],
+ install_requires=['httplib2', 'mock', 'WebTest', ],
)
=== modified file 'src/mailmanclient/NEWS.txt'
--- src/mailmanclient/NEWS.txt 2014-03-15 20:43:52 +0000
+++ src/mailmanclient/NEWS.txt 2014-11-24 10:25:10 +0000
@@ -2,6 +2,12 @@
NEWS for mailman.client
=======================
+1.0.0b1 (xxxx-xx-xx)
+
+* Addedd an improved test harness using WebTest. Contributed by Aurélien Abompard.
+
+
1.0.0a1 (2014-03-15)
====================
+
* Initial release.
=== modified file 'src/mailmanclient/docs/using.txt'
--- src/mailmanclient/docs/using.txt 2014-04-22 12:47:00 +0000
+++ src/mailmanclient/docs/using.txt 2014-11-18 15:08:41 +0000
@@ -640,12 +640,7 @@
... Some text.
...
... """
- >>> server = smtplib.LMTP('localhost', 8024)
- >>> server.sendmail('[email protected]', '[email protected]', msg)
- {}
- >>> server.quit()
- (221, 'Bye')
- >>> time.sleep(2)
+ >>> inject_message('[email protected]', msg)
Messages held for moderation can be listed on a per list basis.
=== modified file 'src/mailmanclient/tests/test_docs.py'
--- src/mailmanclient/tests/test_docs.py 2014-03-15 20:43:52 +0000
+++ src/mailmanclient/tests/test_docs.py 2014-11-19 10:58:36 +0000
@@ -17,7 +17,7 @@
"""Test harness for doctests."""
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, unicode_literals, print_function
__metaclass__ = type
__all__ = [
@@ -33,11 +33,16 @@
import tempfile
import unittest
import subprocess
+from textwrap import dedent
+from mock import patch
# pylint: disable-msg=F0401
from pkg_resources import (
resource_filename, resource_exists, resource_listdir, cleanup_resources)
+from mailman.config import config
+from mailmanclient.tests.utils import FakeMailmanClient, inject_message
+
COMMASPACE = ', '
DOT = '.'
@@ -50,17 +55,17 @@
def dump(results):
if results is None:
- print None
+ print(None)
return
for key in sorted(results):
if key == 'entries':
for i, entry in enumerate(results[key]):
# entry is a dictionary.
- print 'entry %d:' % i
+ print('entry %d:' % i)
for entry_key in sorted(entry):
- print ' {0}: {1}'.format(entry_key, entry[entry_key])
+ print(' {0}: {1}'.format(entry_key, entry[entry_key]))
else:
- print '{0}: {1}'.format(key, results[key])
+ print('{0}: {1}'.format(key, results[key]))
@@ -74,69 +79,33 @@
def setup(testobj):
- """Test setup."""
- # Create a unique database for the running version of Mailman, then start
- # it up. It should not yet be running. This environment variable must be
- # set to find the installation of Mailman we can run. Yes, this should be
- # fixed.
- testobj._bindir = os.environ.get('MAILMAN_TEST_BINDIR')
- if testobj._bindir is None:
- raise RuntimeError('Must set $MAILMAN_TEST_BINDIR to run tests')
- vardir = testobj._vardir = tempfile.mkdtemp()
- cfgfile = testobj._cfgfile = os.path.join(vardir, 'client_test.cfg')
- with open(cfgfile, 'w') as fp:
- print >> fp, """\
-[mailman]
-layout: tmpdir
-[paths.tmpdir]
-var_dir: {vardir}
-log_dir: /tmp/mmclient/logs
-[webservice]
-port: 9001
-[runner.archive]
-start: no
-[runner.bounces]
-start: no
-[runner.command]
-start: yes
-[runner.in]
-start: yes
-[runner.lmtp]
-start: yes
-[runner.news]
-start: no
-[runner.out]
-start: yes
-[runner.pipeline]
-start: no
-[runner.retry]
-start: no
-[runner.virgin]
-start: yes
-[runner.digest]
-start: no
-""".format(vardir=vardir)
- mailman = os.path.join(testobj._bindir, 'mailman')
- subprocess.call([mailman, '-C', cfgfile, 'start', '-q'])
- time.sleep(3)
- # Make sure future statements in our doctests match the Python code. When
- # run with 2to3, the future import gets removed and these names are not
- # defined.
- try:
- testobj.globs['absolute_import'] = absolute_import
- testobj.globs['unicode_literals'] = unicode_literals
- except NameError:
- pass
testobj.globs['stop'] = stop
testobj.globs['dump'] = dump
-
-
+ testobj.globs['inject_message'] = inject_message
+ FakeMailmanClient.setUp()
+ # In unit tests, passwords aren't encrypted. Don't show this in the doctests
+ passlib_cfg = os.path.join(config.VAR_DIR, 'passlib.cfg')
+ with open(passlib_cfg, 'w') as fp:
+ print(dedent("""
+ [passlib]
+ schemes = sha512_crypt
+ """), file=fp)
+ conf_hash_pw = dedent("""
+ [passwords]
+ configuration: {}
+ """.format(passlib_cfg))
+ config.push('conf_hash_pw', conf_hash_pw)
+ # Use the FakeMailmanClient
+ testobj.patcher = patch("mailmanclient.Client", FakeMailmanClient)
+ fmc = testobj.patcher.start()
+
+
+
def teardown(testobj):
"""Test teardown."""
- mailman = os.path.join(testobj._bindir, 'mailman')
- subprocess.call([mailman, '-C', testobj._cfgfile, 'stop', '-q'])
- shutil.rmtree(testobj._vardir)
- time.sleep(3)
+ testobj.patcher.stop()
+ config.pop('conf_hash_pw')
+ FakeMailmanClient.tearDown()
=== added file 'src/mailmanclient/tests/utils.py'
--- src/mailmanclient/tests/utils.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/tests/utils.py 2014-11-20 09:25:13 +0000
@@ -0,0 +1,144 @@
+# Copyright (C) 2010-2014 by The Free Software Foundation, Inc.
+#
+# This file is part of mailman.client.
+#
+# mailman.client is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# mailman.client is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
+
+"""Test tools."""
+
+from __future__ import absolute_import, unicode_literals, print_function
+
+import os
+from urllib2 import HTTPError
+from urlparse import urljoin
+
+from zope.component import getUtility
+from webtest import TestApp
+
+from mailman.config import config
+from mailman.core.chains import process
+from mailman.interfaces.listmanager import IListManager
+from mailman.interfaces.usermanager import IUserManager
+from mailman.testing.helpers import specialized_message_from_string
+from mailman.testing.layers import ConfigLayer
+import mailmanclient
+
+__metaclass__ = type
+__all__ = [
+ "inject_message",
+ "FakeMailmanClient",
+ ]
+
+
+
+def inject_message(fqdn_listname, msg):
+ mlist = getUtility(IListManager).get(fqdn_listname)
+ user_manager = getUtility(IUserManager)
+ msg = specialized_message_from_string(msg)
+ for sender in msg.senders:
+ if user_manager.get_address(sender) is None:
+ user_manager.create_address(sender)
+ process(mlist, msg, {})
+
+
+#
+# Mocking mailman client
+#
+
+
+class FakeConnection(mailmanclient._client._Connection):
+ """
+ Looks for information inside a dict instead of making HTTP requests.
+ Also, logs the called URLs as called_paths.
+ Very incomplete at the moment.
+ """
+
+ def __init__(self, baseurl, name=None, password=None):
+ super(FakeConnection, self).__init__(baseurl, name, password)
+ self.called_paths = []
+ from mailman.rest.wsgiapp import make_application
+ self.app = TestApp(make_application())
+ if self.basic_auth:
+ self.app.authorization = ('Basic', (self.name, self.password))
+ super(FakeConnection, self).__init__(baseurl, name, password)
+
+ def call(self, path, data=None, method=None):
+ self.called_paths.append(
+ { "path": path, "data": data, "method": method })
+ if method is None:
+ if data is None:
+ method = 'GET'
+ else:
+ method = 'POST'
+ method_fn = getattr(self.app, method.lower())
+ url = urljoin(self.baseurl, path)
+ try:
+ kw = {"expect_errors": True}
+ if data:
+ kw["params"] = data
+ response = method_fn(url, **kw)
+ headers = response.headers
+ headers["status"] = response.status_int
+ content = unicode(response.body)
+ # If we did not get a 2xx status code, make this look like a
+ # urllib2 exception, for backward compatibility.
+ if response.status_int // 100 != 2:
+ raise HTTPError(url, response.status_int, content, headers, None)
+ if len(content) == 0:
+ return headers, None
+ # XXX Work around for http://bugs.python.org/issue10038
+ return headers, response.json
+ except HTTPError:
+ raise
+ except IOError:
+ raise mailmanclient.MailmanConnectionError(
+ 'Could not connect to Mailman API')
+
+
+
+class FakeMailmanClient(mailmanclient.Client):
+ """
+ Subclass of mailmanclient.Client to instantiate a FakeConnection object
+ instead of the real connection.
+ """
+
+ def __init__(self, baseurl, name=None, password=None):
+ self._connection = FakeConnection(baseurl, name, password)
+
+ @property
+ def called_paths(self):
+ return self._connection.called_paths
+
+ @classmethod
+ def setUp(self):
+ ConfigLayer.setUp()
+
+ @classmethod
+ def tearDown(self):
+ config.create_paths = False # or ConfigLayer.tearDown will create them
+ ConfigLayer.testTearDown()
+ ConfigLayer.tearDown()
+ reset_mailman_config()
+
+def reset_mailman_config():
+ # This is necessary because ConfigLayer.setup/tearDown was designed to be
+ # run only once for the whole test suite, and thus does not reset
+ # everything afterwards
+ for prop in ("switchboards", "rules", "chains", "handlers",
+ "pipelines", "commands"):
+ getattr(config, prop).clear()
+ from mailman.model.user import uid_factory
+ uid_factory._lockobj = None
+ from mailman.model.member import uid_factory
+ uid_factory._lockobj = None
_______________________________________________
Mailman-coders mailing list
[email protected]
https://mail.python.org/mailman/listinfo/mailman-coders