/
oriono
/
taskman
Обзор
Документация
Войти
/
oriono
/
taskman
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
tests/test_users.py
944 строки
37 KB
abo
Fix tests for invite code based team joining
17 май 2026, 02:37
17 май 2026, 02:37
4223597
Код
Авторство
О чём код?
from task_manager.user.models import User from task_manager.teams.models import Team, TeamMembership from task_manager.statuses.models import Status from django.test import TestCase, Client from django.urls import reverse from django.contrib.messages import get_messages from django.utils.translation import gettext as _ from django.contrib.auth.hashers import check_password import uuid class UserTestCase(TestCase): fixtures = ["tests/fixtures/test_teams.json", "tests/fixtures/test_users.json", "tests/fixtures/test_teams_memberships.json", "tests/fixtures/test_statuses.json", "tests/fixtures/test_tasks.json", "tests/fixtures/test_labels.json"] def setUp(self): self.user = User.objects.get(username="he") self.c = Client() self.c.force_login(self.user) self.user_data = { 'username': 'new', 'password1': '22222222', 'password2': '22222222', } # Update UUIDs for fixtures that were loaded without them self._update_fixture_uuids() def _update_fixture_uuids(self): """Update UUIDs for fixture-loaded objects""" # Update teams team1 = Team.objects.filter(pk=1).first() team2 = Team.objects.filter(pk=2).first() if team1 and not hasattr(team1, '_uuid_set'): team1.uuid = uuid.UUID('550e8400-e29b-41d4-a716-446655440030') team1.save(update_fields=['uuid']) team1._uuid_set = True if team2 and not hasattr(team2, '_uuid_set'): team2.uuid = uuid.UUID('550e8400-e29b-41d4-a716-446655440031') team2.save(update_fields=['uuid']) team2._uuid_set = True # Update memberships memberships_data = [ (1, '550e8400-e29b-41d4-a716-446655440040'), (2, '550e8400-e29b-41d4-a716-446655440041'), (3, '550e8400-e29b-41d4-a716-446655440042'), ] for membership_pk, membership_uuid in memberships_data: membership = TeamMembership.objects.filter(pk=membership_pk).first() if membership: membership.uuid = uuid.UUID(membership_uuid) membership.save(update_fields=['uuid']) # list def test_user_list_response_200(self): response = self.c.get(reverse('user:user-list')) self.assertEqual(response.status_code, 200) def test_user_list_content(self): response = self.c.get(reverse('user:user-list')) self.assertContains(response, _('Users')) # Check that user card design is present self.assertContains(response, 'user-card') # Check that toolbar is present self.assertContains(response, 'toolbar-row') # Check that user counter is present self.assertContains(response, 'task-counter') # Check that reference book links are present self.assertContains(response, 'Tasks') self.assertContains(response, 'Statuses') self.assertContains(response, 'Labels') def test_user_list_unauthenticated(self): """test that unauthenticated users get empty queryset""" self.c.logout() response = self.c.get(reverse('user:user-list')) self.assertEqual(response.status_code, 200) # should return empty queryset for unauthenticated users self.assertQuerySetEqual(response.context['user_list'], []) # to do later - this test fails def test_user_list_with_team_context(self): """test user list with active team context""" # set active team in session session = self.c.session session['active_team_uuid'] = str(Team.objects.get(pk=1).uuid) session.save() response = self.c.get(reverse('user:user-list')) self.assertEqual(response.status_code, 200) # check that user_memberships context is present self.assertIn('user_memberships', response.context) self.assertIn('user_membership', response.context) # should show team users that current user can see # user 'he' (id=12) should see himself and possibly other team members team_users = response.context['user_list'] actual_user_ids = [user.id for user in team_users] # at minimum, current user should be in the list self.assertIn(self.user.id, actual_user_ids) # check that user_memberships contains memberships for team 1 user_memberships = response.context['user_memberships'] membership_user_ids = [m.user.id for m in user_memberships] expected_membership_user_ids = [10, 12] # from fixtures self.assertEqual( sorted(membership_user_ids), sorted(expected_membership_user_ids) ) def test_user_list_without_team_context(self): """test user list without active team""" # ensure no active team if 'active_team_uuid' in self.c.session: del self.c.session['active_team_uuid'] self.c.session.save() response = self.c.get(reverse('user:user-list')) self.assertEqual(response.status_code, 200) # should show only current user users = response.context['user_list'] self.assertEqual(len(users), 1) self.assertEqual(users[0].id, self.user.id) # check context is empty self.assertEqual(response.context['user_memberships'], []) self.assertIsNone(response.context['user_membership']) # to do later - this test fails def test_user_list_user_membership_context(self): """test that user membership context is correctly set""" session = self.c.session session['active_team_uuid'] = str(Team.objects.get(pk=1).uuid) session.save() response = self.c.get(reverse('user:user-list')) self.assertEqual(response.status_code, 200) # user 'he' (id=12) should have membership in team 1 user_membership = response.context['user_membership'] self.assertIsNotNone(user_membership) self.assertEqual(user_membership.user.id, self.user.id) self.assertEqual(user_membership.team.id, 1) self.assertEqual(user_membership.role, 'member') def test_user_list_with_active_team(self): """test user list shows team members when active""" session = self.c.session session['active_team_uuid'] = str(Team.objects.get(pk=1).uuid) session.save() response = self.c.get(reverse('user:user-list')) self.assertEqual(response.status_code, 200) # should show team members, not just current user users = response.context['user_list'] user_ids = [u.id for u in users] self.assertIn(self.user.id, user_ids) # membership context should be populated memberships = response.context['user_memberships'] self.assertGreater(len(memberships), 0) self.assertIsNotNone( response.context['user_membership'] ) # detail def test_user_detail_view_response_200(self): """test user detail view returns 200""" url = reverse('user:user-detail', args=[self.user.username]) response = self.c.get(url) self.assertEqual(response.status_code, 200) def test_user_detail_view_context(self): """test user detail view context data""" url = reverse('user:user-detail', args=[self.user.username]) response = self.c.get(url) self.assertEqual(response.status_code, 200) # check context object is correct self.assertEqual(response.context['object'].id, self.user.id) # check user_teams context is present self.assertIn('user_teams', response.context) # user 'he' should have one team membership user_teams = response.context['user_teams'] self.assertEqual(len(user_teams), 1) self.assertEqual(user_teams[0].team.id, 1) def test_user_detail_view_shows_description(self): """test user detail view shows description field""" # user 'me' has description in fixture user_with_desc = User.objects.get(username='me') response = self.c.get(reverse('user:user-detail', args=[user_with_desc.username])) self.assertEqual(response.status_code, 200) self.assertContains(response, _('Description')) self.assertContains(response, 'Team administrator and task creator') def test_user_detail_view_without_description(self): """test user detail view shows empty when no description""" user_without_desc = User.objects.get(username='he') response = self.c.get(reverse('user:user-detail', args=[user_without_desc.username])) self.assertEqual(response.status_code, 200) # Description label should still be present self.assertContains(response, _('Description')) # But no description text def test_user_detail_view_user_without_teams(self): """test user detail view for user without teams""" alone_user = User.objects.get(username='alone') response = self.c.get(reverse( 'user:user-detail', args=[alone_user.username])) self.assertEqual(response.status_code, 200) # should have empty user_teams user_teams = response.context['user_teams'] self.assertEqual(len(user_teams), 0) def test_user_detail_edit_delete_buttons_for_current_user(self): """test that Edit/Delete buttons are shown only for current user""" # User should see Edit/Delete buttons on their own detail page url = reverse('user:user-detail', args=[self.user.username]) response = self.c.get(url) self.assertEqual(response.status_code, 200) # Check that Edit and Delete buttons are present for current user self.assertContains(response, 'Edit') self.assertContains(response, 'Delete') def test_user_detail_no_edit_delete_buttons_for_other_user(self): """test that Edit/Delete buttons are not shown for other users""" # Test with another user from fixtures other_user = User.objects.get(username='me') url = reverse('user:user-detail', args=[other_user.username]) response = self.c.get(url) self.assertEqual(response.status_code, 200) # Check that Edit and Delete buttons are NOT present for other users # Check by URL instead of text to avoid false positives from navbar self.assertNotContains( response, reverse('user:user-update', args=[other_user.username])) self.assertNotContains( response, reverse('user:user-delete', args=[other_user.username])) def test_user_detail_admin_can_change_role(self): """test admin sees change role for team member""" admin_user = User.objects.get(username='me') self.c.force_login(admin_user) session = self.c.session session['active_team_uuid'] = str(Team.objects.get(pk=1).uuid) session.save() # view another team member's detail member = User.objects.get(username='he') response = self.c.get( reverse( 'user:user-detail', args=[member.username] ) ) self.assertEqual(response.status_code, 200) self.assertTrue( response.context['can_change_role'] ) self.assertIsNotNone( response.context['membership_uuid'] ) def test_user_detail_admin_views_non_member(self): """test admin views user who is not in active team""" admin_user = User.objects.get(username='me') self.c.force_login(admin_user) session = self.c.session session['active_team_uuid'] = str(Team.objects.get(pk=1).uuid) session.save() # view user who has no team membership alone = User.objects.get(username='alone') response = self.c.get( reverse( 'user:user-detail', args=[alone.username] ) ) self.assertEqual(response.status_code, 200) self.assertFalse( response.context['can_change_role'] ) self.assertIsNone( response.context['membership_uuid'] ) # create def test_create_user_page_content(self): response = self.c.get(reverse('user:user-create')) self.assertEqual(response.status_code, 200) self.assertContains(response, _('Username')) self.assertContains(response, _('Password')) self.assertContains(response, _('Confirm password')) # First name, last name, team join fields are no longer on create page self.assertNotContains(response, _('First name')) self.assertNotContains(response, _('Join team (optional)')) self.assertContains(response, _('Signup')) self.assertRegex( response.content.decode('utf-8'), _(r'\bSign up\b')) def test_create_user_successfully(self): old_count = User.objects.count() response = self.c.post(reverse('user:user-create'), self.user_data, follow=True) self.assertEqual(response.status_code, 200) new_count = User.objects.count() self.assertEqual(old_count + 1, new_count) user = User.objects.filter(username=self.user_data['username']).first() # first_name/last_name are set later via profile update self.assertEqual(user.first_name, '') self.assertEqual(user.last_name, '') # user without team should redirect to tasks list # Now redirects to index first, then to tasks-list self.assertRedirects( response, reverse('tasks:tasks-list'), status_code=302, target_status_code=200, fetch_redirect_response=True ) messages = list(get_messages(response.wsgi_request)) self.assertGreater(len(messages), 0) self.assertEqual( str(messages[0]), _('Welcome! You can create a team or work individually') ) def test_default_statuses_created_for_new_user_without_team(self): # count statuses before creating new user initial_status_count = Status.objects.count() initial_user_status_count = Status.objects.filter( creator=self.user).count() # create new user without team self.c.post( reverse('user:user-create'), self.user_data, follow=True ) # get the newly created user new_user = User.objects.get(username=self.user_data['username']) # check status counts self.assertEqual( Status.objects.count(), # 6 default statuses should be added from statuses/models.py initial_status_count + 6 ) self.assertEqual( Status.objects.filter(creator=new_user).count(), 6 ) # verify no statuses were created for the test user (self.user) self.assertEqual( Status.objects.filter(creator=self.user).count(), initial_user_status_count ) def test_user_auto_login_after_create(self): self.c.logout() self.c.post( reverse('user:user-create'), self.user_data, follow=True) # check if user has been auto login user = User.objects.filter(username=self.user_data['username']).first() self.assertEqual(int(self.c.session['_auth_user_id']), user.pk) def test_create_user_without_team_redirect(self): response = self.c.post( reverse('user:user-create'), self.user_data, follow=True) self.assertEqual(response.status_code, 200) user = User.objects.filter(username=self.user_data['username']).first() self.assertIsNotNone(user) # check user has no team membership self.assertFalse(TeamMembership.objects.filter(user=user).exists()) # Should redirect to tasks list after index redirect self.assertRedirects( response, reverse('tasks:tasks-list'), status_code=302, target_status_code=200, fetch_redirect_response=True ) def test_check_for_not_create_user_with_same_username(self): self.c.post( reverse('user:user-create'), self.user_data, follow=True) users_count = User.objects.count() response = self.c.post( reverse('user:user-create'), self.user_data, follow=True) new_users_count = User.objects.count() self.assertEqual(users_count, new_users_count) self.assertNotEqual(response.status_code, 302) message = _('A user with that username already exists.') self.assertContains(response, message) def test_can_not_create_user_with_empty_name(self): self.user_data['username'] = ' ' response = self.c.post(reverse('user:user-create'), self.user_data, follow=True) self.assertFalse(User.objects.filter(username=" ").exists()) message = _('This field is required.') self.assertContains(response, message) def test_create_user_password_do_not_match(self): user_data = self.user_data.copy() user_data['password2'] = 'different_password' response = self.c.post( reverse('user:user-create'), user_data, follow=True) # user does not created self.assertFalse(User.objects.filter( username=user_data['username']).exists()) # validation error message self.assertContains(response, _("The entered passwords do not match.")) def test_create_user_with_team_join_via_invite_redirect(self): """test that creating user with team invite redirects to tasks""" from django.utils import timezone from datetime import timedelta from task_manager.teams.models import TeamInvite team = Team.objects.get(pk=1) invite = TeamInvite.objects.create( team=team, created_by=User.objects.get(pk=10), max_uses=5, expires_at=timezone.now() + timedelta(days=30) ) user_data_with_invite = self.user_data.copy() user_data_with_invite['invite_code'] = str(invite.invite_code) response = self.c.post( reverse('user:user-create'), user_data_with_invite, follow=True) self.assertEqual(response.status_code, 200) # Should redirect to tasks list after index redirect self.assertRedirects( response, reverse('tasks:tasks-list'), status_code=302, target_status_code=200, fetch_redirect_response=True ) # check that membership was created with active status (via invite) new_user = User.objects.get(username=self.user_data['username']) membership = TeamMembership.objects.filter( user=new_user, team=team ).first() self.assertIsNotNone(membership) self.assertEqual(membership.status, 'active') def test_not_create_user_with_invalid_invite_code(self): """test creating user with invalid invite code still creates user""" from django.core.exceptions import ValidationError user_data_with_invite = self.user_data.copy() user_data_with_invite['invite_code'] = 'invalid-uuid-code' # Invalid UUID may raise ValidationError during form processing try: self.c.post( reverse('user:user-create'), user_data_with_invite, follow=True ) except ValidationError: pass # user should still be created even with invalid invite user = User.objects.filter( username=self.user_data['username']).first() self.assertIsNotNone(user) # should not have team membership self.assertFalse(TeamMembership.objects.filter(user=user).exists()) def test_not_create_user_with_used_invite_code(self): """test creating user with used invite code creates user without team""" from django.utils import timezone from datetime import timedelta from task_manager.teams.models import TeamInvite team = Team.objects.get(pk=1) invite = TeamInvite.objects.create( team=team, created_by=User.objects.get(pk=10), max_uses=5, expires_at=timezone.now() + timedelta(days=30), is_used=True, used_by=User.objects.get(pk=10) ) user_data_with_invite = self.user_data.copy() user_data_with_invite['invite_code'] = str(invite.invite_code) response = self.c.post( reverse('user:user-create'), user_data_with_invite, follow=True) # user should be created but without team membership self.assertEqual(response.status_code, 200) user = User.objects.filter( username=self.user_data['username']).first() self.assertIsNotNone(user) # should not have team membership self.assertFalse(TeamMembership.objects.filter(user=user).exists()) # update def test_update_user_status_200_and_check_content(self): response = self.c.get( reverse('user:user-update', args=[self.user.username]), follow=True) self.assertEqual(response.status_code, 200) self.assertContains(response, _('First name')) self.assertContains(response, _('Last name')) self.assertContains(response, _('Username')) self.assertContains(response, _('Password')) self.assertContains(response, _('Confirm password')) self.assertRegex( response.content.decode('utf-8'), _(r'\bEdit user\b')) self.assertRegex( response.content.decode('utf-8'), _(r'\bEdit\b')) self.assertContains(response, self.user.first_name) self.assertContains(response, self.user.last_name) self.assertContains(response, self.user.username) def test_update_user_successfully(self): update_data = { 'first_name': 'Updated', 'last_name': 'Name', 'username': 'updated_user', 'description': 'Updated description', 'password1': 'newpass123', 'password2': 'newpass123', 'join_team_name': '', 'join_team_password': '' } response = self.c.post( reverse('user:user-update', args=[self.user.username]), update_data, follow=True) self.assertEqual(response.status_code, 200) self.assertRedirects(response, reverse('user:user-list')) messages = list(get_messages(response.wsgi_request)) self.assertGreater(len(messages), 0) self.assertEqual(str(messages[0]), _('User updated successfully')) self.user.refresh_from_db() self.assertEqual(self.user.first_name, update_data['first_name']) self.assertEqual(self.user.last_name, update_data['last_name']) self.assertEqual(self.user.username, update_data['username']) self.assertEqual(self.user.description, update_data['description']) self.assertTrue( check_password(update_data['password1'], self.user.password)) def test_user_auto_login_after_update(self): self.c.post( reverse('user:user-update', args=[self.user.username]), self.user_data, follow=True) # check for user stay login self.assertEqual( str(self.c.session['_auth_user_id']), str(self.user.pk)) def test_check_can_not_update_user_if_same_user_exist(self): new_user_data = { 'first_name': 'New', 'last_name': 'N', 'username': 'me', # username 'me' exists in test_users.json 'password1': '22222222', 'password2': '22222222', 'join_team_name': '', 'join_team_password': '' } response = self.c.post( reverse('user:user-update', args=[self.user.username]), new_user_data, follow=True) message = _('A user with that username already exists.') self.assertContains(response, message) self.assertNotEqual(response.status_code, 302) def test_can_not_set_empty_name_when_update_user(self): new_user_data = { 'first_name': 'New', 'last_name': 'N', 'username': ' ', 'password1': '22222222', 'password2': '22222222', 'join_team_name': '', 'join_team_password': '' } response = self.c.post( reverse('user:user-update', args=[self.user.username]), new_user_data, follow=True) self.assertFalse(User.objects.filter(username=" ").exists()) message = _('This field is required.') self.assertContains(response, message) def test_update_user_password_mismatch(self): user_data = self.user_data.copy() user_data['password2'] = 'different_password' response = self.c.post( reverse('user:user-update', args=[self.user.username]), user_data, follow=True) # check for validation error message self.assertContains(response, _("The entered passwords do not match.")) self.assertNotEqual(response.status_code, 302) def test_update_user_with_team_join(self): """test updating user and joining a team""" # user 'he' is already a member of team pk=1, use team pk=2 instead team = Team.objects.get(pk=2) # user is not a member of this team update_data = self.user_data.copy() update_data.update({ 'join_team_name': team.name, 'join_team_password': ( 'pbkdf2_sha256$260000$abcdefghijklmnopqrstuvwxyz123456' ) }) response = self.c.post( reverse('user:user-update', args=[self.user.username]), update_data, follow=True) self.assertEqual(response.status_code, 200) self.assertRedirects(response, reverse('user:user-list')) # check user has pending membership in the team membership = TeamMembership.objects.filter( user=self.user, team=team).first() self.assertIsNotNone(membership) self.assertEqual(membership.status, 'pending') # check success message messages = list(get_messages(response.wsgi_request)) success_messages = [ str(msg) for msg in messages if 'request to join team' in str(msg).lower() ] self.assertEqual(len(success_messages), 1) def test_update_user_without_changing_password(self): """test updating user without changing password""" original_password = self.user.password update_data = { 'first_name': 'Updated', 'last_name': 'Name', 'username': 'updated_user', 'password1': '', 'password2': '', 'join_team_name': '', 'join_team_password': '' } response = self.c.post( reverse('user:user-update', args=[self.user.username]), update_data, follow=True) self.assertEqual(response.status_code, 200) self.user.refresh_from_db() # check fields updated self.assertEqual(self.user.first_name, 'Updated') self.assertEqual(self.user.last_name, 'Name') self.assertEqual(self.user.username, 'updated_user') # password should remain unchanged self.assertEqual(self.user.password, original_password) def test_cancel_button_on_user_update_page(self): """Test Cancel button exists and redirects to user detail.""" response = self.c.get( reverse('user:user-update', args=[self.user.username]), follow=True ) self.assertEqual(response.status_code, 200) # Check that Cancel button with link to user-detail exists user_detail_url = reverse('user:user-detail', args=[self.user.username]) self.assertContains(response, user_detail_url) # Check that Cancel button has correct text (translated) self.assertContains(response, _('Cancel')) # Test that clicking Cancel redirects to user detail page response = self.c.get(user_detail_url, follow=True) self.assertEqual(response.status_code, 200) # Should be on user detail page self.assertContains(response, self.user.username) response = self.c.get( reverse('user:user-update', args=[self.user.username]), follow=True ) self.assertEqual(response.status_code, 200) # Check that Cancel button with link to user-list exists self.assertContains(response, reverse('user:user-list')) # Check that Cancel button has correct text (translated) self.assertContains(response, _('Cancel')) # Test that clicking Cancel redirects to user list # Find the Cancel link and follow it cancel_url = reverse('user:user-list') response = self.c.get(cancel_url, follow=True) self.assertEqual(response.status_code, 200) # Should be on user list page self.assertContains(response, _('Users')) # delete def test_get_delete_user_response_200_and_check_content(self): self.c.post(reverse('user:user-create'), self.user_data, follow=True) new_user = User.objects.get(username="new") self.c.force_login(new_user) response = self.c.get( reverse('user:user-delete', args=[new_user.username]), follow=False) self.assertEqual(response.status_code, 200) self.assertContains(response, _('Delete user')) self.assertContains(response, _('Yes, delete')) self.assertContains(response, _('Are you sure you want to delete new?')) def test_delete_user_has_cancel_button(self): """Test that delete user page has a Cancel button.""" self.c.post(reverse('user:user-create'), self.user_data, follow=True) new_user = User.objects.get(username="new") self.c.force_login(new_user) response = self.c.get( reverse('user:user-delete', args=[new_user.username]), follow=True) # Check that Cancel button exists self.assertContains(response, _('Cancel')) def test_delete_user_cancel_button_redirects_to_referer(self): """Test that Cancel button redirects to HTTP_REFERER.""" self.c.post(reverse('user:user-create'), self.user_data, follow=True) new_user = User.objects.get(username="new") self.c.force_login(new_user) # Set HTTP_REFERER in request response = self.c.get( reverse('user:user-delete', args=[new_user.username]), HTTP_REFERER=reverse('user:user-list'), follow=True ) # Check that Cancel button is present self.assertContains(response, _('Cancel')) # The Cancel button href should contain the referer URL cancel_url = reverse('user:user-list') self.assertIn(cancel_url, response.content.decode('utf-8')) def test_delete_user_cancel_button_without_referer(self): """Test that Cancel button redirects to home when no referer.""" self.c.post(reverse('user:user-create'), self.user_data, follow=True) new_user = User.objects.get(username="new") self.c.force_login(new_user) # Request without HTTP_REFERER response = self.c.get( reverse('user:user-delete', args=[new_user.username]), follow=True ) # Check that Cancel button exists and points to home self.assertContains(response, _('Cancel')) # Button href should be '/' when no referer self.assertIn('href="/"', response.content.decode('utf-8')) def test_delete_user_successfully(self): self.c.post(reverse('user:user-create'), self.user_data, follow=True) user = User.objects.get(username="new") self.c.force_login(user) response = self.c.post(reverse('user:user-delete', args=[user.username]), follow=True) # User still exists because of soft delete, # but is inactive and has a new username user.refresh_from_db() self.assertTrue(user.is_deleted) self.assertFalse(user.is_active) self.assertNotEqual(user.username, "new") self.assertEqual(response.status_code, 200) self.assertRedirects(response, reverse('index')) messages = list(get_messages(response.wsgi_request)) self.assertGreater(len(messages), 0) self.assertEqual(str(messages[0]), _('User account has been deleted')) def test_can_not_delete_user_bound_with_task(self): # Soft delete is now allowed even if user is bound to task self.c.force_login(self.user) response = self.c.post(reverse('user:user-delete', args=[self.user.username]), follow=True) self.user.refresh_from_db() self.assertTrue(self.user.is_deleted) self.assertRedirects(response, reverse('index')) messages = list(get_messages(response.wsgi_request)) self.assertGreater(len(messages), 0) self.assertEqual(str(messages[0]), _('User account has been deleted')) def test_can_not_delete_user_being_team_admin(self): # Soft delete is now allowed: if it's the last admin, team is deleted # If there are other members, one is promoted # create new user self.c.post(reverse('user:user-create'), self.user_data, follow=True) new_user = User.objects.get(username="new") # create new team and make user admin team = Team.objects.create( name="New Test Team", description="Test team description", password="testpass123" ) TeamMembership.objects.create( user=new_user, team=team, role='admin' ) self.c.force_login(new_user) # try to delete new user response = self.c.post(reverse('user:user-delete', args=[new_user.username]), follow=True) # user should be soft deleted new_user.refresh_from_db() self.assertTrue(new_user.is_deleted) # check for redirect to index (since logout happens) self.assertRedirects(response, reverse('index')) # delete team after test (if it still exists) if Team.objects.filter(id=team.id).exists(): team.delete() def test_can_join_existing_team_via_invite(self): """test that new user can join team via invite code""" from django.utils import timezone from datetime import timedelta from task_manager.teams.models import TeamInvite team = Team.objects.get(pk=1) invite = TeamInvite.objects.create( team=team, created_by=User.objects.get(pk=10), max_uses=5, expires_at=timezone.now() + timedelta(days=30) ) new_user_data = { 'username': 'team_member', 'password1': '12345678', 'password2': '12345678', 'invite_code': str(invite.invite_code) } response = self.c.post(reverse('user:user-create'), new_user_data, follow=True) self.assertEqual(response.status_code, 200) user = User.objects.get(username='team_member') # check membership was created with active status membership = TeamMembership.objects.get(user=user, team=team) self.assertEqual(membership.role, 'member') self.assertEqual(membership.status, 'active') # users who join a team should not get default statuses self.assertEqual(Status.objects.filter(creator=user).count(), 0) def test_delete_user_with_tasks_as_author(self): """test user can be soft deleted even if they are author of tasks""" # user 'me' (id=10) is author of tasks author_user = User.objects.get(username='me') self.c.force_login(author_user) response = self.c.post(reverse( 'user:user-delete', args=[author_user.username]), follow=True) # should redirect to index after logout self.assertRedirects(response, reverse('index')) # user should be soft deleted author_user.refresh_from_db() self.assertTrue(author_user.is_deleted) def test_delete_user_with_tasks_as_executor(self): """test user can be soft deleted even if they are executor of tasks""" # user 'he' (id=12) is executor of tasks executor_user = User.objects.get(username='he') self.c.force_login(executor_user) response = self.c.post(reverse( 'user:user-delete', args=[executor_user.username]), follow=True) # should redirect to index after logout self.assertRedirects(response, reverse('index')) # user should be soft deleted executor_user.refresh_from_db() self.assertTrue(executor_user.is_deleted)