diff --git a/taiga/permissions/service.py b/taiga/permissions/service.py index cc38ff13..75376229 100644 --- a/taiga/permissions/service.py +++ b/taiga/permissions/service.py @@ -39,14 +39,15 @@ def _get_object_project(obj): def is_project_owner(user, obj): + """ + The owner attribute of a project is just an historical reference + """ + if user.is_superuser: return True project = _get_object_project(obj) - if project and project.owner == user: - return True - membership = _get_user_project_membership(user, project) if membership and membership.is_owner: return True @@ -80,17 +81,14 @@ def get_user_project_permissions(user, project): members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS)) public_permissions = list(map(lambda perm: perm[0], USER_PERMISSIONS)) anon_permissions = list(map(lambda perm: perm[0], ANON_PERMISSIONS)) - elif project.owner == user: - owner_permissions = list(map(lambda perm: perm[0], OWNERS_PERMISSIONS)) - members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS)) - public_permissions = project.public_permissions if project.public_permissions is not None else [] - anon_permissions = project.anon_permissions if project.anon_permissions is not None else [] elif membership: if membership.is_owner: owner_permissions = list(map(lambda perm: perm[0], OWNERS_PERMISSIONS)) + members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS)) else: owner_permissions = [] - members_permissions = _get_membership_permissions(membership) + members_permissions = [] + members_permissions = members_permissions + _get_membership_permissions(membership) public_permissions = project.public_permissions if project.public_permissions is not None else [] anon_permissions = project.anon_permissions if project.anon_permissions is not None else [] elif user.is_authenticated(): diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 812ac6b0..ad41a173 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -158,7 +158,7 @@ class ProjectViewSet(ModelCrudViewSet): def leave(self, request, pk=None): project = self.get_object() self.check_permissions(request, 'leave', project) - services.remove_member(project, user=request.user) + services.remove_user_from_project(request.user, project) return Response(status=status.HTTP_200_OK) def pre_save(self, obj): @@ -241,6 +241,10 @@ class MembershipViewSet(ModelCrudViewSet): services.send_invitation(invitation=invitation) return Response(status=status.HTTP_204_NO_CONTENT) + def pre_delete(self, obj): + if not services.can_user_leave_project(obj.user, obj.project): + raise exc.BadRequest(_("At least one of the user must be an active admin")) + def pre_save(self, obj): if not obj.token: obj.token = str(uuid.uuid1()) diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index e8e476e8..126ac9c0 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -22,6 +22,7 @@ from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, from taiga.base import exceptions as exc from taiga.projects.models import Membership +from . import services class CanLeaveProject(PermissionComponent): def check_permissions(self, request, view, obj=None): @@ -29,14 +30,7 @@ class CanLeaveProject(PermissionComponent): return False try: - membership = Membership.objects.get(user=request.user, project=obj) - other_admin_memberships_count = Membership.objects\ - .exclude(id=membership.id)\ - .filter(project=obj, is_owner=True)\ - .count() - - # The project need at least one owner - if membership.is_owner and other_admin_memberships_count == 0: + if not services.can_user_leave_project(request.user, obj): raise exc.PermissionDenied(_("You can't leave the project if there are no more owners")) return True diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index cbd2f2b9..5dbbba4c 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -30,6 +30,7 @@ from taiga.users.validators import RoleExistsValidator from taiga.permissions.service import get_user_project_permissions, is_project_owner from . import models +from . import services from . validators import ProjectExistsValidator @@ -202,6 +203,17 @@ class MembershipSerializer(ModelSerializer): return attrs + def validate_is_owner(self, attrs, source): + is_owner = attrs[source] + project = attrs.get("project", None) + if project is None: + project = self.object.project + + if self.object and not services.project_has_valid_owners(project, exclude_user=self.object.user): + raise serializers.ValidationError(_("At least one of the user must be an active admin")) + + return attrs + class ProjectMembershipSerializer(ModelSerializer): role_name = serializers.CharField(source='role.name', required=False) diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index e4a98aa0..bf1aac00 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -34,7 +34,7 @@ from .stats import get_member_stats_for_project from .members import create_members_in_bulk from .members import get_members_from_bulk -from .members import remove_member +from .members import remove_user_from_project, project_has_valid_owners, can_user_leave_project from .invitations import send_invitation from .invitations import find_invited_user diff --git a/taiga/projects/services/members.py b/taiga/projects/services/members.py index 58f77670..f4efc5e9 100644 --- a/taiga/projects/services/members.py +++ b/taiga/projects/services/members.py @@ -32,5 +32,27 @@ def create_members_in_bulk(bulk_data, callback=None, precall=None, **additional_ return members -def remove_member(project, user): +def remove_user_from_project(user, project): models.Membership.objects.get(project=project, user=user).delete() + + +def project_has_valid_owners(project, exclude_user=None): + """ + Checks if the project has any owner membership with a user different than the specified + """ + owner_memberships = project.memberships.filter(is_owner=True, user__is_active=True) + if exclude_user: + owner_memberships = owner_memberships.exclude(user=exclude_user) + + return owner_memberships.count() > 0 + + +def can_user_leave_project(user, project): + membership = project.memberships.get(user=user) + if not membership.is_owner: + return True + + if not project_has_valid_owners(project, exclude_user=user): + return False + + return True diff --git a/taiga/users/api.py b/taiga/users/api.py index 56bee9e7..139c1f11 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -40,6 +40,7 @@ from taiga.base.api import ModelCrudViewSet from taiga.base.utils.slug import slugify_uniquely from taiga.projects.votes import services as votes_service from taiga.projects.serializers import StarredSerializer +from taiga.permissions.service import is_project_owner from . import models from . import serializers @@ -53,8 +54,8 @@ class MembersFilterBackend(BaseFilterBackend): if project_id: Project = apps.get_model('projects', 'Project') project = get_object_or_404(Project, pk=project_id) - if request.user.is_authenticated() and (project.memberships.filter(user=request.user).exists() or project.owner == request.user): - return queryset.filter(Q(memberships__project=project) | Q(id=project.owner.id)).distinct() + if request.user.is_authenticated() and project.memberships.filter(user=request.user).exists(): + return queryset.filter(memberships__project=project).distinct() else: raise exc.PermissionDenied(_("You don't have permisions to see this project users.")) diff --git a/tests/integration/resources_permissions/test_attachment_resources.py b/tests/integration/resources_permissions/test_attachment_resources.py index 2bdf2e2f..d198bd98 100644 --- a/tests/integration/resources_permissions/test_attachment_resources.py +++ b/tests/integration/resources_permissions/test_attachment_resources.py @@ -69,6 +69,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + return m @pytest.fixture diff --git a/tests/integration/resources_permissions/test_history_resources.py b/tests/integration/resources_permissions/test_history_resources.py index 7cf1950c..fe85eda2 100644 --- a/tests/integration/resources_permissions/test_history_resources.py +++ b/tests/integration/resources_permissions/test_history_resources.py @@ -56,10 +56,23 @@ def data(): user=m.project_member_with_perms, role__project=m.private_project2, role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory(project=m.private_project2, user=m.project_member_without_perms, role__project=m.private_project2, role__permissions=[]) + + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) return m @pytest.fixture diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index 2bf40ebf..0e32b5e0 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -64,6 +64,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_issue = f.IssueFactory(project=m.public_project, status__project=m.public_project, severity__project=m.public_project, diff --git a/tests/integration/resources_permissions/test_milestones_resources.py b/tests/integration/resources_permissions/test_milestones_resources.py index 76d8ff4e..ce6fb6de 100644 --- a/tests/integration/resources_permissions/test_milestones_resources.py +++ b/tests/integration/resources_permissions/test_milestones_resources.py @@ -64,6 +64,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_milestone = f.MilestoneFactory(project=m.public_project) m.private_milestone1 = f.MilestoneFactory(project=m.private_project1) m.private_milestone2 = f.MilestoneFactory(project=m.private_project2) diff --git a/tests/integration/resources_permissions/test_projects_choices_resources.py b/tests/integration/resources_permissions/test_projects_choices_resources.py index 8f52124b..ed0a6a77 100644 --- a/tests/integration/resources_permissions/test_projects_choices_resources.py +++ b/tests/integration/resources_permissions/test_projects_choices_resources.py @@ -44,6 +44,7 @@ def data(): email=m.project_member_with_perms.email, role__project=m.private_project1, role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory(project=m.private_project1, user=m.project_member_without_perms, email=m.project_member_without_perms.email, @@ -60,6 +61,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_points = f.PointsFactory(project=m.public_project) m.private_points1 = f.PointsFactory(project=m.private_project1) m.private_points2 = f.PointsFactory(project=m.private_project2) @@ -1427,31 +1440,31 @@ def test_membership_list(client, data): response = client.get(url) projects_data = json.loads(response.content.decode('utf-8')) - assert len(projects_data) == 3 + assert len(projects_data) == 5 assert response.status_code == 200 client.login(data.registered_user) response = client.get(url) projects_data = json.loads(response.content.decode('utf-8')) - assert len(projects_data) == 3 + assert len(projects_data) == 5 assert response.status_code == 200 client.login(data.project_member_without_perms) response = client.get(url) projects_data = json.loads(response.content.decode('utf-8')) - assert len(projects_data) == 3 + assert len(projects_data) == 5 assert response.status_code == 200 client.login(data.project_member_with_perms) response = client.get(url) projects_data = json.loads(response.content.decode('utf-8')) - assert len(projects_data) == 5 + assert len(projects_data) == 8 assert response.status_code == 200 client.login(data.project_owner) response = client.get(url) projects_data = json.loads(response.content.decode('utf-8')) - assert len(projects_data) == 5 + assert len(projects_data) == 8 assert response.status_code == 200 diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 58ef3eae..6518734b 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -53,6 +53,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + ContentType = apps.get_model("contenttypes", "ContentType") Project = apps.get_model("projects", "Project") diff --git a/tests/integration/resources_permissions/test_resolver_resources.py b/tests/integration/resources_permissions/test_resolver_resources.py index 096c61bb..009500ff 100644 --- a/tests/integration/resources_permissions/test_resolver_resources.py +++ b/tests/integration/resources_permissions/test_resolver_resources.py @@ -65,6 +65,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.view_only_membership = f.MembershipFactory(project=m.private_project2, user=m.other_user, role__project=m.private_project2, diff --git a/tests/integration/resources_permissions/test_search_resources.py b/tests/integration/resources_permissions/test_search_resources.py index 0867675e..ef35d050 100644 --- a/tests/integration/resources_permissions/test_search_resources.py +++ b/tests/integration/resources_permissions/test_search_resources.py @@ -62,6 +62,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_issue = f.IssueFactory(project=m.public_project, status__project=m.public_project, severity__project=m.public_project, diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index bc3f8768..680164a3 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -63,6 +63,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_task = f.TaskFactory(project=m.public_project, status__project=m.public_project, milestone__project=m.public_project, diff --git a/tests/integration/resources_permissions/test_timelines_resources.py b/tests/integration/resources_permissions/test_timelines_resources.py index c364b0a7..63948ff8 100644 --- a/tests/integration/resources_permissions/test_timelines_resources.py +++ b/tests/integration/resources_permissions/test_timelines_resources.py @@ -61,6 +61,17 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) return m diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index bf41066d..fe03d969 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -63,6 +63,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_points = f.PointsFactory(project=m.public_project) m.private_points1 = f.PointsFactory(project=m.private_project1) m.private_points2 = f.PointsFactory(project=m.private_project2) diff --git a/tests/integration/resources_permissions/test_wiki_resources.py b/tests/integration/resources_permissions/test_wiki_resources.py index 0691f775..800a6f4a 100644 --- a/tests/integration/resources_permissions/test_wiki_resources.py +++ b/tests/integration/resources_permissions/test_wiki_resources.py @@ -64,6 +64,18 @@ def data(): role__project=m.private_project2, role__permissions=[]) + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_owner=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_owner=True) + m.public_wiki_page = f.WikiPageFactory(project=m.public_project) m.private_wiki_page1 = f.WikiPageFactory(project=m.private_project1) m.private_wiki_page2 = f.WikiPageFactory(project=m.private_project2) diff --git a/tests/integration/test_attachments.py b/tests/integration/test_attachments.py index 2d8cc791..1aa1ef70 100644 --- a/tests/integration/test_attachments.py +++ b/tests/integration/test_attachments.py @@ -15,6 +15,7 @@ def test_create_user_story_attachment_without_file(client): Bug test "Don't create attachments without attached_file" """ us = f.UserStoryFactory.create() + membership = f.MembershipFactory(project=us.project, user=us.owner, is_owner=True) attachment_data = { "description": "test", "attached_file": None, @@ -31,6 +32,7 @@ def test_create_user_story_attachment_without_file(client): def test_create_attachment_on_wrong_project(client): issue1 = f.create_issue() issue2 = f.create_issue(owner=issue1.owner) + membership = f.MembershipFactory(project=issue1.project, user=issue1.owner, is_owner=True) assert issue1.owner == issue2.owner assert issue1.project.owner == issue2.project.owner diff --git a/tests/integration/test_github_hook.py b/tests/integration/test_github_hook.py index 168e44ef..bb902d15 100644 --- a/tests/integration/test_github_hook.py +++ b/tests/integration/test_github_hook.py @@ -399,6 +399,7 @@ def test_issues_event_bad_comment(client): def test_api_get_project_modules(client): project = f.create_project() + membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True) url = reverse("projects-modules", args=(project.id,)) @@ -413,6 +414,7 @@ def test_api_get_project_modules(client): def test_api_patch_project_modules(client): project = f.create_project() + membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True) url = reverse("projects-modules", args=(project.id,)) diff --git a/tests/integration/test_history.py b/tests/integration/test_history.py index 22b8cd7a..73334d7d 100644 --- a/tests/integration/test_history.py +++ b/tests/integration/test_history.py @@ -143,7 +143,7 @@ def test_issue_resource_history_test(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) role = f.RoleFactory.create(project=project) - member = f.MembershipFactory.create(project=project, user=user, role=role) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True) issue = f.IssueFactory.create(owner=user, project=project) mock_path = "taiga.projects.issues.api.IssueViewSet.pre_conditions_on_save" @@ -200,6 +200,7 @@ def test_take_hidden_snapshot(): def test_history_with_only_comment_shouldnot_be_hidden(client): project = f.create_project() us = f.create_userstory(project=project) + membership = f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) qs_all = HistoryEntry.objects.all() qs_hidden = qs_all.filter(is_hidden=True) @@ -209,11 +210,9 @@ def test_history_with_only_comment_shouldnot_be_hidden(client): url = reverse("userstories-detail", args=[us.pk]) data = json.dumps({"comment": "test comment", "version": us.version}) - print(url, data) client.login(project.owner) response = client.patch(url, data, content_type="application/json") assert response.status_code == 200, response.content assert qs_all.count() == 1 assert qs_hidden.count() == 0 - diff --git a/tests/integration/test_importer_api.py b/tests/integration/test_importer_api.py index 4502080b..00480c0d 100644 --- a/tests/integration/test_importer_api.py +++ b/tests/integration/test_importer_api.py @@ -168,6 +168,7 @@ def test_invalid_project_import_with_extra_data(client): def test_invalid_issue_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-issue", args=[project.pk]) @@ -179,6 +180,7 @@ def test_invalid_issue_import(client): def test_valid_user_story_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_us_status = f.UserStoryStatusFactory.create(project=project) project.save() client.login(user) @@ -199,6 +201,7 @@ def test_valid_user_story_import(client): def test_valid_issue_import_without_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_issue_type = f.IssueTypeFactory.create(project=project) project.default_issue_status = f.IssueStatusFactory.create(project=project) project.default_severity = f.SeverityFactory.create(project=project) @@ -220,6 +223,7 @@ def test_valid_issue_import_without_extra_data(client): def test_valid_issue_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_issue_type = f.IssueTypeFactory.create(project=project) project.default_issue_status = f.IssueStatusFactory.create(project=project) project.default_severity = f.SeverityFactory.create(project=project) @@ -252,6 +256,7 @@ def test_valid_issue_import_with_extra_data(client): def test_invalid_issue_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_issue_type = f.IssueTypeFactory.create(project=project) project.default_issue_status = f.IssueStatusFactory.create(project=project) project.default_severity = f.SeverityFactory.create(project=project) @@ -275,6 +280,7 @@ def test_invalid_issue_import_with_extra_data(client): def test_invalid_issue_import_with_bad_choices(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_issue_type = f.IssueTypeFactory.create(project=project) project.default_issue_status = f.IssueStatusFactory.create(project=project) project.default_severity = f.SeverityFactory.create(project=project) @@ -333,6 +339,7 @@ def test_invalid_issue_import_with_bad_choices(client): def test_invalid_us_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-us", args=[project.pk]) @@ -344,6 +351,7 @@ def test_invalid_us_import(client): def test_valid_us_import_without_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_us_status = f.UserStoryStatusFactory.create(project=project) project.save() client.login(user) @@ -362,6 +370,7 @@ def test_valid_us_import_without_extra_data(client): def test_valid_us_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_us_status = f.UserStoryStatusFactory.create(project=project) project.save() client.login(user) @@ -389,6 +398,7 @@ def test_valid_us_import_with_extra_data(client): def test_invalid_us_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_us_status = f.UserStoryStatusFactory.create(project=project) project.save() client.login(user) @@ -409,6 +419,7 @@ def test_invalid_us_import_with_extra_data(client): def test_invalid_us_import_with_bad_choices(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_us_status = f.UserStoryStatusFactory.create(project=project) project.save() client.login(user) @@ -428,6 +439,7 @@ def test_invalid_us_import_with_bad_choices(client): def test_invalid_task_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-task", args=[project.pk]) @@ -439,6 +451,7 @@ def test_invalid_task_import(client): def test_valid_task_import_without_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_task_status = f.TaskStatusFactory.create(project=project) project.save() client.login(user) @@ -457,6 +470,7 @@ def test_valid_task_import_without_extra_data(client): def test_valid_task_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_task_status = f.TaskStatusFactory.create(project=project) project.save() client.login(user) @@ -484,6 +498,7 @@ def test_valid_task_import_with_extra_data(client): def test_invalid_task_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_task_status = f.TaskStatusFactory.create(project=project) project.save() client.login(user) @@ -504,6 +519,7 @@ def test_invalid_task_import_with_extra_data(client): def test_invalid_task_import_with_bad_choices(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_task_status = f.TaskStatusFactory.create(project=project) project.save() client.login(user) @@ -523,6 +539,7 @@ def test_invalid_task_import_with_bad_choices(client): def test_valid_task_with_user_story(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) project.default_task_status = f.TaskStatusFactory.create(project=project) us = f.UserStoryFactory.create(project=project) project.save() @@ -542,6 +559,7 @@ def test_valid_task_with_user_story(client): def test_invalid_wiki_page_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-wiki-page", args=[project.pk]) @@ -553,6 +571,7 @@ def test_invalid_wiki_page_import(client): def test_valid_wiki_page_import_without_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-wiki-page", args=[project.pk]) @@ -568,6 +587,7 @@ def test_valid_wiki_page_import_without_extra_data(client): def test_valid_wiki_page_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-wiki-page", args=[project.pk]) @@ -592,6 +612,7 @@ def test_valid_wiki_page_import_with_extra_data(client): def test_invalid_wiki_page_import_with_extra_data(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-wiki-page", args=[project.pk]) @@ -610,6 +631,7 @@ def test_invalid_wiki_page_import_with_extra_data(client): def test_invalid_wiki_link_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-wiki-link", args=[project.pk]) @@ -621,6 +643,7 @@ def test_invalid_wiki_link_import(client): def test_valid_wiki_link_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-wiki-link", args=[project.pk]) @@ -636,6 +659,7 @@ def test_valid_wiki_link_import(client): def test_invalid_milestone_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-milestone", args=[project.pk]) @@ -647,6 +671,7 @@ def test_invalid_milestone_import(client): def test_valid_milestone_import(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-milestone", args=[project.pk]) @@ -663,6 +688,7 @@ def test_valid_milestone_import(client): def test_milestone_import_duplicated_milestone(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + membership = f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) url = reverse("importer-milestone", args=[project.pk]) diff --git a/tests/integration/test_issues.py b/tests/integration/test_issues.py index a40cd418..8f69c49d 100644 --- a/tests/integration/test_issues.py +++ b/tests/integration/test_issues.py @@ -46,6 +46,7 @@ def test_update_issues_order_in_bulk(): def test_api_create_issues_in_bulk(client): project = f.create_project() + membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True) url = reverse("issues-bulk-create") diff --git a/tests/integration/test_memberships.py b/tests/integration/test_memberships.py index c77aff75..f3703829 100644 --- a/tests/integration/test_memberships.py +++ b/tests/integration/test_memberships.py @@ -34,6 +34,7 @@ def test_api_create_bulk_members(client): joseph = f.UserFactory.create() tester = f.RoleFactory(project=project, name="Tester") gamer = f.RoleFactory(project=project, name="Gamer") + membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True) url = reverse("memberships-bulk-create") @@ -54,6 +55,7 @@ def test_api_create_bulk_members(client): def test_api_create_bulk_members_with_extra_text(client, outbox): project = f.ProjectFactory() tester = f.RoleFactory(project=project, name="Tester") + membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True) url = reverse("memberships-bulk-create") invitation_extra_text = "this is a not so random invitation text" @@ -77,6 +79,7 @@ def test_api_create_bulk_members_with_extra_text(client, outbox): def test_api_resend_invitation(client, outbox): invitation = f.create_invitation() + membership = f.MembershipFactory(project=invitation.project, user=invitation.project.owner, is_owner=True) url = reverse("memberships-resend-invitation", kwargs={"pk": invitation.pk}) client.login(invitation.project.owner) @@ -91,6 +94,7 @@ def test_api_invite_existing_user(client, outbox): "Should create the invitation linked to that user" user = f.UserFactory.create() role = f.RoleFactory.create() + membership = f.MembershipFactory(project=role.project, user=role.project.owner, is_owner=True) client.login(role.project.owner) diff --git a/tests/integration/test_milestones.py b/tests/integration/test_milestones.py index e32f23ac..ab980e35 100644 --- a/tests/integration/test_milestones.py +++ b/tests/integration/test_milestones.py @@ -33,9 +33,8 @@ def test_update_milestone_with_userstories_list(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) role = f.RoleFactory.create(project=project) - member = f.MembershipFactory.create(project=project, user=user, role=role) + member = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True) sprint = f.MilestoneFactory.create(project=project, owner=user) - points = f.PointsFactory.create(project=project, value=None) us = f.UserStoryFactory.create(project=project, owner=user) @@ -49,4 +48,3 @@ def test_update_milestone_with_userstories_list(client): client.login(user) response = client.json.patch(url, json.dumps(form_data)) assert response.status_code == 200 - diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index da0e6bd4..682c54a1 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -230,7 +230,7 @@ def test_resource_notification_test(client, mail): user2 = f.UserFactory.create() project = f.ProjectFactory.create(owner=user1) role = f.RoleFactory.create(project=project) - member1 = f.MembershipFactory.create(project=project, user=user1, role=role) + member1 = f.MembershipFactory.create(project=project, user=user1, role=role, is_owner=True) member2 = f.MembershipFactory.create(project=project, user=user2, role=role) issue = f.IssueFactory.create(owner=user2, project=project) @@ -268,7 +268,7 @@ def test_watchers_assignation_for_issue(client): project2 = f.ProjectFactory.create(owner=user2) role1 = f.RoleFactory.create(project=project1) role2 = f.RoleFactory.create(project=project2) - member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1) + member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True) member2 = f.MembershipFactory.create(project=project2, user=user2, role=role2) client.login(user1) @@ -321,7 +321,7 @@ def test_watchers_assignation_for_task(client): project2 = f.ProjectFactory.create(owner=user2) role1 = f.RoleFactory.create(project=project1) role2 = f.RoleFactory.create(project=project2) - member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1) + member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True) member2 = f.MembershipFactory.create(project=project2, user=user2, role=role2) client.login(user1) @@ -374,7 +374,7 @@ def test_watchers_assignation_for_us(client): project2 = f.ProjectFactory.create(owner=user2) role1 = f.RoleFactory.create(project=project1) role2 = f.RoleFactory.create(project=project2) - member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1) + member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True) member2 = f.MembershipFactory.create(project=project2, user=user2, role=role2) client.login(user1) diff --git a/tests/integration/test_occ.py b/tests/integration/test_occ.py index eb25eef8..88597b12 100644 --- a/tests/integration/test_occ.py +++ b/tests/integration/test_occ.py @@ -33,7 +33,7 @@ pytestmark = pytest.mark.django_db def test_invalid_concurrent_save_for_issue(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) issue = f.IssueFactory.create(version=10, project=project) client.login(user) @@ -48,7 +48,7 @@ def test_invalid_concurrent_save_for_issue(client): def test_valid_concurrent_save_for_issue(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) issue = f.IssueFactory.create(version=10, project=project) client.login(user) @@ -66,7 +66,7 @@ def test_valid_concurrent_save_for_issue(client): def test_invalid_concurrent_save_for_wiki_page(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) wiki_page = f.WikiPageFactory.create(version=10, project=project, owner=user) client.login(user) @@ -78,7 +78,7 @@ def test_invalid_concurrent_save_for_wiki_page(client): def test_valid_concurrent_save_for_wiki_page(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) wiki_page = f.WikiPageFactory.create(version=10, project=project, owner=user) client.login(user) @@ -93,7 +93,7 @@ def test_valid_concurrent_save_for_wiki_page(client): def test_invalid_concurrent_save_for_us(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) userstory = f.UserStoryFactory.create(version=10, project=project) client.login(user) @@ -105,7 +105,7 @@ def test_invalid_concurrent_save_for_us(client): def test_valid_us_creation(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) client.login(user) @@ -121,7 +121,7 @@ def test_valid_us_creation(client): def test_valid_concurrent_save_for_us(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) userstory = f.UserStoryFactory.create(version=10, project=project) client.login(user) @@ -136,7 +136,7 @@ def test_valid_concurrent_save_for_us(client): def test_invalid_concurrent_save_for_task(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) task = f.TaskFactory.create(version=10, project=project) client.login(user) @@ -150,7 +150,7 @@ def test_invalid_concurrent_save_for_task(client): def test_valid_concurrent_save_for_task(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) - membership = f.MembershipFactory.create(project=project, user=user) + membership = f.MembershipFactory.create(project=project, user=user, is_owner=True) task = f.TaskFactory.create(version=10, project=project) client.login(user) diff --git a/tests/integration/test_permissions.py b/tests/integration/test_permissions.py index 4e337e43..aabfc34a 100644 --- a/tests/integration/test_permissions.py +++ b/tests/integration/test_permissions.py @@ -53,9 +53,7 @@ def test_owner_get_user_project_permissions(): factories.MembershipFactory(user=user1, project=project, role=role) expected_perms = set( - ["test1", "test2"] + - [x[0] for x in permissions.OWNERS_PERMISSIONS] + - [x[0] for x in permissions.MEMBERS_PERMISSIONS] + ["test1", "test2", "view_us"] ) assert service.get_user_project_permissions(user1, project) == expected_perms @@ -70,7 +68,8 @@ def test_owner_member_get_user_project_permissions(): expected_perms = set( ["test1", "test2", "test3"] + - [x[0] for x in permissions.OWNERS_PERMISSIONS] + [x[0] for x in permissions.OWNERS_PERMISSIONS] + + [x[0] for x in permissions.MEMBERS_PERMISSIONS] ) assert service.get_user_project_permissions(user1, project) == expected_perms diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index 26607bf5..ad4ad86e 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -22,6 +22,7 @@ def test_create_project(client): def test_partially_update_project(client): project = f.create_project() + f.MembershipFactory(user=project.owner, project=project, is_owner=True) url = reverse("projects-detail", kwargs={"pk": project.pk}) data = {"name": ""} @@ -32,6 +33,7 @@ def test_partially_update_project(client): def test_us_status_slug_generation(client): us_status = f.UserStoryStatusFactory(name="NEW") + f.MembershipFactory(user=us_status.project.owner, project=us_status.project, is_owner=True) assert us_status.slug == "new" client.login(us_status.project.owner) @@ -51,6 +53,7 @@ def test_us_status_slug_generation(client): def test_task_status_slug_generation(client): task_status = f.TaskStatusFactory(name="NEW") + f.MembershipFactory(user=task_status.project.owner, project=task_status.project, is_owner=True) assert task_status.slug == "new" client.login(task_status.project.owner) @@ -70,6 +73,7 @@ def test_task_status_slug_generation(client): def test_issue_status_slug_generation(client): issue_status = f.IssueStatusFactory(name="NEW") + f.MembershipFactory(user=issue_status.project.owner, project=issue_status.project, is_owner=True) assert issue_status.slug == "new" client.login(issue_status.project.owner) @@ -90,7 +94,7 @@ def test_issue_status_slug_generation(client): def test_points_name_duplicated(client): point_1 = f.PointsFactory() point_2 = f.PointsFactory(project=point_1.project) - + f.MembershipFactory(user=point_1.project.owner, project=point_1.project, is_owner=True) client.login(point_1.project.owner) url = reverse("points-detail", kwargs={"pk": point_2.pk}) @@ -192,3 +196,30 @@ def test_leave_project_invalid_membership(client): url = reverse("projects-leave", args=(project.id,)) response = client.post(url) assert response.status_code == 404 + + +def test_delete_membership_only_owner(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True) + client.login(user) + url = reverse("memberships-detail", args=(membership.id,)) + response = client.delete(url) + assert response.status_code == 400 + assert json.loads(response.content)["_error_message"] == "At least one of the user must be an active admin" + + +def test_edit_membership_only_owner(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True) + data = { + "is_owner": False + } + client.login(user) + url = reverse("memberships-detail", args=(membership.id,)) + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 400 + assert response.data["is_owner"][0] == "At least one of the user must be an active admin" diff --git a/tests/integration/test_roles.py b/tests/integration/test_roles.py index e8bd89fa..923da195 100644 --- a/tests/integration/test_roles.py +++ b/tests/integration/test_roles.py @@ -39,7 +39,7 @@ def test_destroy_role_and_reassign_members(client): project = f.ProjectFactory.create(owner=user1) role1 = f.RoleFactory.create(project=project) role2 = f.RoleFactory.create(project=project) - member = f.MembershipFactory.create(project=project, user=user1, role=role1) + member = f.MembershipFactory.create(project=project, user=user1, role=role1, is_owner=True) member = f.MembershipFactory.create(project=project, user=user2, role=role2) url = reverse("roles-detail", args=[role2.pk]) + "?moveTo={}".format(role1.pk) diff --git a/tests/integration/test_stars.py b/tests/integration/test_stars.py index 27293eb1..779ef238 100644 --- a/tests/integration/test_stars.py +++ b/tests/integration/test_stars.py @@ -74,6 +74,7 @@ def test_project_member_unstar_project(client): def test_list_project_fans(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) fan = f.VoteFactory.create(content_object=project) url = reverse("projects-fans", args=(project.id,)) @@ -100,6 +101,7 @@ def test_list_user_starred_projects(client): def test_get_project_stars(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) url = reverse("projects-detail", args=(project.id,)) f.VotesFactory.create(content_object=project, count=5) f.VotesFactory.create(count=3) diff --git a/tests/integration/test_tasks.py b/tests/integration/test_tasks.py index 105541ac..d18bd1a8 100644 --- a/tests/integration/test_tasks.py +++ b/tests/integration/test_tasks.py @@ -36,6 +36,7 @@ Task #2 def test_api_update_task_tags(client): task = f.create_task() + f.MembershipFactory.create(project=task.project, user=task.owner, is_owner=True) url = reverse("tasks-detail", kwargs={"pk": task.pk}) data = {"tags": ["back", "front"], "version": task.version} @@ -47,6 +48,7 @@ def test_api_update_task_tags(client): def test_api_create_in_bulk_with_status(client): us = f.create_userstory() + f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) us.project.default_task_status = f.TaskStatusFactory.create(project=us.project) url = reverse("tasks-bulk-create") data = { @@ -69,6 +71,7 @@ def test_api_create_invalid_task(client): # But the User Story is not associated with the milestone us_milestone = f.MilestoneFactory.create() us = f.create_userstory(milestone=us_milestone) + f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) us.project.default_task_status = f.TaskStatusFactory.create(project=us.project) task_milestone = f.MilestoneFactory.create(project=us.project, owner=us.owner) diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 83c35c65..d19555b8 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -44,6 +44,7 @@ def test_update_userstories_order_in_bulk(): def test_api_delete_userstory(client): us = f.create_userstory() + f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) url = reverse("userstories-detail", kwargs={"pk": us.pk}) client.login(us.owner) @@ -67,6 +68,7 @@ def test_api_filter_by_subject(client): def test_api_create_in_bulk_with_status(client): project = f.create_project() + f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) url = reverse("userstories-bulk-create") data = { "bulk_stories": "Story #1\nStory #2", @@ -83,6 +85,7 @@ def test_api_create_in_bulk_with_status(client): def test_api_update_backlog_order_in_bulk(client): project = f.create_project() + f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) us1 = f.create_userstory(project=project) us2 = f.create_userstory(project=project) @@ -102,9 +105,9 @@ def test_api_update_backlog_order_in_bulk(client): response2 = client.json.post(url2, json.dumps(data)) response3 = client.json.post(url3, json.dumps(data)) - assert response1.status_code == 204, response.data - assert response2.status_code == 204, response.data - assert response3.status_code == 204, response.data + assert response1.status_code == 204, response1.data + assert response2.status_code == 204, response2.data + assert response3.status_code == 204, response3.data from taiga.projects.userstories.serializers import UserStorySerializer @@ -118,7 +121,7 @@ def test_update_userstory_points(client): role1 = f.RoleFactory.create(project=project) role2 = f.RoleFactory.create(project=project) - member = f.MembershipFactory.create(project=project, user=user1, role=role1) + member = f.MembershipFactory.create(project=project, user=user1, role=role1, is_owner=True) member = f.MembershipFactory.create(project=project, user=user2, role=role2) points1 = f.PointsFactory.create(project=project, value=None) diff --git a/tests/integration/test_vote_issues.py b/tests/integration/test_vote_issues.py index c1659e6a..390dfcaa 100644 --- a/tests/integration/test_vote_issues.py +++ b/tests/integration/test_vote_issues.py @@ -27,6 +27,7 @@ pytestmark = pytest.mark.django_db def test_upvote_issue(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) url = reverse("issues-upvote", args=(issue.id,)) client.login(user) @@ -38,6 +39,7 @@ def test_upvote_issue(client): def test_downvote_issue(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) url = reverse("issues-downvote", args=(issue.id,)) client.login(user) @@ -49,6 +51,7 @@ def test_downvote_issue(client): def test_list_issue_voters(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) url = reverse("issue-voters-list", args=(issue.id,)) f.VoteFactory.create(content_object=issue, user=user) @@ -62,6 +65,7 @@ def test_list_issue_voters(client): def test_get_issue_voter(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) vote = f.VoteFactory.create(content_object=issue, user=user) url = reverse("issue-voters-detail", args=(issue.id, vote.user.id)) @@ -75,6 +79,7 @@ def test_get_issue_voter(client): def test_get_issue_votes(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) url = reverse("issues-detail", args=(issue.id,)) f.VotesFactory.create(content_object=issue, count=5)