diff --git a/setup.py b/setup.py index 1f6fda5..de67453 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ 'Django>=1.8', 'Django<1.12', 'django-mailman3', - 'mailmanclient', + 'mailmanclient~=3.1.1a1' ], tests_require=[ "mock", diff --git a/src/postorius/templatetags/membership_helpers.py b/src/postorius/templatetags/membership_helpers.py index fed0048..6c21fc5 100644 --- a/src/postorius/templatetags/membership_helpers.py +++ b/src/postorius/templatetags/membership_helpers.py @@ -23,7 +23,7 @@ from postorius.auth.utils import user_is_in_list_roster from postorius.models import List -from mailmanclient._client import MailingList +from mailmanclient import MailingList register = template.Library() diff --git a/src/postorius/tests/mailman_api_tests/test_subscriptions.py b/src/postorius/tests/mailman_api_tests/test_subscriptions.py index 30b2801..ffdc973 100644 --- a/src/postorius/tests/mailman_api_tests/test_subscriptions.py +++ b/src/postorius/tests/mailman_api_tests/test_subscriptions.py @@ -54,7 +54,7 @@ for address in self.mm_user.addresses: address.verify() - @patch('mailmanclient._client.MailingList.subscribe') + @patch('mailmanclient.MailingList.subscribe') def test_anonymous_subscribe(self, mock_subscribe): response = self.client.post( reverse('list_anonymous_subscribe', diff --git a/src/postorius/tests/utils.py b/src/postorius/tests/utils.py index 3b42c75..167448c 100644 --- a/src/postorius/tests/utils.py +++ b/src/postorius/tests/utils.py @@ -18,6 +18,7 @@ from __future__ import absolute_import, unicode_literals import os +import vcr import logging from django.conf import settings @@ -25,16 +26,66 @@ from django.core.urlresolvers import reverse from django.test import TestCase from mock import MagicMock -from six.moves.urllib_parse import quote +from six import binary_type, text_type, PY3 +from six.moves.urllib_parse import ( + quote, urlparse, urlunparse, parse_qsl, urlencode) from django_mailman3.lib.mailman import get_mailman_client from django_mailman3.tests.utils import get_flash_messages -from mailmanclient.testing.vcr_helpers import get_vcr vcr_log = logging.getLogger('vcr') vcr_log.setLevel(logging.WARNING) +def reorder_request_params(request): + def reorder_params(params): + if PY3: + if isinstance(params, binary_type): + params = params.decode("ascii") + parsed = parse_qsl(params, encoding="utf-8") + else: + parsed = parse_qsl(params) + if parsed: + return urlencode(sorted(parsed, key=lambda kv: kv[0])) + else: + # Parsing failed, it may be a simple string. + return params + # sort the URL query-string by key names. + uri_parts = urlparse(request.uri) + if uri_parts.query: + request.uri = urlunparse(( + uri_parts.scheme, uri_parts.netloc, uri_parts.path, + uri_parts.params, reorder_params(uri_parts.query), + uri_parts.fragment, + )) + # convert the request body to text and sort the parameters. + if isinstance(request.body, binary_type): + try: + request._body = request._body.decode('utf-8') + except UnicodeDecodeError: + pass + if isinstance(request.body, text_type): + request._body = reorder_params(request._body.encode('utf-8')) + return request + + +def filter_response_headers(response): + for header in ('Date', 'Server', 'date', 'server'): + # The headers are lowercase on Python 2 and capitalized on Python 3 + if header in response['headers']: + del response['headers'][header] + return response + + +def get_vcr(**kwargs): + return vcr.VCR( + filter_headers=['authorization', 'user-agent', 'date'], + before_record=reorder_request_params, + before_record_response=filter_response_headers, + **kwargs + ) + + def create_mock_domain(properties=None): """Create and return a mocked Domain.