Add Python APIs for account/profile information to user_api Updating profile page to have social linking Authors: Renzo Lucioni, Alasdair Swan, Stephen Sanchez, Will Daly
277 lines
11 KiB
Python
277 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
""" Tests for the account API. """
|
|
|
|
import unittest
|
|
from nose.tools import raises
|
|
import ddt
|
|
from dateutil.parser import parse as parse_datetime
|
|
from django.conf import settings
|
|
from django.test import TestCase
|
|
from user_api.api import account as account_api
|
|
from user_api.models import UserProfile
|
|
|
|
|
|
@ddt.ddt
|
|
class AccountApiTest(TestCase):
|
|
|
|
USERNAME = u"frank-underwood"
|
|
PASSWORD = u"ṕáśśẃőŕd"
|
|
EMAIL = u"frank+underwood@example.com"
|
|
|
|
INVALID_USERNAMES = [
|
|
None,
|
|
u"",
|
|
u"a",
|
|
u"a" * (account_api.USERNAME_MAX_LENGTH + 1),
|
|
u"invalid_symbol_@",
|
|
u"invalid-unicode_fŕáńḱ",
|
|
]
|
|
|
|
INVALID_EMAILS = [
|
|
None,
|
|
u"",
|
|
u"a",
|
|
"no_domain",
|
|
"no+domain",
|
|
"@",
|
|
"@domain.com",
|
|
"test@no_extension",
|
|
|
|
# Long email -- subtract the length of the @domain
|
|
# except for one character (so we exceed the max length limit)
|
|
u"{user}@example.com".format(
|
|
user=(u'e' * (account_api.EMAIL_MAX_LENGTH - 11))
|
|
)
|
|
]
|
|
|
|
INVALID_PASSWORDS = [
|
|
None,
|
|
u"",
|
|
u"a",
|
|
u"a" * (account_api.PASSWORD_MAX_LENGTH + 1)
|
|
]
|
|
|
|
def test_activate_account(self):
|
|
# Create the account, which is initially inactive
|
|
activation_key = account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
account = account_api.account_info(self.USERNAME)
|
|
self.assertEqual(account, {
|
|
'username': self.USERNAME,
|
|
'email': self.EMAIL,
|
|
'is_active': False
|
|
})
|
|
|
|
# Activate the account and verify that it is now active
|
|
account_api.activate_account(activation_key)
|
|
account = account_api.account_info(self.USERNAME)
|
|
self.assertTrue(account['is_active'])
|
|
|
|
def test_change_email(self):
|
|
# Request an email change
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
activation_key = account_api.request_email_change(
|
|
self.USERNAME, u"new+email@example.com", self.PASSWORD
|
|
)
|
|
|
|
# Verify that the email has not yet changed
|
|
account = account_api.account_info(self.USERNAME)
|
|
self.assertEqual(account['email'], self.EMAIL)
|
|
|
|
# Confirm the change, using the activation code
|
|
old_email, new_email = account_api.confirm_email_change(activation_key)
|
|
self.assertEqual(old_email, self.EMAIL)
|
|
self.assertEqual(new_email, u"new+email@example.com")
|
|
|
|
# Verify that the email is changed
|
|
account = account_api.account_info(self.USERNAME)
|
|
self.assertEqual(account['email'], u"new+email@example.com")
|
|
|
|
def test_confirm_email_change_repeat(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
activation_key = account_api.request_email_change(
|
|
self.USERNAME, u"new+email@example.com", self.PASSWORD
|
|
)
|
|
|
|
# Confirm the change once
|
|
account_api.confirm_email_change(activation_key)
|
|
|
|
# Confirm the change again
|
|
# The activation code should be single-use
|
|
# so this should raise an error.
|
|
with self.assertRaises(account_api.AccountNotAuthorized):
|
|
account_api.confirm_email_change(activation_key)
|
|
|
|
def test_create_account_duplicate_username(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
with self.assertRaises(account_api.AccountUserAlreadyExists):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, 'different+email@example.com')
|
|
|
|
# Email uniqueness constraints were introduced in a database migration,
|
|
# which we disable in the unit tests to improve the speed of the test suite.
|
|
@unittest.skipUnless(settings.SOUTH_TESTS_MIGRATE, "South migrations required")
|
|
def test_create_account_duplicate_email(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
with self.assertRaises(account_api.AccountUserAlreadyExists):
|
|
account_api.create_account("different_user", self.PASSWORD, self.EMAIL)
|
|
|
|
def test_username_too_long(self):
|
|
long_username = 'e' * (account_api.USERNAME_MAX_LENGTH + 1)
|
|
with self.assertRaises(account_api.AccountUsernameInvalid):
|
|
account_api.create_account(long_username, self.PASSWORD, self.EMAIL)
|
|
|
|
def test_account_info_no_user(self):
|
|
self.assertIs(account_api.account_info("does_not_exist"), None)
|
|
|
|
@raises(account_api.AccountEmailInvalid)
|
|
@ddt.data(*INVALID_EMAILS)
|
|
def test_create_account_invalid_email(self, invalid_email):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, invalid_email)
|
|
|
|
@raises(account_api.AccountPasswordInvalid)
|
|
@ddt.data(*INVALID_PASSWORDS)
|
|
def test_create_account_invalid_password(self, invalid_password):
|
|
account_api.create_account(self.USERNAME, invalid_password, self.EMAIL)
|
|
|
|
@raises(account_api.AccountPasswordInvalid)
|
|
def test_create_account_username_password_equal(self):
|
|
# Username and password cannot be the same
|
|
account_api.create_account(self.USERNAME, self.USERNAME, self.EMAIL)
|
|
|
|
@raises(account_api.AccountRequestError)
|
|
@ddt.data(*INVALID_USERNAMES)
|
|
def test_create_account_invalid_username(self, invalid_username):
|
|
account_api.create_account(invalid_username, self.PASSWORD, self.EMAIL)
|
|
|
|
@raises(account_api.AccountNotAuthorized)
|
|
def test_activate_account_invalid_key(self):
|
|
account_api.activate_account(u"invalid")
|
|
|
|
@raises(account_api.AccountUserNotFound)
|
|
def test_request_email_change_no_user(self):
|
|
account_api.request_email_change(u"no_such_user", self.EMAIL, self.PASSWORD)
|
|
|
|
@ddt.data(*INVALID_EMAILS)
|
|
def test_request_email_change_invalid_email(self, invalid_email):
|
|
# Create an account with a valid email address
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
|
|
# Attempt to change the account to an invalid email
|
|
with self.assertRaises(account_api.AccountEmailInvalid):
|
|
account_api.request_email_change(self.USERNAME, invalid_email, self.PASSWORD)
|
|
|
|
def test_request_email_change_already_exists(self):
|
|
# Create two accounts, both activated
|
|
activation_key = account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
account_api.activate_account(activation_key)
|
|
activation_key = account_api.create_account(u"another_user", u"password", u"another+user@example.com")
|
|
account_api.activate_account(activation_key)
|
|
|
|
# Try to change the first user's email to the same as the second user's
|
|
with self.assertRaises(account_api.AccountEmailAlreadyExists):
|
|
account_api.request_email_change(self.USERNAME, u"another+user@example.com", self.PASSWORD)
|
|
|
|
def test_request_email_change_duplicates_unactivated_account(self):
|
|
# Create two accounts, but the second account is inactive
|
|
activation_key = account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
account_api.activate_account(activation_key)
|
|
account_api.create_account(u"another_user", u"password", u"another+user@example.com")
|
|
|
|
# Try to change the first user's email to the same as the second user's
|
|
# Since the second user has not yet activated, this should succeed.
|
|
account_api.request_email_change(self.USERNAME, u"another+user@example.com", self.PASSWORD)
|
|
|
|
def test_request_email_change_same_address(self):
|
|
# Create and activate the account
|
|
activation_key = account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
account_api.activate_account(activation_key)
|
|
|
|
# Try to change the email address to the current address
|
|
with self.assertRaises(account_api.AccountEmailAlreadyExists):
|
|
account_api.request_email_change(self.USERNAME, self.EMAIL, self.PASSWORD)
|
|
|
|
def test_request_email_change_wrong_password(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
|
|
# Use the wrong password
|
|
with self.assertRaises(account_api.AccountNotAuthorized):
|
|
account_api.request_email_change(self.USERNAME, u"new+email@example.com", u"wrong password")
|
|
|
|
def test_confirm_email_change_invalid_activation_key(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
account_api.request_email_change(self.USERNAME, u"new+email@example.com", self.PASSWORD)
|
|
|
|
with self.assertRaises(account_api.AccountNotAuthorized):
|
|
account_api.confirm_email_change(u"invalid")
|
|
|
|
def test_confirm_email_change_no_request_pending(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
|
|
def test_confirm_email_already_exists(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
|
|
# Request a change
|
|
activation_key = account_api.request_email_change(
|
|
self.USERNAME, u"new+email@example.com", self.PASSWORD
|
|
)
|
|
|
|
# Another use takes the email before we confirm the change
|
|
account_api.create_account(u"other_user", u"password", u"new+email@example.com")
|
|
|
|
# When we try to confirm our change, we get an error because the email is taken
|
|
with self.assertRaises(account_api.AccountEmailAlreadyExists):
|
|
account_api.confirm_email_change(activation_key)
|
|
|
|
# Verify that the email was NOT changed
|
|
self.assertEqual(account_api.account_info(self.USERNAME)['email'], self.EMAIL)
|
|
|
|
def test_confirm_email_no_user_profile(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
activation_key = account_api.request_email_change(
|
|
self.USERNAME, u"new+email@example.com", self.PASSWORD
|
|
)
|
|
|
|
# This should never happen, but just in case...
|
|
UserProfile.objects.get(user__username=self.USERNAME).delete()
|
|
|
|
with self.assertRaises(account_api.AccountInternalError):
|
|
account_api.confirm_email_change(activation_key)
|
|
|
|
def test_record_email_change_history(self):
|
|
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
|
|
|
|
# Change the email once
|
|
activation_key = account_api.request_email_change(
|
|
self.USERNAME, u"new+email@example.com", self.PASSWORD
|
|
)
|
|
account_api.confirm_email_change(activation_key)
|
|
|
|
# Verify that the old email appears in the history
|
|
meta = UserProfile.objects.get(user__username=self.USERNAME).get_meta()
|
|
self.assertEqual(len(meta['old_emails']), 1)
|
|
email, timestamp = meta['old_emails'][0]
|
|
self.assertEqual(email, self.EMAIL)
|
|
self._assert_is_datetime(timestamp)
|
|
|
|
# Change the email again
|
|
activation_key = account_api.request_email_change(
|
|
self.USERNAME, u"another_new+email@example.com", self.PASSWORD
|
|
)
|
|
account_api.confirm_email_change(activation_key)
|
|
|
|
# Verify that both emails appear in the history
|
|
meta = UserProfile.objects.get(user__username=self.USERNAME).get_meta()
|
|
self.assertEqual(len(meta['old_emails']), 2)
|
|
email, timestamp = meta['old_emails'][1]
|
|
self.assertEqual(email, "new+email@example.com")
|
|
self._assert_is_datetime(timestamp)
|
|
|
|
def _assert_is_datetime(self, timestamp):
|
|
if not timestamp:
|
|
return False
|
|
try:
|
|
parse_datetime(timestamp)
|
|
except ValueError:
|
|
return False
|
|
else:
|
|
return True
|