def test_user_stats(self): self.maxDiff = None def update_member_activity(user, **kwargs): GroupMembership.objects.filter(user=user).update( lastseen_at=timezone.now() - relativedelta(**kwargs)) def do_pickup(place, user, **kwargs): pickup = PickupDateFactory(place=place, date=to_range(timezone.now() - relativedelta(**kwargs))) pickup.add_collector(user) # 9 verified users, 1 unverified user users = [VerifiedUserFactory() for _ in range(9)] users.insert(0, UserFactory()) # 5 some users of varying levels of inactivity inactive_users = [VerifiedUserFactory() for _ in range(5)] inactive_group = GroupFactory(members=inactive_users) GroupMembership.objects.filter(group=inactive_group).update( inactive_at=timezone.now()) update_member_activity(inactive_users[0], days=2) update_member_activity(inactive_users[1], days=8) update_member_activity(inactive_users[2], days=31) update_member_activity(inactive_users[3], days=61) update_member_activity(inactive_users[4], days=91) # 1 deleted user deleted_user = UserFactory() deleted_user.deleted = True deleted_user.save() # one user with bounced email bounce_user = users[0] for _ in range(5): EmailEvent.objects.create(created_at=timezone.now(), address=bounce_user.email, event='bounce', payload={}) # one user with location location_user = users[1] location_user.latitude = 50.4 location_user.longitude = 23.2 location_user.save() # one user with mobile number mobile_number_user = users[2] mobile_number_user.mobile_number = '123' mobile_number_user.save() # five users without description def remove_description(user): user.description = '' user.save() [remove_description(u) for u in users[:5]] # one user with photo photo_user = users[4] photo_user.photo = 'photo.jpg' photo_user.save() # 2 groups where everybody is active, 1 where everybody is inactive group = GroupFactory(members=users[:9]) GroupFactory(members=users[:9]) group_all_inactive = GroupFactory(members=users[:9]) GroupMembership.objects.filter(group=group_all_inactive).update( inactive_at=timezone.now()) # do some pickups! place = PlaceFactory(group=group) do_pickup(place, users[0], days=2) do_pickup(place, users[1], days=8) do_pickup(place, users[2], days=31) do_pickup(place, users[3], days=61) do_pickup(place, users[4], days=91) points = stats.get_users_stats() self.assertEqual( points, { 'active_count': 9, 'active_unverified_count': 1, 'active_ignored_email_count': 1, 'active_with_location_count': 1, 'active_with_mobile_number_count': 1, 'active_with_description_count': 4, 'active_with_photo_count': 1, 'active_memberships_per_active_user_avg': 2.0, 'no_membership_count': 1, 'deleted_count': 1, 'count_active_1d': 9, 'count_active_7d': 10, 'count_active_30d': 11, 'count_active_60d': 12, 'count_active_90d': 13, 'count_pickup_active_1d': 0, 'count_pickup_active_7d': 1, 'count_pickup_active_30d': 2, 'count_pickup_active_60d': 3, 'count_pickup_active_90d': 4, })
class UserReceiverTest(WSTestCase): def setUp(self): super().setUp() self.member = UserFactory() self.other_member = UserFactory() self.unrelated_user = UserFactory() self.group = GroupFactory(members=[self.member, self.other_member]) pathlib.Path(settings.MEDIA_ROOT).mkdir(exist_ok=True) copyfile(image_path, os.path.join(settings.MEDIA_ROOT, 'photo.jpg')) self.member.photo = 'photo.jpg' self.member.save() self.other_member.photo = 'photo.jpg' self.other_member.save() def test_receive_own_user_changes(self): client = self.connect_as(self.member) name = faker.name() self.member.display_name = name self.member.save() response = client.messages_by_topic.get('auth:user')[0] self.assertEqual(response['payload']['display_name'], name) self.assertTrue('current_group' in response['payload']) self.assertTrue( response['payload']['photo_urls']['full_size'].startswith( settings.HOSTNAME)) self.assertEqual(len(client.messages), 1) def test_receive_changes_of_other_user(self): client = self.connect_as(self.member) name = faker.name() self.other_member.display_name = name self.other_member.save() response = client.messages_by_topic.get('users:user')[0] self.assertEqual(response['payload']['display_name'], name) self.assertTrue('current_group' not in response['payload']) self.assertTrue( response['payload']['photo_urls']['full_size'].startswith( settings.HOSTNAME)) self.assertEqual(len(client.messages), 1) def test_do_not_send_too_many_updates(self): [ GroupFactory(members=[self.member, self.other_member]) for _ in range(3) ] client = self.connect_as(self.member) name = faker.name() self.other_member.display_name = name self.other_member.save() self.assertEqual(len(client.messages), 1) self.assertIn('users:user', client.messages_by_topic.keys()) def test_unrelated_user_receives_no_changes(self): client = self.connect_as(self.unrelated_user) self.member.display_name = faker.name() self.member.save() self.assertEqual(len(client.messages), 0)
def test_user_stats(self): self.maxDiff = None # avoid test flakyness: remove leftover users from other tests get_user_model().objects.all().delete() def update_member_activity(user, **kwargs): GroupMembership.objects.filter(user=user).update(lastseen_at=timezone.now() - relativedelta(**kwargs)) def do_activity(place, user, **kwargs): activity = ActivityFactory(place=place, date=to_range(timezone.now() - relativedelta(**kwargs))) activity.add_participant(user) # 9 verified users, 1 unverified user users = [VerifiedUserFactory() for _ in range(9)] users.insert(0, UserFactory()) # 5 some users of varying levels of inactivity inactive_users = [VerifiedUserFactory() for _ in range(5)] inactive_group = GroupFactory(members=inactive_users) GroupMembership.objects.filter(group=inactive_group).update(inactive_at=timezone.now()) update_member_activity(inactive_users[0], days=2) update_member_activity(inactive_users[1], days=8) update_member_activity(inactive_users[2], days=31) update_member_activity(inactive_users[3], days=61) update_member_activity(inactive_users[4], days=91) # 1 deleted user deleted_user = UserFactory() deleted_user.deleted = True deleted_user.save() # one user with location location_user = users[1] location_user.latitude = 50.4 location_user.longitude = 23.2 location_user.save() # one user with mobile number mobile_number_user = users[2] mobile_number_user.mobile_number = '123' mobile_number_user.save() # five users without description def remove_description(user): user.description = '' user.save() [remove_description(u) for u in users[:5]] # one user with photo photo_user = users[4] photo_user.photo = 'photo.jpg' photo_user.save() # 2 groups where everybody is active, 1 where everybody is inactive group = GroupFactory(members=users[:9]) GroupFactory(members=users[:9]) group_all_inactive = GroupFactory(members=users[:9]) GroupMembership.objects.filter(group=group_all_inactive).update(inactive_at=timezone.now()) # do some activities! place = PlaceFactory(group=group) do_activity(place, users[0], days=2) do_activity(place, users[1], days=8) do_activity(place, users[2], days=31) do_activity(place, users[3], days=61) do_activity(place, users[4], days=91) points = stats.get_users_stats() self.assertEqual( points, { 'active_count': 9, 'active_unverified_count': 1, 'active_with_location_count': 1, 'active_with_mobile_number_count': 1, 'active_with_description_count': 4, 'active_with_photo_count': 1, 'active_memberships_per_active_user_avg': 2.0, 'no_membership_count': 1, 'deleted_count': 1, 'count_active_1d': 9, 'count_active_7d': 10, 'count_active_30d': 11, 'count_active_60d': 12, 'count_active_90d': 13, 'count_activity_active_1d': 0, 'count_activity_active_7d': 1, 'count_activity_active_30d': 2, 'count_activity_active_60d': 3, 'count_activity_active_90d': 4, } )
class TestInviteCreate(APITestCase): def setUp(self): self.member = UserFactory() self.member2 = UserFactory() self.newcomer = UserFactory() self.group = GroupFactory(members=[self.member, self.member2], newcomers=[self.newcomer]) self.group2 = GroupFactory(members=[self.member]) # effectively disable throttling from karrot.invitations.api import InvitesPerDayThrottle InvitesPerDayThrottle.rate = '1000/min' def test_invite_same_email_twice(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_invite_again_after_one_hour(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) # make invitation get one hour behind the timezone to allow resending email i = Invitation.objects.get(id=response.data['id']) i.created_at = i.created_at - relativedelta(hours=1) i.save() response = self.client.post(base_url + str(i.id) + '/resend/') self.assertEqual(response.status_code, status.HTTP_200_OK) def test_cannot_invite_again_in_less_then_one_hour(self): # make invitation.created_at back to timezone.now() to not allow resend the email self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) i = Invitation.objects.get(id=response.data['id']) response = self.client.post(base_url + str(i.id) + '/resend/') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_invite_again_when_expired(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) # make invitation expire i = Invitation.objects.get(id=response.data['id']) i.expires_at = timezone.now() - relativedelta(days=1) i.save() # delete expired invitations Invitation.objects.delete_expired_invitations() response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.data) def test_invite_same_email_to_different_groups(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group2.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_invite_existing_user_but_not_member(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': self.member2.email, 'group': self.group2.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_invite_existing_member(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': self.member2.email, 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_invite_to_invalid_group(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': 345236 }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_invite_with_invalid_email(self): self.client.force_login(self.member) response = self.client.post(base_url, { 'email': 'pleasehä', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @patch('karrot.utils.email_utils.AnymailMessage.send') def test_invite_with_rejected_email(self, send): send.side_effect = AnymailAPIError() self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(Invitation.objects.count(), 0) def test_invite_when_inviting_user_is_not_member_of_group(self): self.client.force_login(self.member2) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group2.id }) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_invite_as_newcomer_fails(self): self.client.force_login(self.newcomer) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group2.id }) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_invite_with_different_invited_by_language(self): self.member.language = 'fr' self.member.save() self.client.force_login(self.member) response = self.client.post(base_url, { 'email': '*****@*****.**', 'group': self.group.id }) self.assertEqual(response.status_code, status.HTTP_201_CREATED) expected = f'[{settings.SITE_NAME}] Invitation de joinde {self.group.name}' self.assertEqual(mail.outbox[-1].subject, expected)
class TestUsersAPI(APITestCase): def setUp(self): self.user = UserFactory() self.user.photo = 'photo.jpg' self.user.save() self.user2 = UserFactory() self.url = '/api/users/' self.user_data = { 'email': faker.email(), 'password': faker.name(), 'display_name': faker.name(), 'address': faker.address(), 'latitude': faker.latitude(), 'longitude': faker.longitude() } self.group = GroupFactory(members=[self.user, self.user2]) self.another_common_group = GroupFactory(members=[self.user, self.user2]) self.user_in_another_group = UserFactory() self.another_group = GroupFactory(members=[self.user_in_another_group]) mail.outbox = [] def test_list_users_forbidden(self): response = self.client.get(self.url) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_list_users_allowed(self): self.client.force_login(user=self.user) response = self.client.get(self.url) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 2) def test_retrieve_user_forbidden(self): url = self.url + str(self.user.id) + '/' response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_retrieve_user_allowed(self): self.client.force_login(user=self.user2) url = self.url + str(self.user.id) + '/' response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('photo_urls', response.data) self.assertNotIn('description', response.data) def test_retrieve_user_profile(self): self.client.force_login(user=self.user2) url = self.url + str(self.user.id) + '/profile/' response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['description'], self.user.description) self.assertEqual(set(response.data['groups']), set([self.group.id, self.another_common_group.id])) self.assertTrue(response.data['photo_urls']['full_size'].startswith('http')) def test_retrieve_user_in_another_group_fails(self): self.client.force_login(user=self.user2) url = self.url + str(self.user_in_another_group.id) + '/' response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_get_conversation(self): self.client.force_login(user=self.user) response = self.client.get('/api/users/{}/conversation/'.format(self.user2.id)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn(self.user.id, response.data['participants']) self.assertIn(self.user2.id, response.data['participants']) self.assertEqual(len(response.data['participants']), 2) def test_get_conversation_for_yourself_fails(self): self.client.force_login(user=self.user) response = self.client.get('/api/users/{}/conversation/'.format(self.user.id)) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)