Merge lp:~nkarageuzian/mailman/usermanagement into lp:mailman

Proposed by nicolask
Status: Rejected
Rejected by: Barry Warsaw
Proposed branch: lp:~nkarageuzian/mailman/usermanagement
Merge into: lp:mailman
Diff against target: 103 lines (+81/-3)
2 files modified
src/mailman/model/tests/test_usermanager.py (+67/-0)
src/mailman/model/usermanager.py (+14/-3)
To merge this branch: bzr merge lp:~nkarageuzian/mailman/usermanagement
Reviewer Review Type Date Requested Status
Mailman Coders Pending
Review via email: mp+200128@code.launchpad.net

Description of the change

solves the use case:
user sends a mail to the list without subscribing nor registering
admin accepts message
user wants to register to mailman through API but
create_user method fails as sender's address is registered.

and add tests for usermanager and the create_user function.

To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :
Download full text (5.0 KiB)

nicolask - thanks very much for the contribution to Mailman! I'm afraid I'm going to have to reject this merge proposal though because I think this issue needs to be solved at a different level. The model tries to be as simple as possible, and because the IUser object is a fundamental object, the IUserManager.create_user() method should not implement these additional semantics. On the mailing list, I outlined an approach that I think would be better.

In the hopes that you'll continue to contribute to Mailman, I'd like to offer some additional style feedback on your diff. Again, thanks for working on Mailman!

=== added file 'src/mailman/model/tests/test_usermanager.py'
--- src/mailman/model/tests/test_usermanager.py 1970-01-01 00:00:00 +0000
+++ src/mailman/model/tests/test_usermanager.py 2013-12-28 10:18:08 +0000
> +class TestUserManager(unittest.TestCase):
> + """Test usermanager."""
> +
> + layer = ConfigLayer
> +
> + def setUp(self):
> + self._mlist = create_list('<email address hidden>')
> + #getUtility(IAddress)('<email address hidden>','No one')

Of course, you can get rid of that commented out line, but also, since your
tests all use the IUserManager utility, it might be better to do something
like this in the setUp():

        self._user_manager = getUtility(IUserManager)

and then use self._user_manager instead of having to get the utility every
time.

> +
> + def test_create_user(self):
> + anne = getUtility(IUserManager).create_user(
> + '<email address hidden>', 'Anne Person')
> +
> + def test_create_user_existing_address_user(self):
> + anne = getUtility(IUserManager).create_user(
> + '<email address hidden>', 'Anne Person')
> + self.assertRaises(ExistingAddressError, getUtility(IUserManager).create_user,*(
> + '<email address hidden>', 'Anne Person'))

You'll want to make sure this line is less than 80 characters wide. Also, you
should need the * there. I think the following would work better:

        self.assertRaises(ExistingAddressError,
                          self._user_manager.create_user,
                          '<email address hidden>', 'Anne Person')

> +
> + def test_create_user_existing_address_no_user(self):
> + anne = getUtility(IUserManager).create_user(
> + '<email address hidden>', 'Anne Person')
> + noone = getUtility(IUserManager).create_user(
> + '<email address hidden>','No one')
> + self.assertTrue(anne.user_id != noone.user_id)

It would be better to use self.assertNotEqual() here, since the error message
is better should the test ever fail. (Also, remember, a single space comes
after commas in an argument list.)

https://docs.python.org/2.7/library/unittest.html?highlight=unittest#assert-methods

