Skip to content

Commit b6bd7a6

Browse files
committed
Merge branch 'pr/4577'
2 parents 194c92a + c29636e commit b6bd7a6

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

readthedocs/core/signals.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
13
"""Signal handling for core app."""
24

35
from __future__ import absolute_import
@@ -12,6 +14,7 @@
1214
from django.dispatch import receiver
1315
from future.backports.urllib.parse import urlparse
1416

17+
from readthedocs.oauth.models import RemoteOrganization
1518
from readthedocs.projects.models import Project, Domain
1619

1720
log = logging.getLogger(__name__)
@@ -23,7 +26,6 @@
2326
'/api/v2/sustainability',
2427
]
2528

26-
2729
webhook_github = Signal(providing_args=['project', 'data', 'event'])
2830
webhook_gitlab = Signal(providing_args=['project', 'data', 'event'])
2931
webhook_bitbucket = Signal(providing_args=['project', 'data', 'event'])
@@ -79,12 +81,20 @@ def decide_if_cors(sender, request, **kwargs): # pylint: disable=unused-argumen
7981
def delete_projects_and_organizations(sender, instance, *args, **kwargs):
8082
# Here we count the owner list from the projects that the user own
8183
# Then exclude the projects where there are more than one owner
82-
projects = instance.projects.all().annotate(num_users=Count('users')).exclude(num_users__gt=1)
84+
# Add annotate before filter
85+
# https://github.com/rtfd/readthedocs.org/pull/4577
86+
# https://docs.djangoproject.com/en/2.1/topics/db/aggregation/#order-of-annotate-and-filter-clauses # noqa
87+
projects = (
88+
Project.objects.annotate(num_users=Count('users'))
89+
.filter(users=instance.id).exclude(num_users__gt=1)
90+
)
8391

8492
# Here we count the users list from the organization that the user belong
8593
# Then exclude the organizations where there are more than one user
86-
oauth_organizations = (instance.oauth_organizations.annotate(num_users=Count('users'))
87-
.exclude(num_users__gt=1))
94+
oauth_organizations = (
95+
RemoteOrganization.objects.annotate(num_users=Count('users'))
96+
.filter(users=instance.id).exclude(num_users__gt=1)
97+
)
8898

8999
projects.delete()
90100
oauth_organizations.delete()

readthedocs/core/tests/__init__.py

Whitespace-only changes.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import pytest
2+
import django_dynamic_fixture
3+
4+
from django.contrib.auth.models import User
5+
6+
from readthedocs.oauth.models import RemoteOrganization
7+
from readthedocs.projects.models import Project
8+
9+
10+
@pytest.mark.django_db
11+
class TestProjectOrganizationSignal(object):
12+
13+
@pytest.mark.parametrize('model_class', [Project, RemoteOrganization])
14+
def test_project_organization_get_deleted_upon_user_delete(self, model_class):
15+
"""
16+
If the user has Project or RemoteOrganization where he is the only user,
17+
upon deleting his account, the Project or RemoteOrganization should also get
18+
deleted.
19+
"""
20+
21+
obj = django_dynamic_fixture.get(model_class)
22+
user1 = django_dynamic_fixture.get(User)
23+
obj.users.add(user1)
24+
25+
obj.refresh_from_db()
26+
assert obj.users.all().count() == 1
27+
28+
# Delete the user
29+
user1.delete()
30+
# The object should not exist
31+
obj = model_class.objects.all().filter(id=obj.id)
32+
assert not obj.exists()
33+
34+
@pytest.mark.parametrize('model_class', [Project, RemoteOrganization])
35+
def test_multiple_users_project_organization_not_delete(self, model_class):
36+
"""
37+
Check Project or RemoteOrganization which have multiple users do not get deleted
38+
when any of the user delete his account.
39+
"""
40+
41+
obj = django_dynamic_fixture.get(model_class)
42+
user1 = django_dynamic_fixture.get(User)
43+
user2 = django_dynamic_fixture.get(User)
44+
obj.users.add(user1, user2)
45+
46+
obj.refresh_from_db()
47+
assert obj.users.all().count() > 1
48+
# Delete 1 user of the project
49+
user1.delete()
50+
51+
# The project should still exist and it should have 1 user
52+
obj.refresh_from_db()
53+
assert obj.id
54+
obj_users = obj.users.all()
55+
assert len(obj_users) == 1
56+
assert user2 in obj_users

0 commit comments

Comments
 (0)