From 2a027286260849f69fbb660716d1a156bbd34523 Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Fri, 15 Jul 2016 14:06:06 -0400 Subject: [PATCH] Remove Firefox Accounts and related code - Drop django-allauth, oauthlib, and python3-openid from requirements.txt - Remove FxA and allauth related things from testpilot/settings.py, including Basket API keys - Remove more FxA related modules from the testpilot.users app - Clean up unused Jinja templates - user_id no longer needed in frontend view - Remove & clean up tests Fixes #1035 --- bin/run-common.sh | 1 - requirements.txt | 10 -- testpilot/frontend/views.py | 3 +- testpilot/settings.py | 47 ------ testpilot/users/__init__.py | 1 - testpilot/users/apps.py | 9 -- testpilot/users/management/__init__.py | 0 .../users/management/commands/__init__.py | 0 .../management/commands/updatefxaprovider.py | 50 ------ testpilot/users/models.py | 29 ---- testpilot/users/providers/__init__.py | 0 testpilot/users/providers/fxa/__init__.py | 0 testpilot/users/providers/fxa/provider.py | 32 ---- testpilot/users/providers/fxa/urls.py | 4 - testpilot/users/providers/fxa/views.py | 35 ----- testpilot/users/signals.py | 20 --- testpilot/users/templates/account/logout.html | 17 --- .../socialaccount/authentication_error.html | 16 -- .../socialaccount/login_cancelled.html | 11 -- .../users/templates/socialaccount/signup.html | 22 --- testpilot/users/tests.py | 144 ------------------ 21 files changed, 1 insertion(+), 450 deletions(-) delete mode 100644 testpilot/users/apps.py delete mode 100644 testpilot/users/management/__init__.py delete mode 100644 testpilot/users/management/commands/__init__.py delete mode 100644 testpilot/users/management/commands/updatefxaprovider.py delete mode 100644 testpilot/users/providers/__init__.py delete mode 100644 testpilot/users/providers/fxa/__init__.py delete mode 100644 testpilot/users/providers/fxa/provider.py delete mode 100644 testpilot/users/providers/fxa/urls.py delete mode 100644 testpilot/users/providers/fxa/views.py delete mode 100644 testpilot/users/signals.py delete mode 100644 testpilot/users/templates/account/logout.html delete mode 100644 testpilot/users/templates/socialaccount/authentication_error.html delete mode 100644 testpilot/users/templates/socialaccount/login_cancelled.html delete mode 100644 testpilot/users/templates/socialaccount/signup.html diff --git a/bin/run-common.sh b/bin/run-common.sh index 28bd9e08f1..c4cd2d1703 100755 --- a/bin/run-common.sh +++ b/bin/run-common.sh @@ -2,4 +2,3 @@ ./manage.py migrate --noinput ./manage.py createinitialsuperuser -./manage.py updatefxaprovider diff --git a/requirements.txt b/requirements.txt index 61cc43f996..3e044c00c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -79,19 +79,9 @@ django-jinja==1.4.1 \ defusedxml==0.4.1 \ --hash=sha256:cd551d5a518b745407635bb85116eb813818ecaf182e773c35b36239fc3f2478 \ --hash=sha256:1f7e2f6546caba6a52bcd6a6087df60c1cc63cdbc0d9ca3e5d517adcb5823d0c -django-allauth==0.24.1 \ - --hash=sha256:229a6a5a63ff55f9eb46d4dae3863ba775735bf60bfc16bf4e2042f214f18e1d -oauthlib==1.0.3 \ - --hash=sha256:ef4bfe4663ca3b97a995860c0173b967ebd98033d02f38c9e1b2cbb6c191d9ad -python3-openid==3.0.6 \ - --hash=sha256:833086e554515d58fe39f9326eae5c929ff448f0fce6350d45f622c9cc1591c6 \ - --hash=sha256:8b82c6e8ac590b0433988138bf63d934dbf5cdcbad1643ec7e25cc0ce90ca50a requests==2.7.0 \ --hash=sha256:20f976cdce02a42b69ce80e9e03897a51814b36d448b37288546086ebc473146 \ --hash=sha256:398a3db6d61899d25fd4a06c6ca12051b0ce171d705decd7ed5511517b4bb93d -requests_oauthlib==0.5.0 \ - --hash=sha256:658d9aba85338be8c1d1532c9fb5807b381dc7166e469ff0f62fcaa4240d9eb8 \ - --hash=sha256:c7d2ee6c2e8c58a0fd60c2d7d21ce93c8d2b22e7a9f232d4bf915326afb479af Pillow==3.2.0 \ --hash=sha256:f6e89c3b024a13060de19732e82d1dfd657cdf46b15d647c0f0f5aaa07a6a419 \ --hash=sha256:46cbc1d1487a9c590708e9e135530ac94af8373bf1e8ef0c7acf6cc46c84d3c4 \ diff --git a/testpilot/frontend/views.py b/testpilot/frontend/views.py index ca98d669bb..67980cb52d 100644 --- a/testpilot/frontend/views.py +++ b/testpilot/frontend/views.py @@ -2,5 +2,4 @@ def index(request, url=''): - return render(request, 'testpilot/frontend/index.html', - {'user_id': request.user.email if not request.user.is_anonymous() else ''}) + return render(request, 'testpilot/frontend/index.html') diff --git a/testpilot/settings.py b/testpilot/settings.py index a247634381..1bfc606670 100644 --- a/testpilot/settings.py +++ b/testpilot/settings.py @@ -86,12 +86,6 @@ def path(*args): 'constance.backends.database', 'constance', - # FxA auth handling - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'testpilot.users.providers.fxa', - # Django apps 'django.contrib.admin', 'django.contrib.auth', @@ -121,11 +115,7 @@ def path(*args): AUTHENTICATION_BACKENDS = ( - # Needed to login by username in Django admin, regardless of `allauth` 'django.contrib.auth.backends.ModelBackend', - - # `allauth` specific authentication methods, such as login by e-mail - 'allauth.account.auth_backends.AuthenticationBackend', ) REST_FRAMEWORK = { @@ -139,40 +129,6 @@ def path(*args): 'DEFAULT_VERSION': '1.0.0' } -SOCIALACCOUNT_PROVIDERS = { - 'fxa': dict( - ACCESS_TOKEN_URL=config( - 'FXA_ACCESS_TOKEN_URL', - default='https://oauth.accounts.firefox.com/v1/token'), - AUTHORIZE_URL=config( - 'FXA_AUTHORIZE_URL', - default='https://oauth.accounts.firefox.com/v1/authorization'), - PROFILE_URL=config( - 'FXA_PROFILE_URL', - default='https://profile.accounts.firefox.com/v1/profile'), - SCOPE=["profile"], - AUTH_PARAMS={ - "verification_redirect": "always" - } - ) -} - -FXA_CLIENT_ID = config('FXA_CLIENT_ID', default=None) - -FXA_SECRET_KEY = config('FXA_SECRET_KEY', default=None) - -BASKET_API_KEY = config('BASKET_API_KEY', default=None) - -BASKET_LOOKUP_USER_URL = 'https://basket.mozilla.org/news/lookup-user/' - -BASKET_UNSUBSCRIBE_URL = 'https://basket.mozilla.org/news/unsubscribe/' - -BASKET_SUBSCRIBE_URL = 'https://basket.mozilla.org/news/subscribe/' - -SOCIALACCOUNT_AUTO_SIGNUP = True - -SOCIALACCOUNT_EMAIL_VERIFICATION = False - ACCOUNT_EMAIL_VERIFICATION = False @@ -361,10 +317,7 @@ def lazy_langs(): 'django.template.context_processors.tz', 'django.template.context_processors.request', 'django.contrib.messages.context_processors.messages', - - # `allauth` needs this from django 'django.template.context_processors.request', - ], } }, diff --git a/testpilot/users/__init__.py b/testpilot/users/__init__.py index f10d83e323..e69de29bb2 100644 --- a/testpilot/users/__init__.py +++ b/testpilot/users/__init__.py @@ -1 +0,0 @@ -default_app_config = 'testpilot.users.apps.TestPilotUsersAppConfig' diff --git a/testpilot/users/apps.py b/testpilot/users/apps.py deleted file mode 100644 index 48142e3bce..0000000000 --- a/testpilot/users/apps.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.apps import AppConfig - - -class TestPilotUsersAppConfig(AppConfig): - name = 'testpilot.users' - verbose_name = 'Test Pilot Users' - - def ready(self): - import testpilot.users.signals # noqa diff --git a/testpilot/users/management/__init__.py b/testpilot/users/management/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/testpilot/users/management/commands/__init__.py b/testpilot/users/management/commands/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/testpilot/users/management/commands/updatefxaprovider.py b/testpilot/users/management/commands/updatefxaprovider.py deleted file mode 100644 index 56c7184d47..0000000000 --- a/testpilot/users/management/commands/updatefxaprovider.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.core.exceptions import ObjectDoesNotExist -from django.core.management.base import BaseCommand -from django.conf import settings -from django.contrib.sites.models import Site -from allauth.socialaccount.models import SocialApp -from testpilot.users.providers.fxa.provider import FirefoxAccountsProvider - - -FXA_PROVIDER_ID = FirefoxAccountsProvider.id - - -class Command(BaseCommand): - help = ('Ensures an allauth application for Firefox Accounts exists and has ' - 'credentials that match settings') - - def handle(self, *args, **options): - # Check if FXA_* settings are configured, bail if not. - if settings.FXA_CLIENT_ID is None or settings.FXA_SECRET_KEY is None: - self.stdout.write("FXA_* settings unavailable; " - "skipping provider config.") - return - - # Grab the credentials from settings - data = dict( - name='FxA', - provider=FXA_PROVIDER_ID, - client_id=settings.FXA_CLIENT_ID, - secret=settings.FXA_SECRET_KEY - ) - - try: - # Update the existing provider with current settings. - app = SocialApp.objects.get(provider=FXA_PROVIDER_ID) - self.stdout.write("Updating existing Firefox Accounts provider " - "(pk=%s)" % app.pk) - for k, v in data.items(): - setattr(app, k, v) - app.save() - except ObjectDoesNotExist: - # Create the provider if necessary. - app = SocialApp(**data) - app.save() - self.stdout.write("Created new Firefox Accounts provider (pk=%s)" % - app.pk) - - # Ensure the provider applies to the current default site. - sites_count = app.sites.count() - if sites_count == 0: - default_site = Site.objects.get(pk=settings.SITE_ID) - app.sites.add(default_site) diff --git a/testpilot/users/models.py b/testpilot/users/models.py index 5e25ee9152..de5bbf9dda 100644 --- a/testpilot/users/models.py +++ b/testpilot/users/models.py @@ -1,11 +1,8 @@ from django.db import models from django.contrib.auth.models import User -from django.conf import settings from ..utils import HashedUploadTo -import requests - avatar_upload_to = HashedUploadTo('avatar') @@ -39,29 +36,3 @@ def natural_key(self): return self.user.natural_key() natural_key.dependencies = ['auth.user'] - - def unsubscribe(self): - """Unsubscribe this email from basket. Lookup-user call for token, then - perform the unsubscribe""" - try: - if settings.BASKET_API_KEY is None: - return 'API Key not found' - email = self.user.email - url = settings.BASKET_LOOKUP_USER_URL - - params = {'api_key': settings.BASKET_API_KEY, 'email': email} - r = requests.get(url, params=params) - resp = r.json() - - if 'token' not in resp: - return 'email not found in basket' - user_token = resp['token'] - - url = settings.BASKET_UNSUBSCRIBE_URL + user_token + '/' - payload = {'newsletters': 'test-pilot', 'email': email} - r = requests.post(url, data=payload) - resp = r.json() - - return resp - except Exception as e: - return e diff --git a/testpilot/users/providers/__init__.py b/testpilot/users/providers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/testpilot/users/providers/fxa/__init__.py b/testpilot/users/providers/fxa/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/testpilot/users/providers/fxa/provider.py b/testpilot/users/providers/fxa/provider.py deleted file mode 100644 index 69569afad4..0000000000 --- a/testpilot/users/providers/fxa/provider.py +++ /dev/null @@ -1,32 +0,0 @@ -from allauth.socialaccount import providers -from allauth.socialaccount.providers.base import ProviderAccount -from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider - - -import logging -logger = logging.getLogger(__name__) - - -class FirefoxAccountsAccount(ProviderAccount): - - def to_str(self): - return self.account.uid - - -class FirefoxAccountsProvider(OAuth2Provider): - id = 'fxa' - name = 'Firefox Accounts (for Test Pilot)' - package = 'testpilot.users.providers.fxa' - account_class = FirefoxAccountsAccount - - def get_default_scope(self): - return ['profile'] - - def extract_uid(self, data): - return str(data['uid']) - - def extract_common_fields(self, data): - return dict(email=data.get('email')) - - -providers.registry.register(FirefoxAccountsProvider) diff --git a/testpilot/users/providers/fxa/urls.py b/testpilot/users/providers/fxa/urls.py deleted file mode 100644 index f081ae9f0e..0000000000 --- a/testpilot/users/providers/fxa/urls.py +++ /dev/null @@ -1,4 +0,0 @@ -from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns -from .provider import FirefoxAccountsProvider - -urlpatterns = default_urlpatterns(FirefoxAccountsProvider) diff --git a/testpilot/users/providers/fxa/views.py b/testpilot/users/providers/fxa/views.py deleted file mode 100644 index 4791b49c09..0000000000 --- a/testpilot/users/providers/fxa/views.py +++ /dev/null @@ -1,35 +0,0 @@ -import requests - -from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter, - OAuth2LoginView, - OAuth2CallbackView) -from .provider import FirefoxAccountsProvider - -import logging -logger = logging.getLogger(__name__) - - -class FirefoxAccountsOAuth2Adapter(OAuth2Adapter): - provider_id = FirefoxAccountsProvider.id - - def __init__(self): - provider = self.get_provider() - provider_settings = provider.get_settings() - - self.access_token_url = provider_settings.get( - 'ACCESS_TOKEN_URL', 'https://oauth.accounts.firefox.com/v1/token') - self.authorize_url = provider_settings.get( - 'AUTHORIZE_URL', 'https://oauth.accounts.firefox.com/v1/authorization') - self.profile_url = provider_settings.get( - 'PROFILE_URL', 'https://profile.accounts.firefox.com/v1/profile') - - def complete_login(self, request, app, token, **kwargs): - headers = {'Authorization': 'Bearer {0}'.format(token.token)} - resp = requests.get(self.profile_url, headers=headers) - extra_data = resp.json() - return self.get_provider().sociallogin_from_response(request, - extra_data) - - -oauth2_login = OAuth2LoginView.adapter_view(FirefoxAccountsOAuth2Adapter) -oauth2_callback = OAuth2CallbackView.adapter_view(FirefoxAccountsOAuth2Adapter) diff --git a/testpilot/users/signals.py b/testpilot/users/signals.py deleted file mode 100644 index c0c818ada1..0000000000 --- a/testpilot/users/signals.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Customizations to the signup & login process for Test Pilot. - -See also: http://django-allauth.readthedocs.org/en/latest/signals.html -""" -from django.dispatch import receiver - -from allauth.account.signals import user_signed_up - -import logging - - -@receiver(user_signed_up) -def invite_only_signup_handler(sender, **kwargs): - """ - Sent when a user signs up for an account. - """ - user = kwargs['user'] - logger = logging.getLogger('testpilot.newuser') - logger.info('', extra={'uid': user.id}) diff --git a/testpilot/users/templates/account/logout.html b/testpilot/users/templates/account/logout.html deleted file mode 100644 index 415fe6e1a4..0000000000 --- a/testpilot/users/templates/account/logout.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "testpilot/base.html" %} - -{% block page_title %}{{ _('Sign out') }}{% endblock %} - -{% block content %} -

