summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2014-04-15 17:54:35 -0400
committerBarry Warsaw2014-04-15 17:54:35 -0400
commit13823e4d2b9bea190dd207a02a8107bc43ee49fe (patch)
tree0917bab22a2d9d422567c80d2c27a4e601103ea2 /src
parent29d6d587389c32eb84d7faa51e3072e02e385c2d (diff)
downloadmailman-13823e4d2b9bea190dd207a02a8107bc43ee49fe.tar.gz
mailman-13823e4d2b9bea190dd207a02a8107bc43ee49fe.tar.zst
mailman-13823e4d2b9bea190dd207a02a8107bc43ee49fe.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/docs/NEWS.rst6
-rw-r--r--src/mailman/rest/docs/addresses.rst15
-rw-r--r--src/mailman/rest/docs/membership.rst138
-rw-r--r--src/mailman/rest/members.py23
-rw-r--r--src/mailman/rest/tests/test_membership.py129
-rw-r--r--src/mailman/rest/tests/test_users.py5
6 files changed, 251 insertions, 65 deletions
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst
index f41542a22..0e9a8b875 100644
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -33,6 +33,12 @@ REST
site-wide. [Joanna Skrzeszewska] (LP: #1158040)
* Addresses can be added to existing users, including display names, via the
REST API. [Florian Fuchs]
+ * Fixed a crash in the REST server when searching for nonmembers via
+ ``/find`` which we've never seen before, because those members only have an
+ address record, not a user record. This requires a small change in the API
+ where the JSON response's ``address`` key now contains the URL to the
+ address resource, the new ``email`` key contains the email address as a
+ string, and the ``user`` key is optional.
Commands
--------
diff --git a/src/mailman/rest/docs/addresses.rst b/src/mailman/rest/docs/addresses.rst
index be01dd623..fec0c194b 100644
--- a/src/mailman/rest/docs/addresses.rst
+++ b/src/mailman/rest/docs/addresses.rst
@@ -261,16 +261,18 @@ Elle can get her memberships for each of her email addresses.
>>> dump_json('http://localhost:9001/3.0/addresses/'
... 'elle@example.com/memberships')
entry 0:
- address: elle@example.com
+ address: http://localhost:9001/3.0/addresses/elle@example.com
delivery_mode: regular
+ email: elle@example.com
http_etag: "..."
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/2
entry 1:
- address: elle@example.com
+ address: http://localhost:9001/3.0/addresses/elle@example.com
delivery_mode: regular
+ email: elle@example.com
http_etag: "..."
list_id: bee.example.com
role: member
@@ -298,16 +300,18 @@ does not show up in the list of memberships for his other address.
>>> dump_json('http://localhost:9001/3.0/addresses/'
... 'elle@example.com/memberships')
entry 0:
- address: elle@example.com
+ address: http://localhost:9001/3.0/addresses/elle@example.com
delivery_mode: regular
+ email: elle@example.com
http_etag: "..."
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/2
entry 1:
- address: elle@example.com
+ address: http://localhost:9001/3.0/addresses/elle@example.com
delivery_mode: regular
+ email: elle@example.com
http_etag: "..."
list_id: bee.example.com
role: member
@@ -320,8 +324,9 @@ does not show up in the list of memberships for his other address.
>>> dump_json('http://localhost:9001/3.0/addresses/'
... 'eperson@example.com/memberships')
entry 0:
- address: eperson@example.com
+ address: http://localhost:9001/3.0/addresses/eperson@example.com
delivery_mode: regular
+ email: eperson@example.com
http_etag: "..."
list_id: bee.example.com
role: member
diff --git a/src/mailman/rest/docs/membership.rst b/src/mailman/rest/docs/membership.rst
index 10fd4732e..7c92d8343 100644
--- a/src/mailman/rest/docs/membership.rst
+++ b/src/mailman/rest/docs/membership.rst
@@ -41,8 +41,9 @@ the REST interface.
>>> subscribe(bee, 'Bart')
>>> dump_json('http://localhost:9001/3.0/members')
entry 0:
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -55,8 +56,9 @@ the REST interface.
Bart's specific membership can be accessed directly:
>>> dump_json('http://localhost:9001/3.0/members/1')
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -69,16 +71,18 @@ the REST interface.
>>> subscribe(bee, 'Cris')
>>> dump_json('http://localhost:9001/3.0/members')
entry 0:
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/1
entry 1:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -96,24 +100,27 @@ subscribes, she is returned first.
>>> dump_json('http://localhost:9001/3.0/members')
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/3
user: http://localhost:9001/3.0/users/3
entry 1:
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/1
entry 2:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -135,40 +142,45 @@ User ids are different than member ids.
>>> dump_json('http://localhost:9001/3.0/members')
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/4
user: http://localhost:9001/3.0/users/3
entry 1:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/5
user: http://localhost:9001/3.0/users/2
entry 2:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/3
user: http://localhost:9001/3.0/users/3
entry 3:
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/1
entry 4:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -183,16 +195,18 @@ We can also get just the members of a single mailing list.
>>> dump_json(
... 'http://localhost:9001/3.0/lists/ant@example.com/roster/member')
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/4
user: http://localhost:9001/3.0/users/3
entry 1:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
@@ -215,8 +229,9 @@ page.
... 'http://localhost:9001/3.0/lists/ant@example.com/roster/member'
... '?count=1&page=1')
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
@@ -231,8 +246,9 @@ This works with members of a single list as well as with all members.
>>> dump_json(
... 'http://localhost:9001/3.0/members?count=1&page=1')
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
@@ -275,56 +291,63 @@ mailing list.
>>> dump_json('http://localhost:9001/3.0/members')
entry 0:
- address: dperson@example.com
+ address: http://localhost:9001/3.0/addresses/dperson@example.com
delivery_mode: regular
+ email: dperson@example.com
http_etag: ...
list_id: ant.example.com
role: moderator
self_link: http://localhost:9001/3.0/members/6
user: http://localhost:9001/3.0/users/4
entry 1:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/4
user: http://localhost:9001/3.0/users/3
entry 2:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/5
user: http://localhost:9001/3.0/users/2
entry 3:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: owner
self_link: http://localhost:9001/3.0/members/7
user: http://localhost:9001/3.0/users/2
entry 4:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/3
user: http://localhost:9001/3.0/users/3
entry 5:
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/1
entry 6:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -339,8 +362,9 @@ We can access all the owners of a list.
>>> dump_json(
... 'http://localhost:9001/3.0/lists/bee@example.com/roster/owner')
entry 0:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: owner
@@ -358,8 +382,9 @@ A specific member can always be referenced by their role and address.
>>> dump_json('http://localhost:9001/3.0/lists/'
... 'bee@example.com/owner/cperson@example.com')
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: owner
@@ -373,16 +398,18 @@ example, we can search for all the memberships of a particular address.
... 'subscriber': 'aperson@example.com',
... })
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/4
user: http://localhost:9001/3.0/users/3
entry 1:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -398,32 +425,36 @@ Or, we can find all the memberships for a particular mailing list.
... 'list_id': 'bee.example.com',
... })
entry 0:
- address: aperson@example.com
+ address: http://localhost:9001/3.0/addresses/aperson@example.com
delivery_mode: regular
+ email: aperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/3
user: http://localhost:9001/3.0/users/3
entry 1:
- address: bperson@example.com
+ address: http://localhost:9001/3.0/addresses/bperson@example.com
delivery_mode: regular
+ email: bperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/1
user: http://localhost:9001/3.0/users/1
entry 2:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/2
user: http://localhost:9001/3.0/users/2
entry 3:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: owner
@@ -441,16 +472,18 @@ list.
... 'list_id': 'bee.example.com',
... })
entry 0:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
self_link: http://localhost:9001/3.0/members/2
user: http://localhost:9001/3.0/users/2
entry 1:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: owner
@@ -467,16 +500,18 @@ Or, we can find all the memberships for an address with a specific role.
... 'role': 'member',
... })
entry 0:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/5
user: http://localhost:9001/3.0/users/2
entry 1:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -494,8 +529,9 @@ Finally, we can search for a specific member given all three criteria.
... 'role': 'member',
... })
entry 0:
- address: cperson@example.com
+ address: http://localhost:9001/3.0/addresses/cperson@example.com
delivery_mode: regular
+ email: cperson@example.com
http_etag: ...
list_id: bee.example.com
role: member
@@ -542,8 +578,9 @@ Elly is now a known user, and a member of the mailing list.
entry 0:
...
entry 3:
- address: eperson@example.com
+ address: http://localhost:9001/3.0/addresses/eperson@example.com
delivery_mode: regular
+ email: eperson@example.com
http_etag: ...
list_id: ant.example.com
role: member
@@ -583,8 +620,9 @@ list with her preferred address.
entry 0:
...
entry 4:
- address: gwen@example.com
+ address: http://localhost:9001/3.0/addresses/gwen@example.com
delivery_mode: regular
+ email: gwen@example.com
http_etag: "..."
list_id: ant.example.com
role: member
@@ -606,8 +644,9 @@ the new address.
entry 0:
...
entry 4:
- address: gwen.person@example.com
+ address: http://localhost:9001/3.0/addresses/gwen.person@example.com
delivery_mode: regular
+ email: gwen.person@example.com
http_etag: "..."
list_id: ant.example.com
role: member
@@ -671,8 +710,9 @@ Fred is getting MIME deliveries.
DeliveryMode.mime_digests
>>> dump_json('http://localhost:9001/3.0/members/10')
- address: fperson@example.com
+ address: http://localhost:9001/3.0/addresses/fperson@example.com
delivery_mode: mime_digests
+ email: fperson@example.com
http_etag: "..."
list_id: ant.example.com
role: member
@@ -693,8 +733,9 @@ This can be done by PATCH'ing his member with the `delivery_mode` parameter.
status: 204
>>> dump_json('http://localhost:9001/3.0/members/10')
- address: fperson@example.com
+ address: http://localhost:9001/3.0/addresses/fperson@example.com
delivery_mode: regular
+ email: fperson@example.com
http_etag: "..."
list_id: ant.example.com
role: member
@@ -711,8 +752,9 @@ If a PATCH request changes no attributes, nothing happens.
status: 204
>>> dump_json('http://localhost:9001/3.0/members/10')
- address: fperson@example.com
+ address: http://localhost:9001/3.0/addresses/fperson@example.com
delivery_mode: regular
+ email: fperson@example.com
http_etag: "..."
list_id: ant.example.com
role: member
@@ -753,8 +795,9 @@ addresses.
entry 0:
...
entry 5:
- address: herb@example.com
+ address: http://localhost:9001/3.0/addresses/herb@example.com
delivery_mode: regular
+ email: herb@example.com
http_etag: "..."
list_id: ant.example.com
role: member
@@ -762,8 +805,9 @@ addresses.
user: http://localhost:9001/3.0/users/8
...
entry 10:
- address: herb@example.com
+ address: http://localhost:9001/3.0/addresses/herb@example.com
delivery_mode: regular
+ email: herb@example.com
http_etag: "..."
list_id: bee.example.com
role: member
@@ -818,16 +862,18 @@ his membership ids have not changed.
>>> dump_json('http://localhost:9001/3.0/addresses/'
... 'hperson@example.com/memberships')
entry 0:
- address: hperson@example.com
+ address: http://localhost:9001/3.0/addresses/hperson@example.com
delivery_mode: regular
+ email: hperson@example.com
http_etag: "..."
list_id: ant.example.com
role: member
self_link: http://localhost:9001/3.0/members/11
user: http://localhost:9001/3.0/users/8
entry 1:
- address: hperson@example.com
+ address: http://localhost:9001/3.0/addresses/hperson@example.com
delivery_mode: regular
+ email: hperson@example.com
http_etag: "..."
list_id: bee.example.com
role: member
diff --git a/src/mailman/rest/members.py b/src/mailman/rest/members.py
index 5be7a4525..4aef93e34 100644
--- a/src/mailman/rest/members.py
+++ b/src/mailman/rest/members.py
@@ -56,18 +56,25 @@ class _MemberBase(resource.Resource, CollectionMixin):
def _resource_as_dict(self, member):
"""See `CollectionMixin`."""
enum, dot, role = str(member.role).partition('.')
- # Both the user_id and the member_id are UUIDs. We need to use the
- # integer equivalent in the URL.
- user_id = member.user.user_id.int
- member_id = member.member_id.int
- return dict(
+ # The member will always have a member id and an address id. It will
+ # only have a user id if the address is linked to a user.
+ # E.g. nonmembers we've only seen via postings to lists they are not
+ # subscribed to will not have a user id. The user_id and the
+ # member_id are UUIDs. We need to use the integer equivalent in the
+ # URL.
+ response = dict(
list_id=member.list_id,
- address=member.address.email,
+ email=member.address.email,
role=role,
- user=path_to('users/{0}'.format(user_id)),
- self_link=path_to('members/{0}'.format(member_id)),
+ address=path_to('addresses/{}'.format(member.address.email)),
+ self_link=path_to('members/{}'.format(member.member_id.int)),
delivery_mode=member.delivery_mode,
)
+ # Add the user link if there is one.
+ user = member.user
+ if user is not None:
+ response['user'] = path_to('users/{}'.format(user.user_id.int))
+ return response
@paginate
def _get_collection(self, request):
diff --git a/src/mailman/rest/tests/test_membership.py b/src/mailman/rest/tests/test_membership.py
index 037ef36b0..503d68a94 100644
--- a/src/mailman/rest/tests/test_membership.py
+++ b/src/mailman/rest/tests/test_membership.py
@@ -22,6 +22,7 @@ from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'TestMembership',
+ 'TestNonmembership',
]
@@ -34,8 +35,11 @@ from mailman.app.lifecycle import create_list
from mailman.config import config
from mailman.database.transaction import transaction
from mailman.interfaces.usermanager import IUserManager
-from mailman.testing.helpers import call_api
-from mailman.testing.layers import RESTLayer
+from mailman.testing.helpers import (
+ TestableMaster, call_api, get_lmtp_client, make_testable_runner,
+ wait_for_webservice)
+from mailman.runners.incoming import IncomingRunner
+from mailman.testing.layers import ConfigLayer, RESTLayer
from mailman.utilities.datetime import now
@@ -144,7 +148,10 @@ class TestMembership(unittest.TestCase):
'http://localhost:9001/3.0/members/1')
self.assertEqual(entry_0['role'], 'member')
self.assertEqual(entry_0['user'], 'http://localhost:9001/3.0/users/1')
- self.assertEqual(entry_0['address'], 'anne@example.com')
+ self.assertEqual(entry_0['email'], 'anne@example.com')
+ self.assertEqual(
+ entry_0['address'],
+ 'http://localhost:9001/3.0/addresses/anne@example.com')
self.assertEqual(entry_0['list_id'], 'test.example.com')
def test_member_changes_preferred_address(self):
@@ -158,7 +165,10 @@ class TestMembership(unittest.TestCase):
content, response = call_api('http://localhost:9001/3.0/members')
self.assertEqual(int(content['total_size']), 1)
entry_0 = content['entries'][0]
- self.assertEqual(entry_0['address'], 'anne@example.com')
+ self.assertEqual(entry_0['email'], 'anne@example.com')
+ self.assertEqual(
+ entry_0['address'],
+ 'http://localhost:9001/3.0/addresses/anne@example.com')
# Anne registers a new address and makes it her preferred address.
# There are no changes to her membership.
with transaction():
@@ -169,7 +179,10 @@ class TestMembership(unittest.TestCase):
content, response = call_api('http://localhost:9001/3.0/members')
self.assertEqual(int(content['total_size']), 1)
entry_0 = content['entries'][0]
- self.assertEqual(entry_0['address'], 'aperson@example.com')
+ self.assertEqual(entry_0['email'], 'aperson@example.com')
+ self.assertEqual(
+ entry_0['address'],
+ 'http://localhost:9001/3.0/addresses/aperson@example.com')
def test_get_nonexistent_member(self):
# /members/<bogus> returns 404
@@ -202,3 +215,109 @@ class TestMembership(unittest.TestCase):
with self.assertRaises(HTTPError) as cm:
call_api('http://localhost:9001/3.0/members/1/all')
self.assertEqual(cm.exception.code, 404)
+
+
+
+class CustomLayer(ConfigLayer):
+ """Custom layer which starts both the REST and LMTP servers."""
+
+ server = None
+ client = None
+
+ @classmethod
+ def _wait_for_both(cls):
+ cls.client = get_lmtp_client(quiet=True)
+ wait_for_webservice()
+
+ @classmethod
+ def setUp(cls):
+ assert cls.server is None, 'Layer already set up'
+ cls.server = TestableMaster(cls._wait_for_both)
+ cls.server.start('lmtp', 'rest')
+
+ @classmethod
+ def tearDown(cls):
+ assert cls.server is not None, 'Layer is not set up'
+ cls.server.stop()
+ cls.server = None
+
+
+class TestNonmembership(unittest.TestCase):
+ layer = CustomLayer
+
+ def setUp(self):
+ with transaction():
+ self._mlist = create_list('test@example.com')
+ self._usermanager = getUtility(IUserManager)
+
+ def _go(self, message):
+ lmtp = get_lmtp_client(quiet=True)
+ lmtp.lhlo('remote.example.org')
+ lmtp.sendmail('nonmember@example.com', ['test@example.com'], message)
+ lmtp.close()
+ # The message will now be sitting in the `in` queue. Run the incoming
+ # runner once to process it, which should result in the nonmember
+ # showing up.
+ inq = make_testable_runner(IncomingRunner, 'in')
+ inq.run()
+
+ def test_nonmember_findable_after_posting(self):
+ # A nonmember we have never seen before posts a message to the mailing
+ # list. They are findable through the /members/find API using a role
+ # of nonmember.
+ self._go("""\
+From: nonmember@example.com
+To: test@example.com
+Subject: Nonmember post
+Message-ID: <alpha>
+
+Some text.
+""")
+ # Now use the REST API to try to find the nonmember.
+ response, content = call_api(
+ 'http://localhost:9001/3.0/members/find', {
+ #'list_id': 'test.example.com',
+ 'role': 'nonmember',
+ })
+ self.assertEqual(response['total_size'], 1)
+ nonmember = response['entries'][0]
+ self.assertEqual(nonmember['role'], 'nonmember')
+ self.assertEqual(nonmember['email'], 'nonmember@example.com')
+ self.assertEqual(
+ nonmember['address'],
+ 'http://localhost:9001/3.0/addresses/nonmember@example.com')
+ # There is no user key in the JSON data because there is no user
+ # record associated with the address record.
+ self.assertNotIn('user', nonmember)
+
+ def test_linked_nonmember_findable_after_posting(self):
+ # Like above, a nonmember posts a message to the mailing list. In
+ # this case though, the nonmember already has a user record. They are
+ # findable through the /members/find API using a role of nonmember.
+ with transaction():
+ self._usermanager.create_user('nonmember@example.com')
+ self._go("""\
+From: nonmember@example.com
+To: test@example.com
+Subject: Nonmember post
+Message-ID: <alpha>
+
+Some text.
+""")
+ # Now use the REST API to try to find the nonmember.
+ response, content = call_api(
+ 'http://localhost:9001/3.0/members/find', {
+ #'list_id': 'test.example.com',
+ 'role': 'nonmember',
+ })
+ self.assertEqual(response['total_size'], 1)
+ nonmember = response['entries'][0]
+ self.assertEqual(nonmember['role'], 'nonmember')
+ self.assertEqual(nonmember['email'], 'nonmember@example.com')
+ self.assertEqual(
+ nonmember['address'],
+ 'http://localhost:9001/3.0/addresses/nonmember@example.com')
+ # There is a user key in the JSON data because the address had
+ # previously been linked to a user record.
+ self.assertEqual(nonmember['user'],
+ 'http://localhost:9001/3.0/users/1')
diff --git a/src/mailman/rest/tests/test_users.py b/src/mailman/rest/tests/test_users.py
index 80bf9526d..10cc724a3 100644
--- a/src/mailman/rest/tests/test_users.py
+++ b/src/mailman/rest/tests/test_users.py
@@ -229,7 +229,10 @@ class TestLP1074374(unittest.TestCase):
content, response = call_api('http://localhost:9001/3.0/members')
self.assertEqual(content['total_size'], 1)
member = content['entries'][0]
- self.assertEqual(member['address'], 'anne@example.com')
+ self.assertEqual(
+ member['address'],
+ 'http://localhost:9001/3.0/addresses/anne@example.com')
+ self.assertEqual(member['email'], 'anne@example.com')
self.assertEqual(member['delivery_mode'], 'regular')
self.assertEqual(member['list_id'], 'test.example.com')
self.assertEqual(member['role'], 'member')