Barry Warsaw pushed to branch master at mailman / Mailman
Commits:
ff31bf58 by Barry Warsaw at 2016-01-03T21:59:35Z
Boost coverage.
Use coverage.ini to ignore a few common idioms, so as to reduce code clutter.
Invoke coverage slightly differently in the tox.ini.
- - - - -
e93affa9 by Barry Warsaw at 2016-01-04T08:24:12Z
More coverage.
- - - - -
10 changed files:
- coverage.ini
- src/mailman/app/commands.py
- src/mailman/chains/base.py
- src/mailman/rest/helpers.py
- src/mailman/rest/members.py
- src/mailman/rest/sub_moderation.py
- + src/mailman/rest/tests/test_helpers.py
- src/mailman/rest/tests/test_moderation.py
- src/mailman/utilities/uid.py
- tox.ini
Changes:
=====================================
coverage.ini
=====================================
--- a/coverage.ini
+++ b/coverage.ini
@@ -7,6 +7,12 @@ omit =
.tox/coverage/lib/python3.5/site-packages/*
*/test_*.py
+[report]
+exclude_lines =
+ pragma: no cover
+ raise NotImplementedError
+ assert\s
+
[paths]
source =
mailman
=====================================
src/mailman/app/commands.py
=====================================
--- a/src/mailman/app/commands.py
+++ b/src/mailman/app/commands.py
@@ -35,6 +35,6 @@ def initialize():
command = command_class()
verifyObject(IEmailCommand, command)
assert command_class.name not in config.commands, (
- 'Duplicate email command "{0}" found in {1}'.format(
+ 'Duplicate email command "{}" found in {}'.format(
command_class.name, command_class.__module__))
config.commands[command_class.name] = command_class()
=====================================
src/mailman/chains/base.py
=====================================
--- a/src/mailman/chains/base.py
+++ b/src/mailman/chains/base.py
@@ -71,7 +71,7 @@ class TerminalChainBase:
:param msg: The message.
:param msgdata: The message metadata.
"""
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError
def get_links(self, mlist, msg, msgdata):
"""See `IChain`."""
=====================================
src/mailman/rest/helpers.py
=====================================
--- a/src/mailman/rest/helpers.py
+++ b/src/mailman/rest/helpers.py
@@ -86,7 +86,7 @@ class ExtendedEncoder(json.JSONEncoder):
# It's up to the decoding validator to associate this name with
# the right Enum class.
return obj.name
- return json.JSONEncoder.default(self, obj)
+ return super().default(obj)
def etag(resource):
@@ -130,7 +130,7 @@ class CollectionMixin:
:return: The representation of the resource.
:rtype: dict
"""
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError
def _resource_as_json(self, resource):
"""Return the JSON formatted representation of the resource."""
@@ -147,7 +147,7 @@ class CollectionMixin:
:return: The collection
:rtype: list
"""
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError
def _paginate(self, request, collection):
"""Method to paginate through collection result lists.
=====================================
src/mailman/rest/members.py
=====================================
--- a/src/mailman/rest/members.py
+++ b/src/mailman/rest/members.py
@@ -100,7 +100,7 @@ class MemberCollection(_MemberBase):
"""
def _get_collection(self, request):
"""See `CollectionMixin`."""
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError
def on_get(self, request, response):
"""roster/[members|owners|moderators]"""
=====================================
src/mailman/rest/sub_moderation.py
=====================================
--- a/src/mailman/rest/sub_moderation.py
+++ b/src/mailman/rest/sub_moderation.py
@@ -79,7 +79,7 @@ class IndividualRequest(_ModerationBase):
bad_request(response, str(error))
return
action = arguments['action']
- if action is Action.defer:
+ if action in (Action.defer, Action.hold):
# At least see if the token is in the database.
pendable = self._pendings.confirm(self._token, expunge=False)
if pendable is None:
@@ -100,7 +100,8 @@ class IndividualRequest(_ModerationBase):
not_found(response)
else:
no_content(response)
- elif action is Action.reject:
+ else:
+ assert action is Action.reject, action
# Like discard but sends a rejection notice to the user.
pendable = self._pendings.confirm(self._token, expunge=True)
if pendable is None:
=====================================
src/mailman/rest/tests/test_helpers.py
=====================================
--- /dev/null
+++ b/src/mailman/rest/tests/test_helpers.py
@@ -0,0 +1,82 @@
+# Copyright (C) 2016 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""Additional tests for helpers."""
+
+__all__ = [
+ 'TestHelpers',
+ ]
+
+
+import unittest
+
+from datetime import timedelta
+from mailman.rest import helpers
+from mailman.testing.layers import ConfigLayer
+
+
+class FakeResponse:
+ def __init__(self):
+ self.body = 'not set'
+
+
+class Unserializable:
+ pass
+
+
+
+class TestHelpers(unittest.TestCase):
+ layer = ConfigLayer
+
+ def test_not_found_body_is_none(self):
+ response = FakeResponse()
+ helpers.not_found(response, body=None)
+ self.assertEqual(response.body, 'not set')
+
+ def test_accepted_body_is_not_none(self):
+ response = FakeResponse()
+ helpers.accepted(response, body='set')
+ self.assertEqual(response.body, 'set')
+
+ def test_bad_request_body_is_none(self):
+ response = FakeResponse()
+ helpers.bad_request(response, body=None)
+ self.assertEqual(response.body, 'not set')
+
+ def test_conflict_body_is_none(self):
+ response = FakeResponse()
+ helpers.conflict(response, body=None)
+ self.assertEqual(response.body, 'not set')
+
+ def test_forbidden_body_is_none(self):
+ response = FakeResponse()
+ helpers.forbidden(response, body=None)
+ self.assertEqual(response.body, 'not set')
+
+ def test_json_encoding_datetime_seconds(self):
+ resource = dict(interval=timedelta(seconds=2))
+ unjson = eval(helpers.etag(resource))
+ self.assertEqual(unjson['interval'], '0d2.0s')
+
+ def test_json_encoding_datetime_microseconds(self):
+ resource = dict(interval=timedelta(microseconds=2))
+ unjson = eval(helpers.etag(resource))
+ self.assertEqual(unjson['interval'], '0d2e-06s')
+
+ def test_json_encoding_default(self):
+ resource = dict(interval=Unserializable())
+ self.assertRaises(TypeError, helpers.etag, resource)
=====================================
src/mailman/rest/tests/test_moderation.py
=====================================
--- a/src/mailman/rest/tests/test_moderation.py
+++ b/src/mailman/rest/tests/test_moderation.py
@@ -341,3 +341,25 @@ class TestSubscriptionModeration(unittest.TestCase):
'/requests/bogus',
dict(action='reject'))
self.assertEqual(cm.exception.code, 404)
+
+ def test_hold_keeps_holding(self):
+ # POST to the request to continue holding it.
+ with transaction():
+ token, token_owner, member = self._registrar.register(self._anne)
+ # Anne's subscription request got held.
+ self.assertIsNone(member)
+ # Clear out the virgin queue, which currently contains the
+ # confirmation message sent to Anne.
+ get_queue_messages('virgin')
+ url = 'http://localhost:9001/3.0/lists/[email protected]/requests/{}'
+ content, response = call_api(url.format(token), dict(
+ action='hold',
+ ))
+ self.assertEqual(response.status, 204)
+ # Anne is not a member.
+ self.assertIsNone(self._mlist.members.get_member('[email protected]'))
+ # The request URL still exists.
+ content, response = call_api(url.format(token), dict(
+ action='defer',
+ ))
+ self.assertEqual(response.status, 204)
=====================================
src/mailman/utilities/uid.py
=====================================
--- a/src/mailman/utilities/uid.py
+++ b/src/mailman/utilities/uid.py
@@ -89,7 +89,7 @@ class _PredictableIDGenerator:
The type of the returned id is intended to be the type that
makes sense for the subclass overriding this method.
"""
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError
def _next_predictable_id(self):
"""Generate a predictable id for when Mailman being tested.
@@ -97,7 +97,7 @@ class _PredictableIDGenerator:
The type of the returned id is intended to be the type that
makes sense for the subclass overriding this method.
"""
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError
def _next_id(self):
with self._lock:
=====================================
tox.ini
=====================================
--- a/tox.ini
+++ b/tox.ini
@@ -24,9 +24,10 @@ rc = --rcfile={[coverage]rcfile}
[testenv:coverage]
basepython = python3.5
commands =
- coverage run {[coverage]rc} -m nose2 -v
- coverage combine {[coverage]rc}
- coverage html {[coverage]rc}
+ python -m coverage run {[coverage]rc} -m nose2 -v
+ python -m coverage combine {[coverage]rc}
+ python -m coverage html {[coverage]rc}
+ python -m coverage report -m {[coverage]rc}
#sitepackages = True
usedevelop = True
whitelist_externals = python-coverage
View it on GitLab:
https://gitlab.com/mailman/mailman/compare/f390e08c31477c476e1312d03771b68e22b42675...e93affa947002959138cf8a45f33bea5fa06d3f7
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe:
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org