{{ _('Sign out') }}

- -

{{ _('Are you sure you want to sign out?') }}

- -
- {% csrf_token %} - {% if redirect_field_value %} - - {% endif %} - -
-{% endblock %} diff --git a/testpilot/users/templates/socialaccount/authentication_error.html b/testpilot/users/templates/socialaccount/authentication_error.html deleted file mode 100644 index cf6551558b..0000000000 --- a/testpilot/users/templates/socialaccount/authentication_error.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "testpilot/base.html" %} - -{% block head %} - {{ super() }} - -{% endblock %} - -{% block page_title %}{{ _('Social Network Login Failure') }}{% endblock %} - -{% block content %} -

{{ _('Social Network Login Failure') }}

-

{% trans %} - An error occurred while attempting to login via your social network account. - {% endtrans %}

-

{% trans %}Return to the home page{% endtrans %}

-{% endblock %} diff --git a/testpilot/users/templates/socialaccount/login_cancelled.html b/testpilot/users/templates/socialaccount/login_cancelled.html deleted file mode 100644 index 54748e0b7f..0000000000 --- a/testpilot/users/templates/socialaccount/login_cancelled.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "testpilot/base.html" %} - -{% block page_title %}{{ _('Login Cancelled') }}{% endblock %} - -{% block content %} - -

