Merge lp:~sinzui/launchpad/merge-non-active-person into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 16055
Proposed branch: lp:~sinzui/launchpad/merge-non-active-person
Merge into: lp:launchpad
Diff against target: 82 lines (+49/-1)
2 files modified
lib/lp/services/verification/browser/logintoken.py (+2/-1)
lib/lp/services/verification/browser/tests/test_logintoken.py (+47/-0)
To merge this branch: bzr merge lp:~sinzui/launchpad/merge-non-active-person
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+127041@code.launchpad.net

Commit message

Allow users to merge non-active accounts.

Description of the change

MergePeopleView.initialize() uses PersonSet.getByEmail() in a way that
won't find people by a NEW email address, causing merges of unactivated
accounts to fail like OOPS-e2f5d3b6769c012de049daf4a60c0941.

This is basically the same as bug #1019975, just at a later stage.
The fix was to pass filter_status=False to the method so that NEW
addresses can be used to look up the IPerson.

--------------------------------------------------------------------

RULES

    Pre-implementation: no one
    * Add a test for the current case with active accounts and validated
      email addresses.
    * Setup the same test with a NEW email address and non-active account.
      Expect the LocationError: (None, 'name') result.
    * Add filter_status=False to the view's call to PersonSet.getByEmail()
      and verify the test passes.

QA

    * Visit https://qastaging.launchpad.net/~ddw
    * Follow the "Are you Ddw" link and Continue to send an confirmation email.
    * Look in qastaging db for the token associated with your address:
      select * from logintoken where created > '2012-09-28';
    * Visit https://qastaging.launchpad.net/token/<TOKEN/+accountmerge
    * Verify the page loads.
    * Confirm
    * Verify the page loads and you see that the merge is queued.

LINT

    lib/lp/services/verification/browser/logintoken.py
    lib/lp/services/verification/browser/tests/test_logintoken.py

LoC

    I have a 3000 line credit at the moment.

TEST

    ./bin/test -vvc -t MergePeopleView lp.services.verification.browser.tests

IMPLEMENTATION

This is a rare case where the implementation plan was exactly as expected.
    lib/lp/services/verification/browser/logintoken.py
    lib/lp/services/verification/browser/tests/test_logintoken.py

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Looks good to me. I'd prefer sticking to 'example.com' for fake addresses but I'm an old fuddy-duddy.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/services/verification/browser/logintoken.py'
2--- lib/lp/services/verification/browser/logintoken.py 2012-08-14 23:19:36 +0000
3+++ lib/lp/services/verification/browser/logintoken.py 2012-09-28 18:28:25 +0000
4@@ -535,7 +535,8 @@
5
6 def initialize(self):
7 self.redirectIfInvalidOrConsumedToken()
8- self.dupe = getUtility(IPersonSet).getByEmail(self.context.email)
9+ self.dupe = getUtility(IPersonSet).getByEmail(
10+ self.context.email, filter_status=False)
11
12 def success(self, message):
13 # We're not a GeneralFormView, so we need to do the redirect
14
15=== modified file 'lib/lp/services/verification/browser/tests/test_logintoken.py'
16--- lib/lp/services/verification/browser/tests/test_logintoken.py 2012-06-14 05:18:22 +0000
17+++ lib/lp/services/verification/browser/tests/test_logintoken.py 2012-09-28 18:28:25 +0000
18@@ -4,6 +4,7 @@
19 from zope.component import getUtility
20 from zope.security.proxy import removeSecurityProxy
21
22+from lp.services.identity.interfaces.account import AccountStatus
23 from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
24 from lp.services.verification.browser.logintoken import (
25 ClaimTeamView,
26@@ -18,6 +19,7 @@
27 )
28 from lp.testing.deprecated import LaunchpadFormHarness
29 from lp.testing.layers import DatabaseFunctionalLayer
30+from lp.testing.views import create_initialized_view
31
32
33 class TestCancelActionOnLoginTokenViews(TestCaseWithFactory):
34@@ -100,3 +102,48 @@
35 msgs = self._claimToken(token2)
36 self.assertEquals(
37 [u'claimee has already been converted to a team.'], msgs)
38+
39+
40+class MergePeopleViewTestCase(TestCaseWithFactory):
41+ """Test the view for confirming a merge via login token."""
42+
43+ layer = DatabaseFunctionalLayer
44+
45+ def assertWorkflow(self, claimer, dupe):
46+ token = getUtility(ILoginTokenSet).new(
47+ requester=claimer, requesteremail='me@example.com',
48+ email="him@example.com", tokentype=LoginTokenType.ACCOUNTMERGE)
49+ view = create_initialized_view(token, name="+accountmerge")
50+ self.assertIs(False, view.mergeCompleted)
51+ self.assertTextMatchesExpressionIgnoreWhitespace(
52+ '.*to merge the Launchpad account named.*claimer', view.render())
53+ view = create_initialized_view(
54+ token, name="+accountmerge", principal=claimer,
55+ form={'VALIDATE': 'Confirm'}, method='POST')
56+ with person_logged_in(claimer):
57+ view.render()
58+ self.assertIs(True, view.mergeCompleted)
59+ notifications = view.request.notifications
60+ self.assertEqual(2, len(notifications))
61+ text = notifications[0].message
62+ self.assertTextMatchesExpressionIgnoreWhitespace(
63+ "A merge is queued.*", text)
64+
65+ def test_confirm_email_for_active_account(self):
66+ # Users can confirm they control an email address to merge a duplicate
67+ # profile.
68+ claimer = self.factory.makePerson(
69+ email='me@example.com', name='claimer')
70+ dupe = self.factory.makePerson(email='him@example.com', name='dupe')
71+ self.assertWorkflow(claimer, dupe)
72+
73+ def test_confirm_email_for_non_active_account(self):
74+ # Users can confirm they control an email address to merge a
75+ # non-active duplicate profile.
76+ claimer = self.factory.makePerson(
77+ email='me@example.com', name='claimer')
78+ dupe = self.factory.makePerson(
79+ email='him@example.com', name='dupe',
80+ email_address_status=EmailAddressStatus.NEW,
81+ account_status=AccountStatus.NOACCOUNT)
82+ self.assertWorkflow(claimer, dupe)