-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add/remove user from organization synchronously (#2715)
* add/remove user from organization synchronously Since an organization is a Group, adding/removing user from an organization does not take long and can be done synchronously. Sharing organization projects will take longer and remains asynchronous * fix lint error cyclic-import * add docstring * ignore items order in test * add comment * fix failing tests
- Loading branch information
1 parent
0169c90
commit 19a52eb
Showing
6 changed files
with
88 additions
and
367 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,17 +7,13 @@ | |
from django.core.cache import cache | ||
from django.contrib.auth import get_user_model | ||
from django.db import DatabaseError, OperationalError | ||
from django.test import override_settings | ||
|
||
from onadata.apps.api.tasks import ( | ||
send_project_invitation_email_async, | ||
regenerate_form_instance_json, | ||
add_org_user_and_share_projects_async, | ||
remove_org_user_async, | ||
share_project_async, | ||
ShareProject, | ||
) | ||
from onadata.apps.api.models.organization_profile import OrganizationProfile | ||
from onadata.apps.logger.models import ProjectInvitation, Instance | ||
from onadata.apps.main.tests.test_base import TestBase | ||
from onadata.libs.permissions import ManagerRole | ||
|
@@ -122,180 +118,12 @@ def mock_get_full_dict( | |
instance.refresh_from_db() | ||
self.assertFalse(instance.json) | ||
|
||
|
||
def set_cache_for_org(org, request): | ||
"""Utility to set org cache""" | ||
org_profile_json = OrganizationSerializer( | ||
org, context={"request": request} | ||
).data | ||
org_profile_json = OrganizationSerializer(org, context={"request": request}).data | ||
cache.set(f"{ORG_PROFILE_CACHE}{org.user.username}-owner", org_profile_json) | ||
|
||
@patch("onadata.apps.api.tasks.tools.add_org_user_and_share_projects") | ||
class AddOrgUserAndShareProjectsAsyncTestCase(TestBase): | ||
"""Tests for add_org_user_and_share_projects_async""" | ||
|
||
def setUp(self): | ||
super().setUp() | ||
|
||
self.org_user = User.objects.create(username="onaorg") | ||
alice = self._create_user("alice", "1234&&") | ||
self.org = OrganizationProfile.objects.create( | ||
user=self.org_user, name="Ona Org", creator=alice | ||
) | ||
self.extra = {"HTTP_AUTHORIZATION": f"Token {self.user.auth_token}"} | ||
|
||
def test_user_added_to_org(self, mock_add): | ||
"""User is added to organization""" | ||
request = self.factory.get("/", **self.extra) | ||
request.user = self.user | ||
set_cache_for_org(self.org, request) | ||
cache_key = f"{ORG_PROFILE_CACHE}{self.org.user.username}-owner" | ||
self.assertIsNotNone(cache.get(cache_key)) | ||
add_org_user_and_share_projects_async.delay( | ||
self.org.pk, self.user.pk, "manager" | ||
) | ||
mock_add.assert_called_once_with(self.org, self.user, "manager") | ||
self.assertEqual(cache.get(cache_key), None) | ||
|
||
def test_role_optional(self, mock_add): | ||
"""role param is optional""" | ||
add_org_user_and_share_projects_async.delay(self.org.pk, self.user.pk) | ||
mock_add.assert_called_once_with(self.org, self.user, None) | ||
|
||
@patch("onadata.apps.api.tasks.logger.exception") | ||
def test_invalid_org_id(self, mock_log, mock_add): | ||
"""Invalid org_id is handled""" | ||
add_org_user_and_share_projects_async.delay(sys.maxsize, self.user.pk) | ||
mock_add.assert_not_called() | ||
mock_log.assert_called_once() | ||
|
||
@patch("onadata.apps.api.tasks.logger.exception") | ||
def test_invalid_user_id(self, mock_log, mock_add): | ||
"""Invalid org_id is handled""" | ||
add_org_user_and_share_projects_async.delay(self.org.pk, sys.maxsize) | ||
mock_add.assert_not_called() | ||
mock_log.assert_called_once() | ||
|
||
@patch("onadata.apps.api.tasks.add_org_user_and_share_projects_async.retry") | ||
def test_database_error(self, mock_retry, mock_add): | ||
"""We retry calls if DatabaseError is raised""" | ||
mock_add.side_effect = DatabaseError() | ||
add_org_user_and_share_projects_async.delay(self.org.pk, self.user.pk) | ||
self.assertTrue(mock_retry.called) | ||
_, kwargs = mock_retry.call_args_list[0] | ||
self.assertTrue(isinstance(kwargs["exc"], DatabaseError)) | ||
|
||
@patch("onadata.apps.api.tasks.add_org_user_and_share_projects_async.retry") | ||
def test_connection_error(self, mock_retry, mock_add): | ||
"""We retry calls if ConnectionError is raised""" | ||
mock_add.side_effect = ConnectionError() | ||
add_org_user_and_share_projects_async.delay(self.org.pk, self.user.pk) | ||
self.assertTrue(mock_retry.called) | ||
_, kwargs = mock_retry.call_args_list[0] | ||
self.assertTrue(isinstance(kwargs["exc"], ConnectionError)) | ||
|
||
@patch("onadata.apps.api.tasks.add_org_user_and_share_projects_async.retry") | ||
def test_operation_error(self, mock_retry, mock_add): | ||
"""We retry calls if OperationError is raised""" | ||
mock_add.side_effect = OperationalError() | ||
add_org_user_and_share_projects_async.delay(self.org.pk, self.user.pk) | ||
self.assertTrue(mock_retry.called) | ||
_, kwargs = mock_retry.call_args_list[0] | ||
self.assertTrue(isinstance(kwargs["exc"], OperationalError)) | ||
|
||
@override_settings(DEFAULT_FROM_EMAIL="[email protected]") | ||
@patch("onadata.apps.api.tasks.send_mail") | ||
def test_send_mail(self, mock_email, mock_add): | ||
"""Send mail works""" | ||
self.user.email = "[email protected]" | ||
self.user.save() | ||
add_org_user_and_share_projects_async.delay( | ||
self.org.pk, self.user.pk, "manager", "Subject", "Body" | ||
) | ||
mock_email.assert_called_with( | ||
"Subject", | ||
"Body", | ||
"[email protected]", | ||
("[email protected]",), | ||
) | ||
mock_add.assert_called_once_with(self.org, self.user, "manager") | ||
|
||
@override_settings(DEFAULT_FROM_EMAIL="[email protected]") | ||
@patch("onadata.apps.api.tasks.send_mail") | ||
def test_user_email_none(self, mock_email, mock_add): | ||
"""Email not sent if user email is None""" | ||
add_org_user_and_share_projects_async.delay( | ||
self.org.pk, self.user.pk, "manager", "Subject", "Body" | ||
) | ||
mock_email.assert_not_called() | ||
mock_add.assert_called_once_with(self.org, self.user, "manager") | ||
|
||
|
||
@patch("onadata.apps.api.tasks.tools.remove_user_from_organization") | ||
class RemoveOrgUserAsyncTestCase(TestBase): | ||
"""Tests for remove_org_user_async""" | ||
|
||
def setUp(self): | ||
super().setUp() | ||
|
||
self.org_user = User.objects.create(username="onaorg") | ||
alice = self._create_user("alice", "1234&&") | ||
self.org = OrganizationProfile.objects.create( | ||
user=self.org_user, name="Ona Org", creator=alice | ||
) | ||
self.extra = {"HTTP_AUTHORIZATION": f"Token {self.user.auth_token}"} | ||
|
||
def test_user_removed_from_org(self, mock_remove): | ||
"""User is removed from organization""" | ||
request = self.factory.get("/", **self.extra) | ||
request.user = self.user | ||
set_cache_for_org(self.org, request) | ||
cache_key = f"{ORG_PROFILE_CACHE}{self.org.user.username}-owner" | ||
self.assertIsNotNone(cache.get(cache_key)) | ||
remove_org_user_async.delay(self.org.pk, self.user.pk) | ||
mock_remove.assert_called_once_with(self.org, self.user) | ||
self.assertEqual(cache.get(cache_key), None) | ||
|
||
@patch("onadata.apps.api.tasks.logger.exception") | ||
def test_invalid_org_id(self, mock_log, mock_remove): | ||
"""Invalid org_id is handled""" | ||
remove_org_user_async.delay(sys.maxsize, self.user.pk) | ||
mock_remove.assert_not_called() | ||
mock_log.assert_called_once() | ||
|
||
@patch("onadata.apps.api.tasks.logger.exception") | ||
def test_invalid_user_id(self, mock_log, mock_remove): | ||
"""Invalid user_id is handled""" | ||
remove_org_user_async.delay(self.org.pk, sys.maxsize) | ||
mock_remove.assert_not_called() | ||
mock_log.assert_called_once() | ||
|
||
@patch("onadata.apps.api.tasks.remove_org_user_async.retry") | ||
def test_database_error(self, mock_retry, mock_remove): | ||
"""We retry calls if DatabaseError is raised""" | ||
mock_remove.side_effect = DatabaseError() | ||
remove_org_user_async.delay(self.org.pk, self.user.pk) | ||
self.assertTrue(mock_retry.called) | ||
_, kwargs = mock_retry.call_args_list[0] | ||
self.assertTrue(isinstance(kwargs["exc"], DatabaseError)) | ||
|
||
@patch("onadata.apps.api.tasks.remove_org_user_async.retry") | ||
def test_connection_error(self, mock_retry, mock_remove): | ||
"""We retry calls if ConnectionError is raised""" | ||
mock_remove.side_effect = ConnectionError() | ||
remove_org_user_async.delay(self.org.pk, self.user.pk) | ||
self.assertTrue(mock_retry.called) | ||
_, kwargs = mock_retry.call_args_list[0] | ||
self.assertTrue(isinstance(kwargs["exc"], ConnectionError)) | ||
|
||
@patch("onadata.apps.api.tasks.remove_org_user_async.retry") | ||
def test_operation_error(self, mock_retry, mock_remove): | ||
"""We retry calls if OperationError is raised""" | ||
mock_remove.side_effect = OperationalError() | ||
remove_org_user_async.delay(self.org.pk, self.user.pk) | ||
self.assertTrue(mock_retry.called) | ||
_, kwargs = mock_retry.call_args_list[0] | ||
self.assertTrue(isinstance(kwargs["exc"], OperationalError)) | ||
|
||
|
||
class ShareProjectAsyncTestCase(TestBase): | ||
"""Tests for share_project_async""" | ||
|
Oops, something went wrong.