{{ _("Login Cancelled") }}

- -

{% trans %}You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please proceed to sign in.{% endtrans %}

- -{% endblock %} diff --git a/testpilot/users/templates/socialaccount/signup.html b/testpilot/users/templates/socialaccount/signup.html deleted file mode 100644 index 38575d9c08..0000000000 --- a/testpilot/users/templates/socialaccount/signup.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "testpilot/base.html" %} - -{% block page_title %}{{ _('Sign up') }}{% endblock %} - -{% block content %} -

{{ _('Sign up') }}

- -

{% trans %} -You are about to use Firefox Accounts to login to Test Pilot. -As a final step, please complete the following form: -{% endtrans %}

- -
- {% csrf_token %} - {{ form.as_p() }} - {% if redirect_field_value %} - - {% endif %} - -
-{% endblock %} - diff --git a/testpilot/users/tests.py b/testpilot/users/tests.py index e6d4aad39a..5b9932ce3c 100644 --- a/testpilot/users/tests.py +++ b/testpilot/users/tests.py @@ -1,9 +1,4 @@ -from unittest.mock import patch, Mock - -from requests.exceptions import Timeout - from django.test import TestCase -from django.test.utils import override_settings from django.contrib.auth.models import User from .models import UserProfile @@ -12,12 +7,6 @@ logger = logging.getLogger(__name__) -EXPECTED_BASKET_API_KEY = 'baskethooray', -EXPECTED_BASKET_LOOKUP_USER_URL = 'https://example.com/news/lookup-user/' -EXPECTED_BASKET_UNSUBSCRIBE_URL = 'https://example.com/news/unsubscribe/' -EXPECTED_BASKET_SUBSCRIBE_URL = 'https://example.com/news/subscribe/' - - class UserProfileTests(TestCase): maxDiff = None @@ -57,136 +46,3 @@ def test_get_profile_exists(self): result_profile = UserProfile.objects.get_profile(user=self.user) self.assertIsNotNone(result_profile) self.assertEqual(expected_title, result_profile.title) - - @override_settings(BASKET_API_KEY=None) - def test_unsubscribe_missing_api_key(self): - """Newsletter unsubscribe should fail gracefully on missing API key""" - profile = UserProfile.objects.get_profile(self.user) - result = profile.unsubscribe() - self.assertEqual('API Key not found', result) - - @patch('requests.get') - @patch('requests.post') - @override_settings( - BASKET_API_KEY=EXPECTED_BASKET_API_KEY, - BASKET_LOOKUP_USER_URL=EXPECTED_BASKET_LOOKUP_USER_URL, - BASKET_UNSUBSCRIBE_URL=EXPECTED_BASKET_UNSUBSCRIBE_URL, - BASKET_SUBSCRIBE_URL=EXPECTED_BASKET_SUBSCRIBE_URL) - def test_unsubscribe(self, mock_requests_post, mock_requests_get): - """Newsletter unsubscribe should call Basket API as expected""" - # http://basket.readthedocs.io/newsletter_api.html#news-lookup-user - expected_get_params = { - 'api_key': EXPECTED_BASKET_API_KEY, - 'email': self.user.email - } - expected_user_token = '8675309' - mock_get_response = Mock() - mock_get_json = Mock(return_value={ - 'token': expected_user_token - }) - mock_get_response.json = mock_get_json - mock_requests_get.return_value = mock_get_response - - # http://basket.readthedocs.io/newsletter_api.html#news-unsubscribe - expected_post_data = { - 'newsletters': 'test-pilot', - 'email': self.user.email - } - mock_post_response = Mock() - mock_post_json = Mock(return_value={'status': 'ok'}) - mock_post_response.json = mock_post_json - mock_requests_post.return_value = mock_post_response - - profile = UserProfile.objects.get_profile(self.user) - profile.unsubscribe() - - mock_requests_get.assert_called_with(EXPECTED_BASKET_LOOKUP_USER_URL, - params=expected_get_params) - mock_get_json.assert_called_with() - - expected_url = '%s%s/' % (EXPECTED_BASKET_UNSUBSCRIBE_URL, - expected_user_token) - mock_requests_post.assert_called_with(expected_url, - data=expected_post_data) - mock_post_json.assert_called_with() - - @patch('requests.get') - @patch('requests.post') - @override_settings( - BASKET_API_KEY=EXPECTED_BASKET_API_KEY, - BASKET_LOOKUP_USER_URL=EXPECTED_BASKET_LOOKUP_USER_URL, - BASKET_UNSUBSCRIBE_URL=EXPECTED_BASKET_UNSUBSCRIBE_URL, - BASKET_SUBSCRIBE_URL=EXPECTED_BASKET_SUBSCRIBE_URL) - def test_unsubscribe_lookup_error(self, mock_requests_post, mock_requests_get): - """Newsletter unsubscribe should report lookup failure""" - # http://basket.readthedocs.io/newsletter_api.html#news-lookup-user - expected_get_params = { - 'api_key': EXPECTED_BASKET_API_KEY, - 'email': self.user.email - } - mock_get_response = Mock() - mock_get_json = Mock(return_value={ - 'status': 'error', - 'desc': 'user not found' - }) - mock_get_response.json = mock_get_json - mock_requests_get.return_value = mock_get_response - - # http://basket.readthedocs.io/newsletter_api.html#news-unsubscribe - mock_post_response = Mock() - mock_post_json = Mock() - mock_post_response.json = mock_post_json - mock_requests_post.return_value = mock_post_response - - profile = UserProfile.objects.get_profile(self.user) - result = profile.unsubscribe() - - self.assertEqual('email not found in basket', result) - - mock_requests_get.assert_called_with(EXPECTED_BASKET_LOOKUP_USER_URL, - params=expected_get_params) - mock_get_json.assert_called_with() - - self.assertFalse(mock_requests_post.called) - self.assertFalse(mock_post_json.called) - - @patch('requests.get') - @patch('requests.post') - @override_settings( - BASKET_API_KEY=EXPECTED_BASKET_API_KEY, - BASKET_LOOKUP_USER_URL=EXPECTED_BASKET_LOOKUP_USER_URL, - BASKET_UNSUBSCRIBE_URL=EXPECTED_BASKET_UNSUBSCRIBE_URL, - BASKET_SUBSCRIBE_URL=EXPECTED_BASKET_SUBSCRIBE_URL) - def test_unsubscribe_request_exception(self, mock_requests_post, mock_requests_get): - """Newsletter unsubscribe should gracefully handle request exceptions""" - expected_get_params = { - 'api_key': EXPECTED_BASKET_API_KEY, - 'email': self.user.email - } - mock_get_response = Mock() - mock_get_json = Mock(return_value={}) - mock_get_response.json = mock_get_json - - expected_result = Timeout() - - def raise_request_exception(*args, **kwargs): - raise expected_result - - mock_requests_get.side_effect = raise_request_exception - mock_requests_get.return_value = mock_get_response - - mock_post_response = Mock() - mock_post_json = Mock() - mock_post_response.json = mock_post_json - mock_requests_post.return_value = mock_post_response - - profile = UserProfile.objects.get_profile(self.user) - result = profile.unsubscribe() - - self.assertEqual(expected_result, result) - - mock_requests_get.assert_called_with(EXPECTED_BASKET_LOOKUP_USER_URL, - params=expected_get_params) - self.assertFalse(mock_get_json.called) - self.assertFalse(mock_requests_post.called) - self.assertFalse(mock_post_json.called)