=== modified file 'src/mailman/model/usermanager.py'
--- src/mailman/model/usermanager.py 2013-01-01 14:05:42 +0000
+++ src/mailman/model/usermanager.py 2013-12-28 10:18:08 +0000
> @@ -40,13 +40,24 @@
> @implementer(IUserManager)
> class UserManager:
> """See `IUserManager`."""
> -
> - def create_user(self, email=None, display_name=None):
> + @dbconnection
> + def create_user(s...

Read more...

Revision history for this message
nicolask (nkarageuzian) wrote :

barry,

No problems and many thanks for the time you spent for writing style hints.

I've read #1312884 and plan to contribute on it as I agree with the aproach proposed.
I guess I could get one more hint (file ref) to dive quickly in test writings for that.

Revision history for this message
Barry Warsaw (barry) wrote :

Writing tests for the REST API is actually pretty easy. In general, the
doctests in docs/ should be used to document how to use the API for the common
cases, and it should only document good-path uses (i.e. not error or corner
conditions). Use unittests for bad-path, error condition, or corner cases.

I think good examples of these are the user tests.

Here's the unittests: http://tinyurl.com/mldysb2

And the doctests: http://tinyurl.com/mqtacm9

Hope that helps and don't worry; I'll do a thorough review and help you get
your contribution landed. Thanks again!

Unmerged revisions

7232. By nicolask

improve usermanagement : allow create_user method even if an address is known (mail sent to the list without subscribing).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/mailman/model/tests/test_usermanager.py'
2--- src/mailman/model/tests/test_usermanager.py 1970-01-01 00:00:00 +0000
3+++ src/mailman/model/tests/test_usermanager.py 2013-12-28 10:18:08 +0000
4@@ -0,0 +1,67 @@
5+# Copyright (C) 2011-2013 by the Free Software Foundation, Inc.
6+#
7+# This file is part of GNU Mailman.
8+#
9+# GNU Mailman is free software: you can redistribute it and/or modify it under
10+# the terms of the GNU General Public License as published by the Free
11+# Software Foundation, either version 3 of the License, or (at your option)
12+# any later version.
13+#
14+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
15+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17+# more details.
18+#
19+# You should have received a copy of the GNU General Public License along with
20+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
21+
22+"""Test usermanager."""
23+
24+from __future__ import absolute_import, unicode_literals
25+
26+__metaclass__ = type
27+__all__ = [
28+ 'TestUserManager',
29+ ]
30+
31+
32+import unittest
33+
34+from zope.component import getUtility
35+
36+from mailman.app.lifecycle import create_list
37+from mailman.interfaces.usermanager import IUserManager
38+from mailman.interfaces.address import ExistingAddressError
39+from mailman.interfaces.address import IAddress
40+from mailman.testing.layers import ConfigLayer
41+from mailman.utilities.datetime import now
42+
43+
44+
45
46+class TestUserManager(unittest.TestCase):
47+ """Test usermanager."""
48+
49+ layer = ConfigLayer
50+
51+ def setUp(self):
52+ self._mlist = create_list('test@example.com')
53+ #getUtility(IAddress)('no_one@example.com','No one')
54+
55+ def test_create_user(self):
56+ anne = getUtility(IUserManager).create_user(
57+ 'anne@example.com', 'Anne Person')
58+
59+ def test_create_user_existing_address_user(self):
60+ anne = getUtility(IUserManager).create_user(
61+ 'anne@example.com', 'Anne Person')
62+ self.assertRaises(ExistingAddressError, getUtility(IUserManager).create_user,*(
63+ 'anne@example.com', 'Anne Person'))
64+
65+ def test_create_user_existing_address_no_user(self):
66+ anne = getUtility(IUserManager).create_user(
67+ 'anne@example.com', 'Anne Person')
68+ noone = getUtility(IUserManager).create_user(
69+ 'no_one@example.com','No one')
70+ self.assertTrue(anne.user_id != noone.user_id)
71+
72+
73
74=== modified file 'src/mailman/model/usermanager.py'
75--- src/mailman/model/usermanager.py 2013-01-01 14:05:42 +0000
76+++ src/mailman/model/usermanager.py 2013-12-28 10:18:08 +0000
77@@ -40,13 +40,24 @@
78 @implementer(IUserManager)
79 class UserManager:
80 """See `IUserManager`."""
81-
82- def create_user(self, email=None, display_name=None):
83+ @dbconnection
84+ def create_user(self, store, email=None, display_name=None):
85 """See `IUserManager`."""
86 user = User(display_name, Preferences())
87 if email:
88- address = self.create_address(email, display_name)
89+ address = None
90+ try:
91+ address = self.create_address(email, display_name)
92+ except:
93+ addresses = store.find(Address, email=email.lower())
94+ if addresses.count() == 0:
95+ raise Exception("inconsistent results")
96+ if addresses.one().user == None:
97+ address = addresses.one()
98+ else:
99+ raise ExistingAddressError(email)
100 user.link(address)
101+ store.add(user)
102 return user
103
104 @dbconnection