from datetime import timedelta import pytest from django.core.exceptions import PermissionDenied, ValidationError from django.utils import timezone from intent.orgs import services from intent.orgs.models import InviteStatus, OrgMembership, Role, validate_org_slug def test_create_org_makes_creator_owner(owner, org): assert org.memberships.get(user=owner).role == Role.OWNER def test_staff_cannot_own_org(make_user): with pytest.raises(ValidationError): services.create_org(slug="x", name="X", owner=make_user(is_staff=True)) def test_reserved_slug_rejected(): with pytest.raises(ValidationError): validate_org_slug("Admin") def test_invite_requires_owner(collaborator, make_user, org): with pytest.raises(PermissionDenied): services.invite(org, collaborator, user=make_user()) def test_reinvite_replaces_pending_invite(owner, make_user, org): invitee = make_user() first, _ = services.invite(org, owner, user=invitee) second, _ = services.invite(org, owner, user=invitee, role=Role.OWNER) first.refresh_from_db() assert first.status == InviteStatus.REVOKED assert second.status == InviteStatus.PENDING def test_cannot_invite_existing_member_or_staff(owner, collaborator, make_user, org): with pytest.raises(ValidationError): services.invite(org, owner, user=collaborator) with pytest.raises(ValidationError): services.invite(org, owner, email="COLE@example.com") with pytest.raises(ValidationError): services.invite(org, owner, user=make_user(is_staff=True)) def test_accept_creates_membership_with_invited_role(owner, make_user, org): invitee = make_user() invite, _ = services.invite(org, owner, user=invitee, role=Role.OWNER) services.accept_invite(invite, invitee) assert org.memberships.get(user=invitee).role == Role.OWNER invite.refresh_from_db() assert invite.status == InviteStatus.ACCEPTED assert invite.accepted_by == invitee def test_expired_invite_cannot_be_accepted(owner, make_user, org): invitee = make_user() invite, _ = services.invite(org, owner, user=invitee) invite.expires_at = timezone.now() - timedelta(seconds=1) invite.save() with pytest.raises(ValidationError): services.accept_invite(invite, invitee) def test_email_invite_is_found_by_verified_email(owner, make_user, org): invitee = make_user("ivy") invite, _ = services.invite(org, owner, email="Ivy@Example.com") assert list(services.open_invites_for(invitee)) == [invite] assert services.is_addressed_to(invite, invitee) assert not services.is_addressed_to(invite, make_user()) def test_last_owner_cannot_leave_or_be_demoted(owner, org): membership = org.memberships.get(user=owner) with pytest.raises(ValidationError): services.remove_member(membership, owner) with pytest.raises(ValidationError): services.update_membership( membership, owner, role=Role.COLLABORATOR, can_create_projects=False ) def test_owner_can_step_down_once_another_owner_exists(owner, collaborator, org): services.update_membership( org.memberships.get(user=collaborator), owner, role=Role.OWNER, can_create_projects=False ) services.remove_member(org.memberships.get(user=owner), owner) assert not org.memberships.filter(user=owner).exists() def test_member_can_leave_but_not_remove_others(owner, collaborator, make_user, org): other = make_user() OrgMembership.objects.create(org=org, user=other) with pytest.raises(PermissionDenied): services.remove_member(org.memberships.get(user=other), collaborator) services.remove_member(org.memberships.get(user=collaborator), collaborator) assert not org.memberships.filter(user=collaborator).exists()