import json
from django.core import mail
from django.db import transaction
from django.test import TestCase, override_settings
from django.urls import reverse
from model_bakery import baker
from ..models import OrganizationUser, OrganizationUserRole
class OrganizationUsersTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = baker.make("users.user")
cls.organization = baker.make(
"organizations_ext.Organization",
name="No",
)
cls.org_user = cls.organization.add_user(
cls.user, role=OrganizationUserRole.MANAGER
)
baker.make("organizations_ext.OrganizationUser", user=cls.user, role=5)
cls.members_url = reverse(
"api:list_organization_members", args=[cls.organization.slug]
)
def setUp(self):
self.client.force_login(self.user)
def get_org_member_detail_url(self, organization_slug, pk):
return reverse("api:get_organization_member", args=[organization_slug, pk])
def test_organization_members_list(self):
res = self.client.get(self.members_url)
self.assertContains(res, self.user.email)
data = res.json()
self.assertNotIn("teams", data[0].keys())
def test_organization_members_email_field(self):
"""
Org Member email should refer to the invited email before acceptance
After acceptance, it should refer to the user's primary email address
"""
url = self.get_org_member_detail_url(self.organization.slug, self.org_user.pk)
res = self.client.get(url)
self.assertEqual(res.json()["email"], self.user.email)
def test_organization_team_members_list(self):
team = baker.make("teams.Team", organization=self.organization)
url = reverse(
"api:list_team_organization_members",
args=[self.organization.slug, team.slug],
)
res = self.client.get(url)
self.assertNotContains(res, self.user.email)
team.members.add(self.org_user)
res = self.client.get(url)
self.assertContains(res, self.user.email)
def test_organization_members_detail(self):
other_user = baker.make("users.user")
other_organization = baker.make("organizations_ext.Organization")
other_org_user = other_organization.add_user(other_user)
team = baker.make("teams.Team", organization=self.organization)
team.members.add(self.org_user)
url = self.get_org_member_detail_url(self.organization.slug, self.org_user.pk)
res = self.client.get(url)
self.assertContains(res, self.user.email)
self.assertContains(res, team.slug)
self.assertNotContains(res, other_user.email)
url = self.get_org_member_detail_url(self.organization.slug, other_org_user.pk)
res = self.client.get(url)
self.assertEqual(res.status_code, 404)
def test_organization_users_add_team_member(self):
team = baker.make("teams.Team", organization=self.organization)
url = (
self.get_org_member_detail_url(self.organization.slug, self.org_user.pk)
+ f"teams/{team.slug}/"
)
self.assertEqual(team.members.count(), 0)
res = self.client.post(url)
self.assertEqual(res.status_code, 201)
self.assertEqual(team.members.count(), 1)
res = self.client.delete(url)
self.assertEqual(res.status_code, 200)
self.assertEqual(team.members.count(), 0)
def test_organization_users_add_self_team_member(self):
team = baker.make("teams.Team", organization=self.organization)
url = reverse(
"api:add_member_to_team", args=[self.organization.slug, "me", team.slug]
)
self.assertEqual(team.members.count(), 0)
res = self.client.post(url)
self.assertEqual(res.status_code, 201)
self.assertEqual(team.members.count(), 1)
res = self.client.delete(url)
self.assertEqual(res.status_code, 200)
self.assertEqual(team.members.count(), 0)
def test_organization_users_create_and_accept_invite(self):
data = {
"email": "new@example.com",
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [],
}
res = self.client.post(self.members_url, data, content_type="application/json")
self.assertTrue(res.json()["pending"])
body = mail.outbox[0].body
html_content = mail.outbox[0].alternatives[0][0]
self.assertFalse("No" in body)
self.assertTrue("NoHtmlInOrgName" in body)
self.assertFalse("No" in html_content)
self.assertTrue("NoHtmlInOrgName" in html_content)
body_split = body[body.find("http://localhost:8000/accept/") :].split("/")
org_user_id = body_split[4]
token = body_split[5]
url = reverse("api:get_accept_invite", args=[org_user_id, token])
# Check that we can determine organization name from GET request to accept invite endpoint
self.client.logout()
res = self.client.get(url)
self.assertContains(res, self.organization.name)
user = baker.make("users.user")
self.client.force_login(user)
data = {"acceptInvite": True}
res = self.client.post(url, data, content_type="application/json")
self.assertContains(res, self.organization.name)
self.assertFalse(res.json()["org_user"]["pending"])
self.assertTrue(
OrganizationUser.objects.filter(
user=user, organization=self.organization
).exists()
)
def test_closed_user_registration(self):
data = {
"email": "new@example.com",
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [],
}
with override_settings(ENABLE_USER_REGISTRATION=False):
# Non-existing user cannot be invited
res = self.client.post(
self.members_url, data, content_type="application/json"
)
self.assertEqual(res.status_code, 403)
# Existing user can be invited
self.user = baker.make("users.user", email="new@example.com")
res = self.client.post(
self.members_url, data, content_type="application/json"
)
self.assertEqual(res.status_code, 201)
def test_organization_users_invite_twice(self):
"""Don't allow inviting user who is already in the group"""
data = {
"email": "new@example.com",
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [],
"reinvite": False,
}
res = self.client.post(self.members_url, data, content_type="application/json")
self.assertEqual(res.status_code, 201)
with transaction.atomic():
res = self.client.post(
self.members_url, data, content_type="application/json"
)
self.assertEqual(res.status_code, 409)
data["email"] = self.user.email
res = self.client.post(self.members_url, data, content_type="application/json")
self.assertEqual(res.status_code, 409)
def test_organization_users_create(self):
team = baker.make("teams.Team", organization=self.organization)
data = {
"email": "new@example.com",
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [{"teamSlug": team.slug}],
}
res = self.client.post(
self.members_url, json.dumps(data), content_type="application/json"
)
self.assertContains(res, data["email"], status_code=201)
self.assertEqual(res.json()["role"], "manager")
self.assertTrue(
OrganizationUser.objects.filter(
organization=self.organization,
email=data["email"],
user=None,
role=OrganizationUserRole.MANAGER,
).exists()
)
self.assertTrue(team.members.exists())
self.assertEqual(len(mail.outbox), 1)
def test_organization_users_create_and_accept(self):
data = {
"email": "new@example.com",
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [],
}
self.client.post(self.members_url, data, content_type="application/json")
body = mail.outbox[0].body
body[body.find("http://localhost:8000/accept/") :].split("/")[4]
def test_organization_users_create_without_permissions(self):
"""Admin cannot add users to org"""
self.org_user.role = OrganizationUserRole.ADMIN
self.org_user.save()
data = {
"email": "new@example.com",
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [],
}
res = self.client.post(self.members_url, data, content_type="application/json")
self.assertEqual(res.status_code, 403)
def test_organization_users_reinvite(self):
other_user = baker.make("users.user")
baker.make(
"organizations_ext.OrganizationUser",
email=other_user.email,
organization=self.organization,
)
data = {
"email": other_user.email,
"orgRole": OrganizationUserRole.MANAGER.label.lower(),
"teamRoles": [],
}
res = self.client.post(self.members_url, data, content_type="application/json")
self.assertContains(res, other_user.email, status_code=201)
self.assertTrue(len(mail.outbox))
def test_organization_users_update(self):
other_user = baker.make("users.user")
other_org_user = self.organization.add_user(other_user)
url = self.get_org_member_detail_url(self.organization.slug, other_org_user.pk)
new_role = OrganizationUserRole.ADMIN
data = {"orgRole": new_role.label.lower(), "teamRoles": []}
res = self.client.put(url, data, content_type="application/json")
self.assertContains(res, other_user.email)
self.assertTrue(
OrganizationUser.objects.filter(
organization=self.organization, role=new_role, user=other_user
).exists()
)
def test_organization_users_update_without_permissions(self):
self.org_user.role = OrganizationUserRole.ADMIN
self.org_user.save()
other_user = baker.make("users.user")
other_org_user = self.organization.add_user(other_user)
url = self.get_org_member_detail_url(self.organization.slug, other_org_user.pk)
new_role = OrganizationUserRole.ADMIN
data = {"orgRole": new_role.label.lower(), "teamRoles": []}
res = self.client.put(url, data, content_type="application/json")
self.assertEqual(res.status_code, 404)
def test_organization_users_delete(self):
other_user = baker.make("users.user")
other_org_user = self.organization.add_user(other_user)
url = self.get_org_member_detail_url(self.organization.slug, other_org_user.pk)
res = self.client.delete(url)
self.assertEqual(res.status_code, 204)
self.assertEqual(other_user.organizations_ext_organizationuser.count(), 0)
url = self.get_org_member_detail_url(self.organization.slug, self.org_user.pk)
res = self.client.delete(url)
self.assertEqual(
res.status_code,
400,
"Org owner should not be able to remove themselves from org",
)
third_user = baker.make("users.user")
third_org_user = self.organization.add_user(third_user)
change_ownership_url = (
self.get_org_member_detail_url(self.organization.slug, third_org_user.pk)
+ "set_owner/"
)
self.client.post(change_ownership_url)
res = self.client.delete(url)
self.assertEqual(
res.status_code,
204,
"Can remove self after transferring ownership",
)
def test_organization_users_delete_without_permissions(self):
self.org_user.role = OrganizationUserRole.ADMIN
self.org_user.save()
other_user = baker.make("users.user")
other_org_user = self.organization.add_user(other_user)
url = self.get_org_member_detail_url(self.organization.slug, other_org_user.pk)
res = self.client.delete(url)
self.assertEqual(res.status_code, 404)
self.assertEqual(other_user.organizations_ext_organizationuser.count(), 1)
def test_organization_members_set_owner(self):
other_user = baker.make("users.user")
other_org_user = self.organization.add_user(other_user)
random_org_user = baker.make("organizations_ext.OrganizationUser")
url = reverse(
"api:set_organization_owner",
args=[self.organization.slug, random_org_user.pk],
)
res = self.client.post(url)
self.assertEqual(
res.status_code, 404, "Don't set random unrelated users as owner"
)
url = (
self.get_org_member_detail_url(self.organization.slug, other_org_user.pk)
+ "set_owner/"
)
res = self.client.post(url)
self.assertTrue(
res.json()["isOwner"], "Current owner may set another org member as owner"
)
url = reverse(
"api:set_organization_owner",
args=[self.organization.slug, self.org_user.pk],
)
url = (
self.get_org_member_detail_url(self.organization.slug, self.org_user.pk)
+ "set_owner/"
)
self.org_user.role = OrganizationUserRole.MANAGER
self.org_user.save()
res = self.client.post(url)
self.assertEqual(
res.status_code, 403, "Can't set self as owner with only manager role"
)
self.org_user.role = OrganizationUserRole.OWNER
self.org_user.save()
res = self.client.post(url)
self.assertTrue(res.json()["isOwner"], "Owner role may set org member as owner")
self.assertEqual(self.organization.owners.count(), 1)