refactor: Ran pyupgrade on openedx/core/djangoapps/user_api
This commit is contained in:
@@ -114,33 +114,33 @@ class RetirementTestCase(TestCase):
|
||||
`responses` for performance reasons.
|
||||
"""
|
||||
retirement_dict = {
|
||||
u'id': retirement.id,
|
||||
u'user': {
|
||||
u'id': retirement.user.id,
|
||||
u'username': retirement.user.username,
|
||||
u'email': retirement.user.email,
|
||||
u'profile': {
|
||||
u'id': retirement.user.profile.id,
|
||||
u'name': retirement.user.profile.name
|
||||
'id': retirement.id,
|
||||
'user': {
|
||||
'id': retirement.user.id,
|
||||
'username': retirement.user.username,
|
||||
'email': retirement.user.email,
|
||||
'profile': {
|
||||
'id': retirement.user.profile.id,
|
||||
'name': retirement.user.profile.name
|
||||
},
|
||||
},
|
||||
u'original_username': retirement.original_username,
|
||||
u'original_email': retirement.original_email,
|
||||
u'original_name': retirement.original_name,
|
||||
u'retired_username': retirement.retired_username,
|
||||
u'retired_email': retirement.retired_email,
|
||||
u'current_state': {
|
||||
u'id': retirement.current_state.id,
|
||||
u'state_name': retirement.current_state.state_name,
|
||||
u'state_execution_order': retirement.current_state.state_execution_order,
|
||||
'original_username': retirement.original_username,
|
||||
'original_email': retirement.original_email,
|
||||
'original_name': retirement.original_name,
|
||||
'retired_username': retirement.retired_username,
|
||||
'retired_email': retirement.retired_email,
|
||||
'current_state': {
|
||||
'id': retirement.current_state.id,
|
||||
'state_name': retirement.current_state.state_name,
|
||||
'state_execution_order': retirement.current_state.state_execution_order,
|
||||
},
|
||||
u'last_state': {
|
||||
u'id': retirement.last_state.id,
|
||||
u'state_name': retirement.last_state.state_name,
|
||||
u'state_execution_order': retirement.last_state.state_execution_order,
|
||||
'last_state': {
|
||||
'id': retirement.last_state.id,
|
||||
'state_name': retirement.last_state.state_name,
|
||||
'state_execution_order': retirement.last_state.state_execution_order,
|
||||
},
|
||||
u'created': retirement.created,
|
||||
u'modified': retirement.modified
|
||||
'created': retirement.created,
|
||||
'modified': retirement.modified
|
||||
}
|
||||
|
||||
if all_fields:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for behavior that is specific to the api methods (vs. the view methods).
|
||||
Most of the functionality is covered in test_views.py.
|
||||
@@ -7,6 +6,7 @@ Most of the functionality is covered in test_views.py.
|
||||
|
||||
import itertools
|
||||
import unicodedata
|
||||
from unittest.mock import Mock, patch
|
||||
import pytest
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
@@ -16,8 +16,6 @@ from django.http import HttpResponse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls import reverse
|
||||
from mock import Mock, patch
|
||||
from six import iteritems
|
||||
from social_django.models import UserSocialAuth
|
||||
from common.djangoapps.student.models import (
|
||||
AccountRecovery,
|
||||
@@ -52,7 +50,7 @@ from openedx.features.enterprise_support.tests.factories import EnterpriseCustom
|
||||
|
||||
def mock_render_to_string(template_name, context):
|
||||
"""Return a string that encodes template_name and context"""
|
||||
return str((template_name, sorted(iteritems(context))))
|
||||
return str((template_name, sorted(context.items())))
|
||||
|
||||
|
||||
def mock_render_to_response(template_name):
|
||||
@@ -64,7 +62,7 @@ def mock_render_to_response(template_name):
|
||||
return HttpResponse(template_name)
|
||||
|
||||
|
||||
class CreateAccountMixin(object): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
class CreateAccountMixin: # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
def create_account(self, username, password, email):
|
||||
# pylint: disable=missing-docstring
|
||||
registration_url = reverse('user_api_registration')
|
||||
@@ -90,7 +88,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
|
||||
password = "test"
|
||||
|
||||
def setUp(self):
|
||||
super(TestAccountApi, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.request_factory = RequestFactory()
|
||||
self.table = "student_languageproficiency"
|
||||
self.user = UserFactory.create(password=self.password)
|
||||
@@ -174,7 +172,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
|
||||
|
||||
def test_set_single_social_link(self):
|
||||
social_links = [
|
||||
dict(platform="facebook", social_link="https://www.facebook.com/{}".format(self.user.username))
|
||||
dict(platform="facebook", social_link=f"https://www.facebook.com/{self.user.username}")
|
||||
]
|
||||
update_account_settings(self.user, {"social_links": social_links})
|
||||
account_settings = get_account_settings(self.default_request)[0]
|
||||
@@ -182,8 +180,8 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
|
||||
|
||||
def test_set_multiple_social_links(self):
|
||||
social_links = [
|
||||
dict(platform="facebook", social_link="https://www.facebook.com/{}".format(self.user.username)),
|
||||
dict(platform="twitter", social_link="https://www.twitter.com/{}".format(self.user.username)),
|
||||
dict(platform="facebook", social_link=f"https://www.facebook.com/{self.user.username}"),
|
||||
dict(platform="twitter", social_link=f"https://www.twitter.com/{self.user.username}"),
|
||||
]
|
||||
update_account_settings(self.user, {"social_links": social_links})
|
||||
account_settings = get_account_settings(self.default_request)[0]
|
||||
@@ -191,13 +189,13 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
|
||||
|
||||
def test_add_social_links(self):
|
||||
original_social_links = [
|
||||
dict(platform="facebook", social_link="https://www.facebook.com/{}".format(self.user.username))
|
||||
dict(platform="facebook", social_link=f"https://www.facebook.com/{self.user.username}")
|
||||
]
|
||||
update_account_settings(self.user, {"social_links": original_social_links})
|
||||
|
||||
extra_social_links = [
|
||||
dict(platform="twitter", social_link="https://www.twitter.com/{}".format(self.user.username)),
|
||||
dict(platform="linkedin", social_link="https://www.linkedin.com/in/{}".format(self.user.username)),
|
||||
dict(platform="twitter", social_link=f"https://www.twitter.com/{self.user.username}"),
|
||||
dict(platform="linkedin", social_link=f"https://www.linkedin.com/in/{self.user.username}"),
|
||||
]
|
||||
update_account_settings(self.user, {"social_links": extra_social_links})
|
||||
|
||||
@@ -229,7 +227,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
|
||||
|
||||
def test_unsupported_social_link_platform(self):
|
||||
social_links = [
|
||||
dict(platform="unsupported", social_link="https://www.unsupported.com/{}".format(self.user.username))
|
||||
dict(platform="unsupported", social_link=f"https://www.unsupported.com/{self.user.username}")
|
||||
]
|
||||
with pytest.raises(AccountValidationError):
|
||||
update_account_settings(self.user, {"social_links": social_links})
|
||||
@@ -502,9 +500,9 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
|
||||
class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase):
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
USERNAME = u'frank-underwood'
|
||||
PASSWORD = u'ṕáśśẃőŕd'
|
||||
EMAIL = u'frank+underwood@example.com'
|
||||
USERNAME = 'frank-underwood'
|
||||
PASSWORD = 'ṕáśśẃőŕd'
|
||||
EMAIL = 'frank+underwood@example.com'
|
||||
ID = -1
|
||||
|
||||
def test_create_account(self):
|
||||
@@ -529,10 +527,10 @@ class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase):
|
||||
'email': self.EMAIL,
|
||||
'id': self.ID,
|
||||
'name': self.USERNAME,
|
||||
'gender': None, 'goals': u'',
|
||||
'gender': None, 'goals': '',
|
||||
'is_active': False,
|
||||
'level_of_education': None,
|
||||
'mailing_address': u'',
|
||||
'mailing_address': '',
|
||||
'year_of_birth': None,
|
||||
'country': None,
|
||||
'state': None,
|
||||
@@ -561,11 +559,11 @@ class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase):
|
||||
"""
|
||||
# Set user password to NFKD format so that we can test that it is normalized to
|
||||
# NFKC format upon account creation.
|
||||
self.create_account(self.USERNAME, unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), self.EMAIL)
|
||||
self.create_account(self.USERNAME, unicodedata.normalize('NFKD', 'Ṗŕệṿïệẅ Ṯệẍt'), self.EMAIL)
|
||||
|
||||
user = User.objects.get(username=self.USERNAME)
|
||||
|
||||
salt_val = user.password.split('$')[1]
|
||||
|
||||
expected_user_password = make_password(unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), salt_val)
|
||||
expected_user_password = make_password(unicodedata.normalize('NFKC', 'Ṗŕệṿïệẅ Ṯệẍt'), salt_val)
|
||||
assert expected_user_password == user.password
|
||||
|
||||
@@ -5,14 +5,13 @@ Tests for helpers.py
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import TestCase
|
||||
from mock import patch
|
||||
from pytz import UTC
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from six import text_type # lint-amnesty, pylint: disable=wrong-import-order
|
||||
|
||||
from ..image_helpers import get_profile_image_urls_for_user
|
||||
|
||||
@@ -28,7 +27,7 @@ class ProfileImageUrlTestCase(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ProfileImageUrlTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.user = UserFactory()
|
||||
# Ensure that parental controls don't apply to this user
|
||||
self.user.profile.year_of_birth = 1980
|
||||
@@ -46,7 +45,7 @@ class ProfileImageUrlTestCase(TestCase):
|
||||
"""
|
||||
Verify correct url structure for a default profile image.
|
||||
"""
|
||||
assert actual_url == '/static/default_{size}.png'.format(size=expected_pixels)
|
||||
assert actual_url == f'/static/default_{expected_pixels}.png'
|
||||
|
||||
def verify_urls(self, actual_urls, expected_name, is_default=False):
|
||||
"""
|
||||
@@ -68,7 +67,7 @@ class ProfileImageUrlTestCase(TestCase):
|
||||
self.user.profile.profile_image_uploaded_at = TEST_PROFILE_IMAGE_UPLOAD_DT
|
||||
self.user.profile.save() # lint-amnesty, pylint: disable=no-member
|
||||
expected_name = hashlib.md5((
|
||||
'secret' + text_type(self.user.username)).encode('utf-8')).hexdigest()
|
||||
'secret' + str(self.user.username)).encode('utf-8')).hexdigest()
|
||||
actual_urls = get_profile_image_urls_for_user(self.user)
|
||||
self.verify_urls(actual_urls, expected_name, is_default=False)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class CanDeactivateUserTest(TestCase):
|
||||
""" Tests for user deactivation API permissions """
|
||||
|
||||
def setUp(self):
|
||||
super(CanDeactivateUserTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.request = RequestFactory().get('/test/url')
|
||||
|
||||
def test_api_permission_superuser(self):
|
||||
@@ -46,7 +46,7 @@ class CanRetireUserTest(TestCase):
|
||||
""" Tests for user retirement API permissions """
|
||||
|
||||
def setUp(self):
|
||||
super(CanRetireUserTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.request = RequestFactory().get('/test/url')
|
||||
|
||||
def test_api_permission_superuser(self):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Test cases to cover account retirement views
|
||||
"""
|
||||
@@ -7,11 +6,10 @@ Test cases to cover account retirement views
|
||||
import datetime
|
||||
import json
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import pytz
|
||||
import six
|
||||
from consent.models import DataSharingConsent
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
@@ -29,8 +27,6 @@ from enterprise.models import (
|
||||
from integrated_channels.sap_success_factors.models import SapSuccessFactorsLearnerDataTransmissionAudit
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework import status
|
||||
from six import iteritems, text_type
|
||||
from six.moves import range
|
||||
from social_django.models import UserSocialAuth
|
||||
from wiki.models import Article, ArticleRevision
|
||||
from wiki.models.pluginbase import RevisionPlugin, RevisionPluginRevision
|
||||
@@ -109,7 +105,7 @@ class TestAccountDeactivation(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestAccountDeactivation, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_user = UserFactory()
|
||||
self.url = reverse('accounts_deactivation', kwargs={'username': self.test_user.username})
|
||||
|
||||
@@ -180,7 +176,7 @@ class TestDeactivateLogout(RetirementTestCase):
|
||||
Tests the account deactivation/logout endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestDeactivateLogout, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_password = 'password'
|
||||
self.test_user = UserFactory(password=self.test_password)
|
||||
UserSocialAuth.objects.create(
|
||||
@@ -277,7 +273,7 @@ class TestPartnerReportingCleanup(ModuleStoreTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestPartnerReportingCleanup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
self.course = CourseFactory()
|
||||
self.course_awesome_org = CourseFactory(org='awesome_org')
|
||||
@@ -389,7 +385,7 @@ class TestPartnerReportingPut(RetirementTestCase, ModuleStoreTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestPartnerReportingPut, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
self.course = CourseFactory()
|
||||
self.course_awesome_org = CourseFactory(org='awesome_org')
|
||||
@@ -496,7 +492,7 @@ class TestPartnerReportingList(ModuleStoreTestCase):
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestPartnerReportingList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
self.course = CourseFactory()
|
||||
self.course_awesome_org = CourseFactory(org='awesome_org')
|
||||
@@ -698,7 +694,7 @@ class TestAccountRetirementList(RetirementTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestAccountRetirementList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
self.headers = build_jwt_headers(self.test_superuser)
|
||||
self.url = reverse('accounts_retirement_queue')
|
||||
@@ -737,7 +733,7 @@ class TestAccountRetirementList(RetirementTestCase):
|
||||
del retirement['created']
|
||||
del retirement['modified']
|
||||
|
||||
six.assertCountEqual(self, response_data, expected_data)
|
||||
self.assertCountEqual(response_data, expected_data)
|
||||
|
||||
def test_empty(self):
|
||||
"""
|
||||
@@ -858,7 +854,7 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestAccountRetirementsByStatusAndDate, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
self.headers = build_jwt_headers(self.test_superuser)
|
||||
self.url = reverse('accounts_retirements_by_status_and_date')
|
||||
@@ -910,7 +906,7 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
six.assertCountEqual(self, response_data, expected_data)
|
||||
self.assertCountEqual(response_data, expected_data)
|
||||
|
||||
def test_empty(self):
|
||||
"""
|
||||
@@ -1005,7 +1001,7 @@ class TestAccountRetirementRetrieve(RetirementTestCase):
|
||||
Tests the account retirement retrieval endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestAccountRetirementRetrieve, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_user = UserFactory()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
self.url = reverse('accounts_retirement_retrieve', kwargs={'username': self.test_user.username})
|
||||
@@ -1076,7 +1072,7 @@ class TestAccountRetirementCleanup(RetirementTestCase):
|
||||
Tests the account retirement cleanup endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestAccountRetirementCleanup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.pending_state = RetirementState.objects.get(state_name='PENDING')
|
||||
self.complete_state = RetirementState.objects.get(state_name='COMPLETE')
|
||||
self.retirements = []
|
||||
@@ -1151,7 +1147,7 @@ class TestAccountRetirementUpdate(RetirementTestCase):
|
||||
Tests the account retirement endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestAccountRetirementUpdate, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.pending_state = RetirementState.objects.get(state_name='PENDING')
|
||||
self.locking_state = RetirementState.objects.get(state_name='LOCKING_ACCOUNT')
|
||||
|
||||
@@ -1291,7 +1287,7 @@ class TestAccountRetirementPost(RetirementTestCase):
|
||||
Tests the account retirement endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestAccountRetirementPost, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.test_user = UserFactory()
|
||||
self.test_superuser = SuperuserFactory()
|
||||
@@ -1403,7 +1399,7 @@ class TestAccountRetirementPost(RetirementTestCase):
|
||||
with mock.patch(path, side_effect=Exception('Unexpected Exception')) as mock_get_retirement:
|
||||
data = {'username': self.test_user.username}
|
||||
response = self.post_and_assert_status(data, status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
assert 'Unexpected Exception' == text_type(response.json())
|
||||
assert 'Unexpected Exception' == str(response.json())
|
||||
mock_get_retirement.assert_called_once_with(self.original_username)
|
||||
|
||||
def test_retire_user_where_user_already_retired(self):
|
||||
@@ -1416,7 +1412,7 @@ class TestAccountRetirementPost(RetirementTestCase):
|
||||
|
||||
def test_retire_user_where_username_not_provided(self):
|
||||
response = self.post_and_assert_status({}, status.HTTP_404_NOT_FOUND)
|
||||
expected_response_message = {'message': text_type('The user was not specified.')}
|
||||
expected_response_message = {'message': 'The user was not specified.'}
|
||||
assert expected_response_message == response.json()
|
||||
|
||||
@mock.patch('openedx.core.djangoapps.user_api.accounts.views.get_profile_image_names')
|
||||
@@ -1434,10 +1430,10 @@ class TestAccountRetirementPost(RetirementTestCase):
|
||||
'is_active': False,
|
||||
'username': self.retired_username,
|
||||
}
|
||||
for field, expected_value in iteritems(expected_user_values):
|
||||
for field, expected_value in expected_user_values.items():
|
||||
assert expected_value == getattr(self.test_user, field)
|
||||
|
||||
for field, expected_value in iteritems(USER_PROFILE_PII):
|
||||
for field, expected_value in USER_PROFILE_PII.items():
|
||||
assert expected_value == getattr(self.test_user.profile, field)
|
||||
|
||||
assert self.test_user.profile.profile_image_uploaded_at is None
|
||||
@@ -1468,7 +1464,7 @@ class TestAccountRetirementPost(RetirementTestCase):
|
||||
self.post_and_assert_status(data)
|
||||
|
||||
def test_deletes_pii_from_user_profile(self):
|
||||
for model_field, value_to_assign in iteritems(USER_PROFILE_PII):
|
||||
for model_field, value_to_assign in USER_PROFILE_PII.items():
|
||||
if value_to_assign == '':
|
||||
value = 'foo'
|
||||
else:
|
||||
@@ -1477,7 +1473,7 @@ class TestAccountRetirementPost(RetirementTestCase):
|
||||
|
||||
AccountRetirementView.clear_pii_from_userprofile(self.test_user)
|
||||
|
||||
for model_field, value_to_assign in iteritems(USER_PROFILE_PII):
|
||||
for model_field, value_to_assign in USER_PROFILE_PII.items():
|
||||
assert value_to_assign == getattr(self.test_user.profile, model_field)
|
||||
|
||||
social_links = SocialLink.objects.filter(
|
||||
@@ -1570,7 +1566,7 @@ class TestLMSAccountRetirementPost(RetirementTestCase, ModuleStoreTestCase):
|
||||
Tests the LMS account retirement (GDPR P2) endpoint.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestLMSAccountRetirementPost, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.pii_standin = 'PII here'
|
||||
self.course = CourseFactory()
|
||||
self.test_user = UserFactory()
|
||||
|
||||
@@ -18,7 +18,7 @@ LOGGER_NAME = "openedx.core.djangoapps.user_api.accounts.serializers"
|
||||
|
||||
class UserReadOnlySerializerTest(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
def setUp(self):
|
||||
super(UserReadOnlySerializerTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
request_factory = RequestFactory()
|
||||
self.request = request_factory.get('/api/user/v1/accounts/')
|
||||
self.user = UserFactory.build(username='test_user', email='test_user@test.com')
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
""" Tests for views related to account settings. """
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
@@ -49,7 +48,7 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, SiteMixin, ProgramsApiCon
|
||||
|
||||
@mock.patch("django.conf.settings.MESSAGE_STORAGE", 'django.contrib.messages.storage.cookie.CookieStorage')
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(AccountSettingsViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD)
|
||||
CommerceConfiguration.objects.create(cache_ttl=10, enabled=True)
|
||||
self.client.login(username=self.USERNAME, password=self.PASSWORD)
|
||||
|
||||
@@ -22,7 +22,7 @@ class UserAccountSettingsTest(TestCase):
|
||||
"""Unit tests for setting Social Media Links."""
|
||||
|
||||
def setUp(self): # lint-amnesty, pylint: disable=useless-super-delegation
|
||||
super(UserAccountSettingsTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
def validate_social_link(self, social_platform, link):
|
||||
"""
|
||||
@@ -71,7 +71,7 @@ class CompletionUtilsTestCase(SharedModuleStoreTestCase, CompletionWaffleTestMix
|
||||
"""
|
||||
Creates a test course that can be used for non-destructive tests
|
||||
"""
|
||||
super(CompletionUtilsTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.override_waffle_switch(True)
|
||||
self.engaged_user = UserFactory.create()
|
||||
self.cruft_user = UserFactory.create()
|
||||
@@ -122,7 +122,7 @@ class CompletionUtilsTestCase(SharedModuleStoreTestCase, CompletionWaffleTestMix
|
||||
self.cruft_user
|
||||
)
|
||||
assert block_url ==\
|
||||
u'test_url:9999/courses/{org}/{course}/{run}/jump_to/i4x://{org}/{course}/vertical/{vertical_id}'.format(
|
||||
'test_url:9999/courses/{org}/{course}/{run}/jump_to/i4x://{org}/{course}/vertical/{vertical_id}'.format(
|
||||
org=self.course.location.course_key.org,
|
||||
course=self.course.location.course_key.course,
|
||||
run=self.course.location.course_key.run,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Test cases to cover Accounts-related behaviors of the User API application
|
||||
"""
|
||||
@@ -8,17 +7,15 @@ import datetime
|
||||
import hashlib
|
||||
import json
|
||||
from copy import deepcopy
|
||||
from unittest import mock
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import pytz
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.test.testcases import TransactionTestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient, APITestCase
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
|
||||
from openedx.core.djangoapps.user_api.accounts import ACCOUNT_VISIBILITY_PREF_KEY
|
||||
@@ -37,8 +34,8 @@ TEST_PROFILE_IMAGE_UPLOADED_AT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo
|
||||
TEST_PROFILE_IMAGE_BACKEND = deepcopy(settings.PROFILE_IMAGE_BACKEND)
|
||||
TEST_PROFILE_IMAGE_BACKEND['options']['base_url'] = '/profile-images/'
|
||||
|
||||
TEST_BIO_VALUE = u"Tired mother of twins"
|
||||
TEST_LANGUAGE_PROFICIENCY_CODE = u"hi"
|
||||
TEST_BIO_VALUE = "Tired mother of twins"
|
||||
TEST_LANGUAGE_PROFICIENCY_CODE = "hi"
|
||||
|
||||
|
||||
class UserAPITestCase(APITestCase):
|
||||
@@ -47,7 +44,7 @@ class UserAPITestCase(APITestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(UserAPITestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.anonymous_client = APIClient()
|
||||
self.different_user = UserFactory.create(password=TEST_PASSWORD)
|
||||
@@ -151,7 +148,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
ENABLED_CACHES = ['default']
|
||||
|
||||
def setUp(self):
|
||||
super(TestOwnUsernameAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.url = reverse("own_username_api")
|
||||
|
||||
@@ -209,7 +206,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
ENABLED_CACHES = ['default']
|
||||
|
||||
def setUp(self):
|
||||
super(TestAccountsAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.url = reverse("accounts_api", kwargs={'username': self.user.username})
|
||||
|
||||
@@ -341,7 +338,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
self.create_mock_profile(self.user)
|
||||
set_user_preference(self.user, ACCOUNT_VISIBILITY_PREF_KEY, PRIVATE_VISIBILITY)
|
||||
|
||||
response = self.send_get(client, query_parameters='email={}'.format(self.user.email))
|
||||
response = self.send_get(client, query_parameters=f'email={self.user.email}')
|
||||
self._verify_full_account_response(response)
|
||||
|
||||
# Note: using getattr so that the patching works even if there is no configuration.
|
||||
@@ -413,7 +410,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
response = self.send_get(client, query_parameters='view=shared')
|
||||
verify_fields_visible_to_all_users(response)
|
||||
|
||||
response = self.send_get(client, query_parameters='view=shared&email={}'.format(self.user.email))
|
||||
response = self.send_get(client, query_parameters=f'view=shared&email={self.user.email}')
|
||||
verify_fields_visible_to_all_users(response)
|
||||
|
||||
@ddt.data(
|
||||
@@ -428,7 +425,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
set_user_preference(self.user, ACCOUNT_VISIBILITY_PREF_KEY, CUSTOM_VISIBILITY)
|
||||
shared_fields = ("bio", "language_proficiencies", "name")
|
||||
for field_name in shared_fields:
|
||||
set_user_preference(self.user, "visibility.{}".format(field_name), ALL_USERS_VISIBILITY)
|
||||
set_user_preference(self.user, f"visibility.{field_name}", ALL_USERS_VISIBILITY)
|
||||
|
||||
# make API request
|
||||
client = self.login_client(api_client, requesting_username)
|
||||
@@ -465,7 +462,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
set_user_preference(self.user, ACCOUNT_VISIBILITY_PREF_KEY, CUSTOM_VISIBILITY)
|
||||
shared_fields = ("bio", "language_proficiencies")
|
||||
for field_name in shared_fields:
|
||||
set_user_preference(self.user, "visibility.{}".format(field_name), ALL_USERS_VISIBILITY)
|
||||
set_user_preference(self.user, f"visibility.{field_name}", ALL_USERS_VISIBILITY)
|
||||
|
||||
# make API request
|
||||
client = self.login_client(api_client, requesting_username)
|
||||
@@ -572,19 +569,19 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
assert 403 == response.status_code
|
||||
|
||||
@ddt.data(
|
||||
("gender", "f", "not a gender", u'"not a gender" is not a valid choice.'),
|
||||
("level_of_education", "none", u"ȻħȺɍłɇs", u'"ȻħȺɍłɇs" is not a valid choice.'),
|
||||
("country", "GB", "XY", u'"XY" is not a valid choice.'),
|
||||
("state", "MA", "PY", u'"PY" is not a valid choice.'),
|
||||
("year_of_birth", 2009, "not_an_int", u"A valid integer is required."),
|
||||
("name", "bob", "z" * 256, u"Ensure this field has no more than 255 characters."),
|
||||
("name", u"ȻħȺɍłɇs", " ", u"The name field must be at least 1 character long."),
|
||||
("gender", "f", "not a gender", '"not a gender" is not a valid choice.'),
|
||||
("level_of_education", "none", "ȻħȺɍłɇs", '"ȻħȺɍłɇs" is not a valid choice.'),
|
||||
("country", "GB", "XY", '"XY" is not a valid choice.'),
|
||||
("state", "MA", "PY", '"PY" is not a valid choice.'),
|
||||
("year_of_birth", 2009, "not_an_int", "A valid integer is required."),
|
||||
("name", "bob", "z" * 256, "Ensure this field has no more than 255 characters."),
|
||||
("name", "ȻħȺɍłɇs", " ", "The name field must be at least 1 character long."),
|
||||
("goals", "Smell the roses"),
|
||||
("mailing_address", "Sesame Street"),
|
||||
# Note that we store the raw data, so it is up to client to escape the HTML.
|
||||
(
|
||||
"bio", u"<html>Lacrosse-playing superhero 壓是進界推日不復女</html>",
|
||||
"z" * 301, u"The about me field must be at most 300 characters long."
|
||||
"bio", "<html>Lacrosse-playing superhero 壓是進界推日不復女</html>",
|
||||
"z" * 301, "The about me field must be at most 300 characters long."
|
||||
),
|
||||
("account_privacy", ALL_USERS_VISIBILITY),
|
||||
("account_privacy", PRIVATE_VISIBILITY),
|
||||
@@ -610,13 +607,13 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
|
||||
if fails_validation_value:
|
||||
error_response = self.send_patch(client, {field: fails_validation_value}, expected_status=400)
|
||||
expected_user_message = u'This value is invalid.'
|
||||
expected_user_message = 'This value is invalid.'
|
||||
if field == 'bio':
|
||||
expected_user_message = u"The about me field must be at most 300 characters long."
|
||||
expected_user_message = "The about me field must be at most 300 characters long."
|
||||
|
||||
assert expected_user_message == error_response.data['field_errors'][field]['user_message']
|
||||
|
||||
assert u"Value '{value}' is not valid for field '{field}': {messages}"\
|
||||
assert "Value '{value}' is not valid for field '{field}': {messages}"\
|
||||
.format(value=fails_validation_value,
|
||||
field=field,
|
||||
messages=[developer_validation_message]) ==\
|
||||
@@ -648,7 +645,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
Internal helper to check the error messages returned
|
||||
"""
|
||||
assert 'This field is not editable via this API' == data['field_errors'][field_name]['developer_message']
|
||||
assert u"The '{0}' field cannot be edited."\
|
||||
assert "The '{}' field cannot be edited."\
|
||||
.format(field_name) == data['field_errors'][field_name]['user_message']
|
||||
|
||||
for field_name in ["username", "date_joined", "is_active", "profile_image", "requires_parental_consent"]:
|
||||
@@ -708,7 +705,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
"""
|
||||
assert 3 == len(change_info)
|
||||
assert old_name == change_info[0]
|
||||
assert u'Name change requested through account API by {}'.format(requester) == change_info[1]
|
||||
assert f'Name change requested through account API by {requester}' == change_info[1]
|
||||
assert change_info[2] is not None
|
||||
# Verify the new name was also stored.
|
||||
get_response = self.send_get(self.client)
|
||||
@@ -812,28 +809,28 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
# than django model id.
|
||||
for proficiencies in ([{"code": "en"}, {"code": "fr"}, {"code": "es"}], [{"code": "fr"}], [{"code": "aa"}], []):
|
||||
response = self.send_patch(client, {"language_proficiencies": proficiencies})
|
||||
six.assertCountEqual(self, response.data["language_proficiencies"], proficiencies)
|
||||
self.assertCountEqual(response.data["language_proficiencies"], proficiencies)
|
||||
|
||||
@ddt.data(
|
||||
(
|
||||
u"not_a_list",
|
||||
{u'non_field_errors': [u'Expected a list of items but got type "unicode".']}
|
||||
"not_a_list",
|
||||
{'non_field_errors': ['Expected a list of items but got type "unicode".']}
|
||||
),
|
||||
(
|
||||
[u"not_a_JSON_object"],
|
||||
[{u'non_field_errors': [u'Invalid data. Expected a dictionary, but got unicode.']}]
|
||||
["not_a_JSON_object"],
|
||||
[{'non_field_errors': ['Invalid data. Expected a dictionary, but got unicode.']}]
|
||||
),
|
||||
(
|
||||
[{}],
|
||||
[{'code': [u'This field is required.']}]
|
||||
[{'code': ['This field is required.']}]
|
||||
),
|
||||
(
|
||||
[{u"code": u"invalid_language_code"}],
|
||||
[{'code': [u'"invalid_language_code" is not a valid choice.']}]
|
||||
[{"code": "invalid_language_code"}],
|
||||
[{'code': ['"invalid_language_code" is not a valid choice.']}]
|
||||
),
|
||||
(
|
||||
[{u"code": u"kw"}, {u"code": u"el"}, {u"code": u"kw"}],
|
||||
[u'The language_proficiencies field must consist of unique languages.']
|
||||
[{"code": "kw"}, {"code": "el"}, {"code": "kw"}],
|
||||
['The language_proficiencies field must consist of unique languages.']
|
||||
),
|
||||
)
|
||||
@ddt.unpack
|
||||
@@ -842,8 +839,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
Verify we handle error cases when patching the language_proficiencies
|
||||
field.
|
||||
"""
|
||||
if six.PY3:
|
||||
expected_error_message = six.text_type(expected_error_message).replace('unicode', 'str')
|
||||
expected_error_message = str(expected_error_message).replace('unicode', 'str')
|
||||
|
||||
client = self.login_client("client", "user")
|
||||
response = self.send_patch(client, {"language_proficiencies": patch_value}, expected_status=400)
|
||||
@@ -925,7 +921,7 @@ class TestAccountAPITransactions(TransactionTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestAccountAPITransactions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.client = APIClient()
|
||||
self.user = UserFactory.create(password=TEST_PASSWORD)
|
||||
self.url = reverse("accounts_api", kwargs={'username': self.user.username})
|
||||
@@ -950,7 +946,7 @@ class TestAccountAPITransactions(TransactionTestCase):
|
||||
response = self.client.get(self.url)
|
||||
data = response.data
|
||||
assert old_email == data['email']
|
||||
assert u'm' == data['gender']
|
||||
assert 'm' == data['gender']
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -960,7 +956,7 @@ class UsernameReplacementViewTests(APITestCase):
|
||||
SERVICE_USERNAME = 'test_replace_username_service_worker'
|
||||
|
||||
def setUp(self):
|
||||
super(UsernameReplacementViewTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.service_user = UserFactory(username=self.SERVICE_USERNAME)
|
||||
self.url = reverse("username_replacement")
|
||||
|
||||
@@ -969,7 +965,7 @@ class UsernameReplacementViewTests(APITestCase):
|
||||
Helper function for creating headers for the JWT authentication.
|
||||
"""
|
||||
token = create_jwt_for_user(user)
|
||||
headers = {'HTTP_AUTHORIZATION': u'JWT {}'.format(token)}
|
||||
headers = {'HTTP_AUTHORIZATION': f'JWT {token}'}
|
||||
return headers
|
||||
|
||||
def call_api(self, user, data):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Utility functions, constants, etc. for testing.
|
||||
"""
|
||||
@@ -10,7 +9,7 @@ from common.djangoapps.util.password_policy_validators import DEFAULT_MAX_PASSWO
|
||||
INVALID_NAMES = [
|
||||
None,
|
||||
'',
|
||||
u''
|
||||
''
|
||||
]
|
||||
|
||||
INVALID_USERNAMES_ASCII = [
|
||||
@@ -20,39 +19,39 @@ INVALID_USERNAMES_ASCII = [
|
||||
]
|
||||
|
||||
INVALID_USERNAMES_UNICODE = [
|
||||
u'invalid-unicode_fŕáńḱ',
|
||||
'invalid-unicode_fŕáńḱ',
|
||||
]
|
||||
|
||||
INVALID_USERNAMES = [
|
||||
None,
|
||||
u'',
|
||||
u'a',
|
||||
u'a' * (USERNAME_MAX_LENGTH + 1),
|
||||
'',
|
||||
'a',
|
||||
'a' * (USERNAME_MAX_LENGTH + 1),
|
||||
] + INVALID_USERNAMES_ASCII + INVALID_USERNAMES_UNICODE
|
||||
|
||||
INVALID_EMAILS = [
|
||||
None,
|
||||
u'',
|
||||
u'a',
|
||||
'',
|
||||
'a',
|
||||
'no_domain',
|
||||
'no+domain',
|
||||
'@',
|
||||
'@domain.com',
|
||||
'test@no_extension',
|
||||
u'fŕáńḱ@example.com',
|
||||
'fŕáńḱ@example.com',
|
||||
|
||||
# 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' * (EMAIL_MAX_LENGTH - 11))
|
||||
'{user}@example.com'.format(
|
||||
user=('e' * (EMAIL_MAX_LENGTH - 11))
|
||||
)
|
||||
]
|
||||
|
||||
INVALID_PASSWORDS = [
|
||||
None,
|
||||
u'',
|
||||
u'a',
|
||||
u'a' * (DEFAULT_MAX_PASSWORD_LENGTH + 1),
|
||||
'',
|
||||
'a',
|
||||
'a' * (DEFAULT_MAX_PASSWORD_LENGTH + 1),
|
||||
]
|
||||
|
||||
INVALID_COUNTRIES = [
|
||||
@@ -63,25 +62,25 @@ INVALID_COUNTRIES = [
|
||||
|
||||
VALID_NAMES = [
|
||||
'Validation Bot',
|
||||
u'Validation Bot'
|
||||
'Validation Bot'
|
||||
]
|
||||
|
||||
VALID_USERNAMES_UNICODE = [
|
||||
u'Enchanté',
|
||||
u'username_with_@',
|
||||
u'username with spaces',
|
||||
u'eastern_arabic_numbers_١٢٣',
|
||||
'Enchanté',
|
||||
'username_with_@',
|
||||
'username with spaces',
|
||||
'eastern_arabic_numbers_١٢٣',
|
||||
]
|
||||
|
||||
VALID_USERNAMES = [
|
||||
u'username',
|
||||
u'a' * USERNAME_MIN_LENGTH,
|
||||
u'a' * USERNAME_MAX_LENGTH,
|
||||
u'-' * USERNAME_MIN_LENGTH,
|
||||
u'-' * USERNAME_MAX_LENGTH,
|
||||
u'_username_',
|
||||
u'-username-',
|
||||
u'-_username_-'
|
||||
'username',
|
||||
'a' * USERNAME_MIN_LENGTH,
|
||||
'a' * USERNAME_MAX_LENGTH,
|
||||
'-' * USERNAME_MIN_LENGTH,
|
||||
'-' * USERNAME_MAX_LENGTH,
|
||||
'_username_',
|
||||
'-username-',
|
||||
'-_username_-'
|
||||
]
|
||||
|
||||
VALID_EMAILS = [
|
||||
@@ -89,11 +88,11 @@ VALID_EMAILS = [
|
||||
]
|
||||
|
||||
VALID_PASSWORDS = [
|
||||
u'good_password_339',
|
||||
'good_password_339',
|
||||
]
|
||||
|
||||
VALID_COUNTRIES = [
|
||||
u'PK',
|
||||
u'Pakistan',
|
||||
u'US'
|
||||
'PK',
|
||||
'Pakistan',
|
||||
'US'
|
||||
]
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestCourseTagAPI(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCourseTagAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.user = UserFactory.create()
|
||||
self.course_id = CourseLocator('test_org', 'test_course_number', 'test_run')
|
||||
self.test_key = 'test_key'
|
||||
|
||||
@@ -25,7 +25,6 @@ from textwrap import dedent
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import connections
|
||||
from django.db.utils import DatabaseError
|
||||
from six.moves import range
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -89,15 +88,15 @@ class Command(BaseCommand):
|
||||
optout_path = options['optout_csv_path']
|
||||
|
||||
if chunk_size <= 0:
|
||||
raise CommandError(u'Only positive chunk size is allowed ({}).'.format(chunk_size))
|
||||
raise CommandError(f'Only positive chunk size is allowed ({chunk_size}).')
|
||||
if sleep_between < 0:
|
||||
raise CommandError(u'Only non-negative sleep between seconds is allowed ({}).'.format(sleep_between))
|
||||
raise CommandError(f'Only non-negative sleep between seconds is allowed ({sleep_between}).')
|
||||
|
||||
# Read the CSV file. Log the number of user/org rows read.
|
||||
with open(optout_path, 'r') as csv_file:
|
||||
with open(optout_path) as csv_file:
|
||||
optout_reader = csv.reader(csv_file)
|
||||
optout_rows = list(optout_reader)
|
||||
log.info(u"Read %s opt-out rows from CSV file '%s'.", len(optout_rows), optout_path)
|
||||
log.info("Read %s opt-out rows from CSV file '%s'.", len(optout_rows), optout_path)
|
||||
|
||||
cursor = connections['default'].cursor()
|
||||
|
||||
@@ -108,7 +107,7 @@ class Command(BaseCommand):
|
||||
start_idx = curr_row_idx
|
||||
end_idx = min(start_idx + chunk_size - 1, len(optout_rows) - 1)
|
||||
|
||||
log.info(u"Attempting opt-out for rows (%s, %s) through (%s, %s)...",
|
||||
log.info("Attempting opt-out for rows (%s, %s) through (%s, %s)...",
|
||||
optout_rows[start_idx][0], optout_rows[start_idx][1],
|
||||
optout_rows[end_idx][0], optout_rows[end_idx][1])
|
||||
|
||||
@@ -131,16 +130,16 @@ class Command(BaseCommand):
|
||||
cursor.execute(query)
|
||||
except DatabaseError as err:
|
||||
cursor.execute('ROLLBACK;')
|
||||
log.error(u"Rolled-back opt-out for rows (%s, %s) through (%s, %s): %s",
|
||||
log.error("Rolled-back opt-out for rows (%s, %s) through (%s, %s): %s",
|
||||
optout_rows[start_idx][0], optout_rows[start_idx][1],
|
||||
optout_rows[end_idx][0], optout_rows[end_idx][1],
|
||||
str(err))
|
||||
raise
|
||||
else:
|
||||
cursor.execute('COMMIT;')
|
||||
log.info(u"Committed opt-out for rows (%s, %s) through (%s, %s).",
|
||||
log.info("Committed opt-out for rows (%s, %s) through (%s, %s).",
|
||||
optout_rows[start_idx][0], optout_rows[start_idx][1],
|
||||
optout_rows[end_idx][0], optout_rows[end_idx][1])
|
||||
log.info(u"Sleeping %s seconds...", sleep_between)
|
||||
log.info("Sleeping %s seconds...", sleep_between)
|
||||
time.sleep(sleep_between)
|
||||
curr_row_idx += chunk_size
|
||||
|
||||
@@ -37,13 +37,13 @@ class Command(BaseCommand):
|
||||
original_email=email_address
|
||||
)
|
||||
except UserRetirementStatus.DoesNotExist:
|
||||
raise CommandError(u"No retirement request with email address '{}' exists.".format(email_address)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise CommandError(f"No retirement request with email address '{email_address}' exists.") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
# Check if the user has started the retirement process -or- not.
|
||||
if retirement_status.current_state.state_name != 'PENDING':
|
||||
raise CommandError(
|
||||
u"Retirement requests can only be cancelled for users in the PENDING state."
|
||||
u" Current request state for '{}': {}".format(
|
||||
"Retirement requests can only be cancelled for users in the PENDING state."
|
||||
" Current request state for '{}': {}".format(
|
||||
email_address,
|
||||
retirement_status.current_state.state_name
|
||||
)
|
||||
@@ -58,4 +58,4 @@ class Command(BaseCommand):
|
||||
# No need to delete the accompanying "permanent" retirement request record - it gets done via Django signal.
|
||||
retirement_status.delete()
|
||||
|
||||
print(u"Successfully cancelled retirement request for user with email address '{}'.".format(email_address))
|
||||
print(f"Successfully cancelled retirement request for user with email address '{email_address}'.")
|
||||
|
||||
@@ -33,9 +33,6 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import connections
|
||||
from django.utils import timezone
|
||||
from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=unused-import
|
||||
import six
|
||||
from six import text_type
|
||||
from six.moves import range
|
||||
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
@@ -88,7 +85,7 @@ class Command(BaseCommand):
|
||||
QUERY_INTERVAL = 1000
|
||||
|
||||
# Default datetime if the user has not set a preference
|
||||
DEFAULT_DATETIME_STR = datetime.datetime(year=2014, month=12, day=1).isoformat(str(' '))
|
||||
DEFAULT_DATETIME_STR = datetime.datetime(year=2014, month=12, day=1).isoformat(' ')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""
|
||||
@@ -109,7 +106,7 @@ class Command(BaseCommand):
|
||||
org_list = options['org_list']
|
||||
|
||||
if os.path.exists(file_path):
|
||||
raise CommandError("File already exists at '{path}'".format(path=file_path))
|
||||
raise CommandError(f"File already exists at '{file_path}'")
|
||||
|
||||
only_courses = options.get("courses")
|
||||
|
||||
@@ -120,7 +117,7 @@ class Command(BaseCommand):
|
||||
courses = self._get_courses_for_org(org_list)
|
||||
|
||||
# Add in organizations from the course keys, to ensure we're including orgs with different capitalizations
|
||||
org_list = list(set(org_list) | set(course.org for course in courses))
|
||||
org_list = list(set(org_list) | {course.org for course in courses})
|
||||
else:
|
||||
courses = list(set(only_courses.split(",")))
|
||||
|
||||
@@ -135,7 +132,7 @@ class Command(BaseCommand):
|
||||
# Let the user know what's about to happen
|
||||
LOGGER.info(
|
||||
"Retrieving data for courses: {courses}".format(
|
||||
courses=", ".join([text_type(course) for course in courses])
|
||||
courses=", ".join([str(course) for course in courses])
|
||||
)
|
||||
)
|
||||
|
||||
@@ -148,7 +145,7 @@ class Command(BaseCommand):
|
||||
self._write_email_opt_in_prefs(file_handle, org_list, course_group)
|
||||
|
||||
# Remind the user where the output file is
|
||||
LOGGER.info("Output file: {file_path}".format(file_path=file_path))
|
||||
LOGGER.info(f"Output file: {file_path}")
|
||||
|
||||
def _get_courses_for_org(self, org_aliases):
|
||||
"""
|
||||
@@ -176,7 +173,7 @@ class Command(BaseCommand):
|
||||
start_time = time.time()
|
||||
yield
|
||||
execution_time = time.time() - start_time
|
||||
LOGGER.info("Execution time: {time} seconds".format(time=execution_time))
|
||||
LOGGER.info(f"Execution time: {execution_time} seconds")
|
||||
|
||||
def _write_email_opt_in_prefs(self, file_handle, org_aliases, courses):
|
||||
"""
|
||||
@@ -254,19 +251,19 @@ class Command(BaseCommand):
|
||||
# Only encode to utf-8 in python2 because python3's csv writer can handle unicode.
|
||||
writer.writerow({
|
||||
"user_id": user_id,
|
||||
"username": username.encode('utf-8') if six.PY2 else username,
|
||||
"email": email.encode('utf-8') if six.PY2 else email,
|
||||
"username": username,
|
||||
"email": email,
|
||||
# There should not be a case where users are without full_names. We only need this safe check because
|
||||
# of ECOM-1995.
|
||||
"full_name": full_name.encode('utf-8') if six.PY2 else full_name,
|
||||
"course_id": course_id.encode('utf-8') if six.PY2 else course_id,
|
||||
"full_name": full_name,
|
||||
"course_id": course_id,
|
||||
"is_opted_in_for_email": is_opted_in if is_opted_in else "True",
|
||||
"preference_set_datetime": pref_set_datetime,
|
||||
})
|
||||
row_count += 1
|
||||
|
||||
# Log the number of rows we processed
|
||||
LOGGER.info("Retrieved {num_rows} records for orgs {org}.".format(num_rows=row_count, org=org_aliases))
|
||||
LOGGER.info(f"Retrieved {row_count} records for orgs {org_aliases}.")
|
||||
|
||||
def _iterate_results(self, cursor):
|
||||
"""
|
||||
@@ -282,14 +279,13 @@ class Command(BaseCommand):
|
||||
rows = cursor.fetchmany(self.QUERY_INTERVAL)
|
||||
if not rows:
|
||||
break
|
||||
for row in rows:
|
||||
yield row
|
||||
yield from rows
|
||||
|
||||
def _sql_list(self, values):
|
||||
"""
|
||||
Serialize a list of values for including in a SQL "IN" statement.
|
||||
"""
|
||||
return ",".join(['"{}"'.format(val) for val in values])
|
||||
return ",".join([f'"{val}"' for val in values])
|
||||
|
||||
def _db_cursor(self):
|
||||
"""
|
||||
|
||||
@@ -62,14 +62,14 @@ class Command(BaseCommand):
|
||||
langs += dark_lang_config.beta_languages_list if dark_lang_config.enable_beta_languages else []
|
||||
|
||||
if new_lang_code not in langs:
|
||||
raise CommandError(u'{} is not a configured language code in settings.LANGUAGES '
|
||||
raise CommandError('{} is not a configured language code in settings.LANGUAGES '
|
||||
'or the current DarkLangConfig.'.format(new_lang_code))
|
||||
|
||||
max_id = UserPreference.objects.all().aggregate(Max('id'))['id__max']
|
||||
|
||||
print(u'Updating user language preferences from {} to {}. '
|
||||
u'Start id is {}, current max id is {}. '
|
||||
u'Chunk size is of {}'.format(old_lang_code, new_lang_code, start, max_id, chunk_size))
|
||||
print('Updating user language preferences from {} to {}. '
|
||||
'Start id is {}, current max id is {}. '
|
||||
'Chunk size is of {}'.format(old_lang_code, new_lang_code, start, max_id, chunk_size))
|
||||
|
||||
updated_count = 0
|
||||
|
||||
@@ -89,7 +89,7 @@ class Command(BaseCommand):
|
||||
|
||||
updated_count += curr
|
||||
|
||||
print(u'Updated rows {} to {}, {} rows affected'.format(start, end - 1, curr))
|
||||
print('Updated rows {} to {}, {} rows affected'.format(start, end - 1, curr))
|
||||
|
||||
if end >= max_id:
|
||||
break
|
||||
@@ -98,7 +98,7 @@ class Command(BaseCommand):
|
||||
end += chunk_size
|
||||
sleep(sleep_time_secs)
|
||||
|
||||
print(u'Finished! Updated {} total preferences from {} to {}'.format(
|
||||
print('Finished! Updated {} total preferences from {} to {}'.format(
|
||||
updated_count,
|
||||
old_lang_code,
|
||||
new_lang_code
|
||||
|
||||
@@ -50,17 +50,17 @@ class Command(BaseCommand):
|
||||
raise CommandError('settings.RETIREMENT_STATES does not exist or is empty.')
|
||||
|
||||
if not set(REQUIRED_STATES).issubset(set(new_states)):
|
||||
raise CommandError(u'settings.RETIREMENT_STATES ({}) does not contain all required states '
|
||||
u'({})'.format(new_states, REQ_STR))
|
||||
raise CommandError('settings.RETIREMENT_STATES ({}) does not contain all required states '
|
||||
'({})'.format(new_states, REQ_STR))
|
||||
|
||||
# Confirm that the start and end states are in the right places
|
||||
if new_states.index(START_STATE) != 0:
|
||||
raise CommandError(u'{} must be the first state'.format(START_STATE))
|
||||
raise CommandError(f'{START_STATE} must be the first state')
|
||||
|
||||
num_end_states = len(END_STATES)
|
||||
|
||||
if new_states[-num_end_states:] != END_STATES:
|
||||
raise CommandError(u'The last {} states must be these (in this order): '
|
||||
raise CommandError('The last {} states must be these (in this order): '
|
||||
'{}'.format(num_end_states, END_STATES))
|
||||
|
||||
def _check_current_users(self):
|
||||
@@ -75,8 +75,8 @@ class Command(BaseCommand):
|
||||
|
||||
def _check_users_in_states_to_delete(self, states_to_delete):
|
||||
if UserRetirementStatus.objects.filter(current_state__state_name__in=states_to_delete).exists():
|
||||
raise CommandError(u'Users exist in a state that is marked for deletion! States to delete'
|
||||
u'are: {}'.format(states_to_delete))
|
||||
raise CommandError('Users exist in a state that is marked for deletion! States to delete'
|
||||
'are: {}'.format(states_to_delete))
|
||||
|
||||
def _delete_old_states_and_create_new(self, new_states, dry_run=False):
|
||||
"""
|
||||
@@ -151,9 +151,9 @@ class Command(BaseCommand):
|
||||
|
||||
# Report
|
||||
print("States have been synchronized. Differences:")
|
||||
print(u" Added: {}".format(created))
|
||||
print(u" Removed: {}".format(deleted))
|
||||
print(u" Remaining: {}".format(existed))
|
||||
print(f" Added: {created}")
|
||||
print(f" Removed: {deleted}")
|
||||
print(f" Remaining: {existed}")
|
||||
print("States updated successfully. Current states:")
|
||||
|
||||
for state in RetirementState.objects.all():
|
||||
|
||||
@@ -85,15 +85,15 @@ class Command(BaseCommand):
|
||||
retire_dot_oauth2_models(user)
|
||||
AccountRecovery.retire_recovery_email(user.id)
|
||||
except KeyError:
|
||||
error_message = 'Username not specified {}'.format(user)
|
||||
error_message = f'Username not specified {user}'
|
||||
logger.error(error_message)
|
||||
raise CommandError(error_message) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
except user_model.DoesNotExist:
|
||||
error_message = 'The user "{}" does not exist.'.format(user.username)
|
||||
error_message = f'The user "{user.username}" does not exist.'
|
||||
logger.error(error_message)
|
||||
raise CommandError(error_message) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
error_message = '500 error deactivating account {}'.format(exc)
|
||||
error_message = f'500 error deactivating account {exc}'
|
||||
logger.error(error_message)
|
||||
raise CommandError(error_message) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from textwrap import dedent
|
||||
|
||||
import six.moves.urllib.parse # pylint: disable=import-error
|
||||
import urllib.parse # pylint: disable=import-error
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from edx_rest_api_client.client import EdxRestApiClient
|
||||
@@ -49,7 +49,7 @@ class Command(BaseCommand):
|
||||
"""
|
||||
start_date = datetime.now().date() - timedelta(initial_days)
|
||||
end_date = datetime.now().date() - timedelta(1)
|
||||
self.stdout.write(u'Getting users from {start} to {end}'.format(start=start_date, end=end_date))
|
||||
self.stdout.write(f'Getting users from {start_date} to {end_date}')
|
||||
users_qs = User.objects.filter(
|
||||
date_joined__date__gte=start_date,
|
||||
date_joined__date__lte=end_date
|
||||
@@ -68,7 +68,7 @@ class Command(BaseCommand):
|
||||
"""
|
||||
|
||||
self.stdout.write(
|
||||
u'Fetching Users for site {site} from {start} to {end}'.format(
|
||||
'Fetching Users for site {site} from {start} to {end}'.format(
|
||||
site=site_domain, start=offset, end=offset + users_query_batch_size
|
||||
)
|
||||
)
|
||||
@@ -77,7 +77,7 @@ class Command(BaseCommand):
|
||||
user for user in users
|
||||
if UserAttribute.get_user_attribute(user, 'created_on_site') == site_domain
|
||||
]
|
||||
self.stdout.write(u'\tSite Users={count}'.format(count=len(site_users)))
|
||||
self.stdout.write('\tSite Users={count}'.format(count=len(site_users)))
|
||||
|
||||
return site_users
|
||||
|
||||
@@ -88,15 +88,15 @@ class Command(BaseCommand):
|
||||
contacts = []
|
||||
for user in users_batch:
|
||||
if not hasattr(user, 'profile'):
|
||||
self.stdout.write(u'skipping user {} due to no profile found'.format(user))
|
||||
self.stdout.write(f'skipping user {user} due to no profile found')
|
||||
continue
|
||||
if not user.profile.meta:
|
||||
self.stdout.write(u'skipping user {} due to no profile meta found'.format(user))
|
||||
self.stdout.write(f'skipping user {user} due to no profile meta found')
|
||||
continue
|
||||
try:
|
||||
meta = json.loads(user.profile.meta)
|
||||
except ValueError:
|
||||
self.stdout.write(u'skipping user {} due to invalid profile meta found'.format(user))
|
||||
self.stdout.write(f'skipping user {user} due to invalid profile meta found')
|
||||
continue
|
||||
|
||||
contact = {
|
||||
@@ -139,12 +139,12 @@ class Command(BaseCommand):
|
||||
contacts.append(contact)
|
||||
|
||||
api_key = site_conf.get_value('HUBSPOT_API_KEY')
|
||||
client = EdxRestApiClient(six.moves.urllib.parse.urljoin(HUBSPOT_API_BASE_URL, 'contacts/v1/contact'))
|
||||
client = EdxRestApiClient(urllib.parse.urljoin(HUBSPOT_API_BASE_URL, 'contacts/v1/contact'))
|
||||
try:
|
||||
client.batch.post(contacts, hapikey=api_key)
|
||||
return len(contacts)
|
||||
except (HttpClientError, HttpServerError) as ex:
|
||||
message = u'An error occurred while syncing batch of contacts for site {domain}, {message}'.format(
|
||||
message = 'An error occurred while syncing batch of contacts for site {domain}, {message}'.format(
|
||||
domain=site_conf.site.domain, message=ex.message # lint-amnesty, pylint: disable=no-member
|
||||
)
|
||||
self.stderr.write(message)
|
||||
@@ -155,7 +155,7 @@ class Command(BaseCommand):
|
||||
Syncs a single site
|
||||
"""
|
||||
site_domain = site_conf.site.domain
|
||||
self.stdout.write(u'Syncing process started for site {site}'.format(site=site_domain))
|
||||
self.stdout.write(f'Syncing process started for site {site_domain}')
|
||||
|
||||
offset = 0
|
||||
users_queue = []
|
||||
@@ -165,7 +165,7 @@ class Command(BaseCommand):
|
||||
while offset < users_count:
|
||||
is_last_iteration = (offset + users_query_batch_size) >= users_count
|
||||
self.stdout.write(
|
||||
u'Syncing users batch from {start} to {end} for site {site}'.format(
|
||||
'Syncing users batch from {start} to {end} for site {site}'.format(
|
||||
start=offset, end=offset + users_query_batch_size, site=site_domain
|
||||
)
|
||||
)
|
||||
@@ -177,14 +177,14 @@ class Command(BaseCommand):
|
||||
successfully_synced_contacts += self._sync_with_hubspot(users_batch, site_conf)
|
||||
time.sleep(0.1) # to make sure request per second could not exceed by 10
|
||||
self.stdout.write(
|
||||
u'Successfully synced users batch from {start} to {end} for site {site}'.format(
|
||||
'Successfully synced users batch from {start} to {end} for site {site}'.format(
|
||||
start=offset, end=offset + users_query_batch_size, site=site_domain
|
||||
)
|
||||
)
|
||||
offset += users_query_batch_size
|
||||
|
||||
self.stdout.write(
|
||||
u'{count} contacts found and sycned for site {site}'.format(
|
||||
'{count} contacts found and sycned for site {site}'.format(
|
||||
count=successfully_synced_contacts, site=site_domain
|
||||
)
|
||||
)
|
||||
@@ -215,15 +215,15 @@ class Command(BaseCommand):
|
||||
initial_sync_days = options['initial_sync_days']
|
||||
batch_size = options['batch_size']
|
||||
try:
|
||||
self.stdout.write(u'Command execution started with options = {}.'.format(options))
|
||||
self.stdout.write(f'Command execution started with options = {options}.')
|
||||
hubspot_sites = self._get_hubspot_enabled_sites()
|
||||
self.stdout.write(u'{count} hubspot enabled sites found.'.format(count=len(hubspot_sites)))
|
||||
self.stdout.write('{count} hubspot enabled sites found.'.format(count=len(hubspot_sites)))
|
||||
users_queryset = self._get_users_queryset(initial_sync_days)
|
||||
users_count = users_queryset.count()
|
||||
self.stdout.write(u'Users count={count}'.format(count=users_count))
|
||||
self.stdout.write(f'Users count={users_count}')
|
||||
for site_conf in hubspot_sites:
|
||||
self._sync_site(site_conf, users_queryset, users_count, batch_size)
|
||||
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
raise CommandError(u'Command failed with traceback %s' % str(ex)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise CommandError('Command failed with traceback %s' % str(ex)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
@@ -8,14 +8,14 @@ import os
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
import pytest
|
||||
from django.core.management import call_command
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
CSV_DATA = u"""1,UniversityX
|
||||
CSV_DATA = """1,UniversityX
|
||||
2,CollegeX
|
||||
3,StateUX
|
||||
"""
|
||||
@@ -27,7 +27,7 @@ def _create_test_csv(csv_data):
|
||||
Context manager to create and populate a CSV file - and delete it after usage.
|
||||
"""
|
||||
__, file_name = tempfile.mkstemp(text=True)
|
||||
with io.open(file_name, 'w') as file_pointer:
|
||||
with open(file_name, 'w') as file_pointer:
|
||||
file_pointer.write(csv_data)
|
||||
try:
|
||||
yield file_name
|
||||
@@ -41,12 +41,12 @@ def test_successful_dry_run(mock_logger):
|
||||
Run the command with default states for a successful initial population
|
||||
"""
|
||||
with _create_test_csv(CSV_DATA) as tmp_csv_file:
|
||||
args = ['--dry_run', '--optout_csv_path={}'.format(tmp_csv_file)]
|
||||
args = ['--dry_run', f'--optout_csv_path={tmp_csv_file}']
|
||||
call_command('bulk_user_org_email_optout', *args)
|
||||
assert mock_logger.call_count == 3
|
||||
mock_logger.assert_any_call(u"Read %s opt-out rows from CSV file '%s'.", 3, tmp_csv_file)
|
||||
mock_logger.assert_any_call("Read %s opt-out rows from CSV file '%s'.", 3, tmp_csv_file)
|
||||
mock_logger.assert_any_call(
|
||||
u'Attempting opt-out for rows (%s, %s) through (%s, %s)...', '1', 'UniversityX', '3', 'StateUX'
|
||||
'Attempting opt-out for rows (%s, %s) through (%s, %s)...', '1', 'UniversityX', '3', 'StateUX'
|
||||
)
|
||||
mock_logger.assert_any_call(
|
||||
'INSERT INTO user_api_userorgtag (`user_id`, `org`, `key`, `value`, `created`, `modified`) \
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for the email opt-in list management command. """
|
||||
|
||||
|
||||
@@ -9,12 +8,9 @@ import tempfile
|
||||
from collections import defaultdict
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import CommandError
|
||||
from six import text_type
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.djangoapps.user_api.management.commands import email_opt_in_list
|
||||
from openedx.core.djangoapps.user_api.models import UserOrgTag
|
||||
@@ -31,10 +27,10 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
class EmailOptInListTest(ModuleStoreTestCase):
|
||||
"""Tests for the email opt-in list management command. """
|
||||
USER_USERNAME = "test_user"
|
||||
USER_FIRST_NAME = u"Ṫëṡẗ"
|
||||
USER_LAST_NAME = u"Űśéŕ"
|
||||
USER_FIRST_NAME = "Ṫëṡẗ"
|
||||
USER_LAST_NAME = "Űśéŕ"
|
||||
|
||||
TEST_ORG = u"téśt_őŕǵ"
|
||||
TEST_ORG = "téśt_őŕǵ"
|
||||
|
||||
OUTPUT_FILE_NAME = "test_org_email_opt_in.csv"
|
||||
OUTPUT_FIELD_NAMES = [
|
||||
@@ -50,7 +46,7 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
DEFAULT_DATETIME_STR = "2014-12-01 00:00:00"
|
||||
|
||||
def setUp(self):
|
||||
super(EmailOptInListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.user = UserFactory.create(
|
||||
username=self.USER_USERNAME,
|
||||
@@ -217,14 +213,10 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
course_ids = []
|
||||
for row in output:
|
||||
course_id = row['course_id'].strip()
|
||||
# Python3 takes care of the decoding in the csv object
|
||||
# but python 2 doesn't
|
||||
if six.PY2:
|
||||
course_id = course_id.decode('utf-8')
|
||||
course_ids.append(course_id)
|
||||
|
||||
for course in self.courses:
|
||||
assert text_type(course.id) in course_ids
|
||||
assert str(course.id) in course_ids
|
||||
|
||||
# Choose numbers before and after the query interval boundary
|
||||
@ddt.data(2, 3, 4, 5, 6, 7, 8, 9)
|
||||
@@ -270,11 +262,7 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
@ddt.data(0, 1)
|
||||
def test_not_enough_args(self, num_args):
|
||||
args = ["dummy"] * num_args
|
||||
if six.PY2:
|
||||
expected_msg_regex = (
|
||||
"^Error: too few arguments$"
|
||||
)
|
||||
elif num_args == 1:
|
||||
if num_args == 1:
|
||||
expected_msg_regex = (
|
||||
"^Error: the following arguments are required: ORG_ALIASES$"
|
||||
)
|
||||
@@ -395,7 +383,7 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
output_path = os.path.join(temp_dir_path, self.OUTPUT_FILE_NAME)
|
||||
org_list = [org] + other_names
|
||||
if only_courses is not None:
|
||||
only_courses = ",".join(text_type(course_id) for course_id in only_courses)
|
||||
only_courses = ",".join(str(course_id) for course_id in only_courses)
|
||||
|
||||
command = email_opt_in_list.Command()
|
||||
|
||||
@@ -414,8 +402,8 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
with open(output_path) as output_file:
|
||||
reader = csv.DictReader(output_file, fieldnames=self.OUTPUT_FIELD_NAMES)
|
||||
rows = [row for row in reader] # lint-amnesty, pylint: disable=unnecessary-comprehension
|
||||
except IOError:
|
||||
self.fail(u"Could not find or open output file at '{path}'".format(path=output_path))
|
||||
except OSError:
|
||||
self.fail(f"Could not find or open output file at '{output_path}'")
|
||||
|
||||
# Return the output as a list of dictionaries
|
||||
return rows
|
||||
@@ -448,8 +436,8 @@ class EmailOptInListTest(ModuleStoreTestCase):
|
||||
for user, course_id, opt_in_pref in args:
|
||||
assert {'user_id': str(user.id), 'username': user.username, 'email': user.email,
|
||||
'full_name': (user.profile.name if hasattr(user, 'profile') else ''),
|
||||
'course_id': text_type(course_id),
|
||||
'is_opted_in_for_email': text_type(opt_in_pref),
|
||||
'course_id': str(course_id),
|
||||
'is_opted_in_for_email': str(opt_in_pref),
|
||||
'preference_set_datetime':
|
||||
(self._latest_pref_set_datetime(self.user) if kwargs.get('expect_pref_datetime', True) else
|
||||
self.DEFAULT_DATETIME_STR)} in output[1:]
|
||||
|
||||
@@ -87,7 +87,7 @@ def test_out_of_order_start_state(settings):
|
||||
del settings.RETIREMENT_STATES[0]
|
||||
settings.RETIREMENT_STATES.insert(4, 'PENDING')
|
||||
|
||||
with pytest.raises(CommandError, match=u'{} must be the first state'.format(START_STATE)):
|
||||
with pytest.raises(CommandError, match=f'{START_STATE} must be the first state'):
|
||||
call_command('populate_retirement_states')
|
||||
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@ Test the sync_hubspot_contacts management command
|
||||
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from io import StringIO
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from django.utils.six import StringIO
|
||||
from mock import patch
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory
|
||||
from openedx.core.djangoapps.user_api.management.commands.sync_hubspot_contacts import Command as sync_command
|
||||
@@ -28,7 +27,7 @@ class TestHubspotSyncCommand(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestHubspotSyncCommand, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.site_config = SiteConfigurationFactory()
|
||||
cls.hubspot_site_config = SiteConfigurationFactory.create(
|
||||
site_values={'HUBSPOT_API_KEY': 'test_key'}
|
||||
@@ -42,11 +41,11 @@ class TestHubspotSyncCommand(TestCase):
|
||||
# Create some test users
|
||||
for i in range(1, 20):
|
||||
profile_meta = {
|
||||
"first_name": "First Name{0}".format(i),
|
||||
"last_name": "Last Name{0}".format(i),
|
||||
"company": "Company{0}".format(i),
|
||||
"title": "Title{0}".format(i),
|
||||
"state": "State{0}".format(i),
|
||||
"first_name": f"First Name{i}",
|
||||
"last_name": f"Last Name{i}",
|
||||
"company": f"Company{i}",
|
||||
"title": f"Title{i}",
|
||||
"state": f"State{i}",
|
||||
"country": "US",
|
||||
}
|
||||
loe = UserProfile.LEVEL_OF_EDUCATION_CHOICES[0][0]
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for preference APIs.
|
||||
"""
|
||||
|
||||
|
||||
import datetime
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
import ddt
|
||||
from dateutil.parser import parse as parse_datetime
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from pytz import common_timezones, utc
|
||||
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
@@ -50,7 +49,7 @@ class TestPreferenceAPI(CacheIsolationTestCase):
|
||||
password = "test"
|
||||
|
||||
def setUp(self):
|
||||
super(TestPreferenceAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.user = UserFactory.create(password=self.password)
|
||||
self.different_user = UserFactory.create(password=self.password)
|
||||
self.staff_user = UserFactory.create(is_staff=True, password=self.password)
|
||||
@@ -107,8 +106,8 @@ class TestPreferenceAPI(CacheIsolationTestCase):
|
||||
"""
|
||||
Verifies the basic behavior of set_user_preference.
|
||||
"""
|
||||
test_key = u'ⓟⓡⓔⓕⓔⓡⓔⓝⓒⓔ_ⓚⓔⓨ'
|
||||
test_value = u'ǝnןɐʌ_ǝɔuǝɹǝɟǝɹd'
|
||||
test_key = 'ⓟⓡⓔⓕⓔⓡⓔⓝⓒⓔ_ⓚⓔⓨ'
|
||||
test_value = 'ǝnןɐʌ_ǝɔuǝɹǝɟǝɹd'
|
||||
set_user_preference(self.user, test_key, test_value)
|
||||
assert get_user_preference(self.user, test_key) == test_value
|
||||
set_user_preference(self.user, test_key, "new_value", username=self.user.username)
|
||||
@@ -151,11 +150,11 @@ class TestPreferenceAPI(CacheIsolationTestCase):
|
||||
|
||||
user_preference_save.side_effect = [Exception, None]
|
||||
with pytest.raises(PreferenceUpdateError) as context_manager:
|
||||
set_user_preference(self.user, u"new_key_ȻħȺɍłɇs", u"new_value_ȻħȺɍłɇs")
|
||||
set_user_preference(self.user, "new_key_ȻħȺɍłɇs", "new_value_ȻħȺɍłɇs")
|
||||
assert context_manager.value.developer_message ==\
|
||||
u"Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs': "
|
||||
"Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs': "
|
||||
assert context_manager.value.user_message ==\
|
||||
u"Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs'."
|
||||
"Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs'."
|
||||
|
||||
def test_update_user_preferences(self):
|
||||
"""
|
||||
@@ -231,15 +230,15 @@ class TestPreferenceAPI(CacheIsolationTestCase):
|
||||
with pytest.raises(PreferenceUpdateError) as context_manager:
|
||||
update_user_preferences(self.user, {self.test_preference_key: "new_value"})
|
||||
assert context_manager.value.developer_message ==\
|
||||
u"Save failed for user preference 'test_key' with value 'new_value': "
|
||||
"Save failed for user preference 'test_key' with value 'new_value': "
|
||||
assert context_manager.value.user_message ==\
|
||||
u"Save failed for user preference 'test_key' with value 'new_value'."
|
||||
"Save failed for user preference 'test_key' with value 'new_value'."
|
||||
|
||||
user_preference_delete.side_effect = [Exception, None]
|
||||
with pytest.raises(PreferenceUpdateError) as context_manager:
|
||||
update_user_preferences(self.user, {self.test_preference_key: None})
|
||||
assert context_manager.value.developer_message == u"Delete failed for user preference 'test_key': "
|
||||
assert context_manager.value.user_message == u"Delete failed for user preference 'test_key'."
|
||||
assert context_manager.value.developer_message == "Delete failed for user preference 'test_key': "
|
||||
assert context_manager.value.user_message == "Delete failed for user preference 'test_key'."
|
||||
|
||||
def test_delete_user_preference(self):
|
||||
"""
|
||||
@@ -270,8 +269,8 @@ class TestPreferenceAPI(CacheIsolationTestCase):
|
||||
user_preference_delete.side_effect = [Exception, None]
|
||||
with pytest.raises(PreferenceUpdateError) as context_manager:
|
||||
delete_user_preference(self.user, self.test_preference_key)
|
||||
assert context_manager.value.developer_message == u"Delete failed for user preference 'test_key': "
|
||||
assert context_manager.value.user_message == u"Delete failed for user preference 'test_key'."
|
||||
assert context_manager.value.developer_message == "Delete failed for user preference 'test_key': "
|
||||
assert context_manager.value.user_message == "Delete failed for user preference 'test_key'."
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -279,9 +278,9 @@ class UpdateEmailOptInTests(ModuleStoreTestCase):
|
||||
"""
|
||||
Test cases to cover API-driven email list opt-in update workflows
|
||||
"""
|
||||
USERNAME = u'claire-underwood'
|
||||
PASSWORD = u'ṕáśśẃőŕd'
|
||||
EMAIL = u'claire+underwood@example.com'
|
||||
USERNAME = 'claire-underwood'
|
||||
PASSWORD = 'ṕáśśẃőŕd'
|
||||
EMAIL = 'claire+underwood@example.com'
|
||||
|
||||
def _create_account(self, username, password, email):
|
||||
# pylint: disable=missing-docstring
|
||||
@@ -297,19 +296,19 @@ class UpdateEmailOptInTests(ModuleStoreTestCase):
|
||||
|
||||
@ddt.data(
|
||||
# Check that a 27 year old can opt-in
|
||||
(27, True, u"True"),
|
||||
(27, True, "True"),
|
||||
|
||||
# Check that a 32-year old can opt-out
|
||||
(32, False, u"False"),
|
||||
(32, False, "False"),
|
||||
|
||||
# Check that someone 14 years old can opt-in
|
||||
(14, True, u"True"),
|
||||
(14, True, "True"),
|
||||
|
||||
# Check that someone 13 years old cannot opt-in (must have turned 13 before this year)
|
||||
(13, True, u"False"),
|
||||
(13, True, "False"),
|
||||
|
||||
# Check that someone 12 years old cannot opt-in
|
||||
(12, True, u"False")
|
||||
(12, True, "False")
|
||||
)
|
||||
@ddt.unpack
|
||||
@override_settings(EMAIL_OPTIN_MINIMUM_AGE=13)
|
||||
@@ -339,7 +338,7 @@ class UpdateEmailOptInTests(ModuleStoreTestCase):
|
||||
|
||||
update_email_opt_in(user, course.id.org, True)
|
||||
result_obj = UserOrgTag.objects.get(user=user, org=course.id.org, key='email-optin')
|
||||
assert result_obj.value == u'True'
|
||||
assert result_obj.value == 'True'
|
||||
|
||||
def test_update_email_optin_anonymous_user(self):
|
||||
"""Verify that the API raises an exception for a user with no profile."""
|
||||
@@ -350,16 +349,16 @@ class UpdateEmailOptInTests(ModuleStoreTestCase):
|
||||
|
||||
@ddt.data(
|
||||
# Check that a 27 year old can opt-in, then out.
|
||||
(27, True, False, u"False"),
|
||||
(27, True, False, "False"),
|
||||
|
||||
# Check that a 32-year old can opt-out, then in.
|
||||
(32, False, True, u"True"),
|
||||
(32, False, True, "True"),
|
||||
|
||||
# Check that someone 13 years old can opt-in, then out.
|
||||
(13, True, False, u"False"),
|
||||
(13, True, False, "False"),
|
||||
|
||||
# Check that someone 12 years old cannot opt-in, then explicitly out.
|
||||
(12, True, False, u"False")
|
||||
(12, True, False, "False")
|
||||
)
|
||||
@ddt.unpack
|
||||
@override_settings(EMAIL_OPTIN_MINIMUM_AGE=13)
|
||||
@@ -425,11 +424,11 @@ def get_expected_validation_developer_message(preference_key, preference_value):
|
||||
"""
|
||||
Returns the expected dict of validation messages for the specified key.
|
||||
"""
|
||||
return u"Value '{preference_value}' not valid for preference '{preference_key}': {error}".format(
|
||||
return "Value '{preference_value}' not valid for preference '{preference_key}': {error}".format(
|
||||
preference_key=preference_key,
|
||||
preference_value=preference_value,
|
||||
error={
|
||||
"key": [u"Ensure this field has no more than 255 characters."]
|
||||
"key": ["Ensure this field has no more than 255 characters."]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -438,11 +437,11 @@ def get_expected_key_error_user_message(preference_key, preference_value): # li
|
||||
"""
|
||||
Returns the expected user message for an invalid key.
|
||||
"""
|
||||
return u"Invalid user preference key '{preference_key}'.".format(preference_key=preference_key)
|
||||
return f"Invalid user preference key '{preference_key}'."
|
||||
|
||||
|
||||
def get_empty_preference_message(preference_key):
|
||||
"""
|
||||
Returns the validation message shown for an empty preference.
|
||||
"""
|
||||
return u"Preference '{preference_key}' cannot be set to an empty value.".format(preference_key=preference_key)
|
||||
return f"Preference '{preference_key}' cannot be set to an empty value."
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Unit tests for preference APIs.
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.test.testcases import TransactionTestCase
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from openedx.core.djangolib.testing.utils import skip_unless_lms
|
||||
@@ -20,7 +18,7 @@ from ...accounts.tests.test_views import UserAPITestCase
|
||||
from ..api import set_user_preference
|
||||
from .test_api import get_expected_key_error_user_message, get_expected_validation_developer_message
|
||||
|
||||
TOO_LONG_PREFERENCE_KEY = u"x" * 256
|
||||
TOO_LONG_PREFERENCE_KEY = "x" * 256
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -30,7 +28,7 @@ class TestPreferencesAPI(UserAPITestCase):
|
||||
Unit tests /api/user/v1/accounts/{username}/
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestPreferencesAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.url_endpoint_name = "preferences_api"
|
||||
self.url = reverse(self.url_endpoint_name, kwargs={'username': self.user.username})
|
||||
|
||||
@@ -151,11 +149,8 @@ class TestPreferencesAPI(UserAPITestCase):
|
||||
expected_status=204
|
||||
)
|
||||
response = self.send_get(self.client)
|
||||
if six.PY2:
|
||||
pref_dict = {u"dict_pref": u"{u'int_key': 10}", u"string_pref": u"value"}
|
||||
else:
|
||||
# lint-amnesty, pylint: disable=bad-option-value, unicode-format-string
|
||||
pref_dict = {"dict_pref": "{'int_key': 10}", "string_pref": "value"}
|
||||
# lint-amnesty, pylint: disable=bad-option-value, unicode-format-string
|
||||
pref_dict = {"dict_pref": "{'int_key': 10}", "string_pref": "value"}
|
||||
assert pref_dict == response.data
|
||||
|
||||
@ddt.data(
|
||||
@@ -228,22 +223,22 @@ class TestPreferencesAPI(UserAPITestCase):
|
||||
"string_pref": "updated_value",
|
||||
TOO_LONG_PREFERENCE_KEY: "new_value",
|
||||
"new_pref": "new_value",
|
||||
u"empty_pref_ȻħȺɍłɇs": "",
|
||||
"empty_pref_ȻħȺɍłɇs": "",
|
||||
"time_zone": "Asia/Africa",
|
||||
},
|
||||
expected_status=400
|
||||
)
|
||||
assert response.data.get('field_errors', None)
|
||||
field_errors = response.data["field_errors"]
|
||||
assert field_errors == {TOO_LONG_PREFERENCE_KEY: {'developer_message': get_expected_validation_developer_message(TOO_LONG_PREFERENCE_KEY, 'new_value'), 'user_message': get_expected_key_error_user_message(TOO_LONG_PREFERENCE_KEY, 'new_value')}, u'empty_pref_ȻħȺɍłɇs': {'developer_message': u"Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value.", 'user_message': u"Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value."}, 'time_zone': {'developer_message': u"Value 'Asia/Africa' not valid for preference 'time_zone': Not in timezone set.", 'user_message': u"Value 'Asia/Africa' is not a valid time zone selection."}} # pylint: disable=line-too-long
|
||||
assert field_errors == {TOO_LONG_PREFERENCE_KEY: {'developer_message': get_expected_validation_developer_message(TOO_LONG_PREFERENCE_KEY, 'new_value'), 'user_message': get_expected_key_error_user_message(TOO_LONG_PREFERENCE_KEY, 'new_value')}, 'empty_pref_ȻħȺɍłɇs': {'developer_message': "Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value.", 'user_message': "Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value."}, 'time_zone': {'developer_message': "Value 'Asia/Africa' not valid for preference 'time_zone': Not in timezone set.", 'user_message': "Value 'Asia/Africa' is not a valid time zone selection."}} # pylint: disable=line-too-long
|
||||
|
||||
# Verify that GET returns the original preferences
|
||||
response = self.send_get(self.client)
|
||||
expected_preferences = {
|
||||
"dict_pref": u"{'int_key': 10}",
|
||||
"string_pref": u"value",
|
||||
"extra_pref": u"extra_value",
|
||||
"time_zone": u"Pacific/Midway",
|
||||
"dict_pref": "{'int_key': 10}",
|
||||
"string_pref": "value",
|
||||
"extra_pref": "extra_value",
|
||||
"time_zone": "Pacific/Midway",
|
||||
}
|
||||
assert expected_preferences == response.data
|
||||
|
||||
@@ -256,14 +251,14 @@ class TestPreferencesAPI(UserAPITestCase):
|
||||
# Verify a non-dict request
|
||||
response = self.send_patch(self.client, "non_dict_request", expected_status=400)
|
||||
assert response.data ==\
|
||||
{'developer_message': u'No data provided for user preference update',
|
||||
'user_message': u'No data provided for user preference update'}
|
||||
{'developer_message': 'No data provided for user preference update',
|
||||
'user_message': 'No data provided for user preference update'}
|
||||
|
||||
# Verify an empty dict request
|
||||
response = self.send_patch(self.client, {}, expected_status=400)
|
||||
assert response.data ==\
|
||||
{'developer_message': u'No data provided for user preference update',
|
||||
'user_message': u'No data provided for user preference update'}
|
||||
{'developer_message': 'No data provided for user preference update',
|
||||
'user_message': 'No data provided for user preference update'}
|
||||
|
||||
@ddt.data(
|
||||
("different_client", "different_user"),
|
||||
@@ -300,7 +295,7 @@ class TestPreferencesAPITransactions(TransactionTestCase):
|
||||
test_password = "test"
|
||||
|
||||
def setUp(self):
|
||||
super(TestPreferencesAPITransactions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.client = APIClient()
|
||||
self.user = UserFactory.create(password=TEST_PASSWORD)
|
||||
self.url = reverse("preferences_api", kwargs={'username': self.user.username})
|
||||
@@ -345,7 +340,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
Unit tests /api/user/v1/accounts/{username}/{preference_key}
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestPreferencesDetailAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_pref_key = "test_key"
|
||||
self.test_pref_value = "test_value"
|
||||
set_user_preference(self.user, self.test_pref_key, self.test_pref_value)
|
||||
@@ -428,7 +423,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
set_user_preference(self.user, "dict_pref", {"int_key": 10})
|
||||
self._set_url("dict_pref")
|
||||
response = self.send_get(client)
|
||||
assert u"{'int_key': 10}" == response.data
|
||||
assert "{'int_key': 10}" == response.data
|
||||
|
||||
def test_create_preference(self):
|
||||
"""
|
||||
@@ -470,8 +465,8 @@ class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
response = self.send_put(self.client, preference_value, expected_status=400)
|
||||
assert response.data ==\
|
||||
{'developer_message': u"Preference 'new_key' cannot be set to an empty value.",
|
||||
'user_message': u"Preference 'new_key' cannot be set to an empty value."}
|
||||
{'developer_message': "Preference 'new_key' cannot be set to an empty value.",
|
||||
'user_message': "Preference 'new_key' cannot be set to an empty value."}
|
||||
self.send_get(self.client, expected_status=404)
|
||||
|
||||
def test_create_preference_too_long_key(self):
|
||||
@@ -504,9 +499,9 @@ class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
self.send_put(client, new_value, expected_status=403)
|
||||
|
||||
@ddt.data(
|
||||
(u"new value",),
|
||||
("new value",),
|
||||
(10,),
|
||||
({u"int_key": 10},)
|
||||
({"int_key": 10},)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_update_preference(self, preference_value):
|
||||
@@ -516,7 +511,7 @@ class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
self.send_put(self.client, preference_value)
|
||||
response = self.send_get(self.client)
|
||||
assert six.text_type(preference_value) == response.data
|
||||
assert str(preference_value) == response.data
|
||||
|
||||
@ddt.data(
|
||||
("different_client", "different_user"),
|
||||
@@ -543,8 +538,8 @@ class TestPreferencesDetailAPI(UserAPITestCase):
|
||||
"""
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
response = self.send_put(self.client, preference_value, expected_status=400)
|
||||
assert response.data == {'developer_message': u"Preference 'test_key' cannot be set to an empty value.",
|
||||
'user_message': u"Preference 'test_key' cannot be set to an empty value."}
|
||||
assert response.data == {'developer_message': "Preference 'test_key' cannot be set to an empty value.",
|
||||
'user_message': "Preference 'test_key' cannot be set to an empty value."}
|
||||
response = self.send_get(self.client)
|
||||
assert self.test_pref_value == response.data
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ class VerificationsDetailsViewTests(VerificationStatusViewTestsMixin, TestCase):
|
||||
'status': self.photo_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(kwargs.get('expected_expires').isoformat()),
|
||||
'message': '',
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': self.photo_verification.receipt_id,
|
||||
}]
|
||||
|
||||
@@ -155,25 +155,25 @@ class VerificationsDetailsViewTests(VerificationStatusViewTestsMixin, TestCase):
|
||||
{
|
||||
'type': 'Software Secure',
|
||||
'status': self.photo_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
|
||||
'expiration_datetime': f'{expected_expires.isoformat()}Z',
|
||||
'message': self.photo_verification.error_msg,
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': self.photo_verification.receipt_id
|
||||
},
|
||||
{
|
||||
'type': 'SSO',
|
||||
'status': self.sso_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
|
||||
'expiration_datetime': f'{expected_expires.isoformat()}Z',
|
||||
'message': '',
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': None,
|
||||
},
|
||||
{
|
||||
'type': 'Manual',
|
||||
'status': self.manual_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
|
||||
'expiration_datetime': f'{expected_expires.isoformat()}Z',
|
||||
'message': self.manual_verification.reason,
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': None,
|
||||
},
|
||||
]
|
||||
@@ -195,25 +195,25 @@ class VerificationsDetailsViewTests(VerificationStatusViewTestsMixin, TestCase):
|
||||
{
|
||||
'type': 'Software Secure',
|
||||
'status': self.photo_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
|
||||
'expiration_datetime': f'{expected_expires.isoformat()}Z',
|
||||
'message': self.photo_verification.error_msg,
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': self.photo_verification.receipt_id,
|
||||
},
|
||||
{
|
||||
'type': 'Software Secure',
|
||||
'status': second_ss_photo_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
|
||||
'expiration_datetime': f'{expected_expires.isoformat()}Z',
|
||||
'message': second_ss_photo_verification.error_msg,
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': second_ss_photo_verification.receipt_id,
|
||||
},
|
||||
{
|
||||
'type': 'SSO',
|
||||
'status': self.sso_verification.status,
|
||||
'expiration_datetime': '{}Z'.format(expected_expires.isoformat()),
|
||||
'expiration_datetime': f'{expected_expires.isoformat()}Z',
|
||||
'message': '',
|
||||
'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()),
|
||||
'updated_at': f'{self.CREATED_AT.isoformat()}Z',
|
||||
'receipt_id': None,
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user