Merge pull request #123 from taigaio/random-fixes
A collection minor fixes + hotfixesremotes/origin/enhancement/email-actions
commit
154e13cc79
|
@ -16,13 +16,15 @@
|
|||
|
||||
import json
|
||||
from rest_framework.utils import encoders
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
|
||||
def dumps(data, ensure_ascii=True, encoder_class=encoders.JSONEncoder):
|
||||
return json.dumps(data, cls=encoder_class, indent=None, ensure_ascii=ensure_ascii)
|
||||
|
||||
|
||||
def loads(data):
|
||||
if isinstance(data, bytes):
|
||||
data = force_text(data)
|
||||
return json.loads(data)
|
||||
|
||||
# Some backward compatibility that should
|
||||
|
|
|
@ -256,20 +256,14 @@ class RolesViewSet(ModelCrudViewSet):
|
|||
filter_backends = (filters.CanViewProjectFilterBackend,)
|
||||
filter_fields = ('project',)
|
||||
|
||||
@tx.atomic
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
moveTo = self.request.QUERY_PARAMS.get('moveTo', None)
|
||||
if moveTo is None:
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
def pre_delete(self, obj):
|
||||
move_to = self.request.QUERY_PARAMS.get('moveTo', None)
|
||||
if move_to:
|
||||
role_dest = get_object_or_404(self.model, project=obj.project, id=move_to)
|
||||
qs = models.Membership.objects.filter(project_id=obj.project.pk, role=obj)
|
||||
qs.update(role=role_dest)
|
||||
|
||||
obj = self.get_object_or_none()
|
||||
|
||||
moveItem = get_object_or_404(self.model, project=obj.project, id=moveTo)
|
||||
|
||||
self.check_permissions(request, 'destroy', obj)
|
||||
|
||||
models.Membership.objects.filter(project=obj.project, role=obj).update(role=moveItem)
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
super().pre_delete(obj)
|
||||
|
||||
|
||||
# User Stories commin ViewSets
|
||||
|
@ -317,19 +311,19 @@ class PointsViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
|
|||
class MoveOnDestroyMixin(object):
|
||||
@tx.atomic
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
moveTo = self.request.QUERY_PARAMS.get('moveTo', None)
|
||||
if moveTo is None:
|
||||
move_to = self.request.QUERY_PARAMS.get('moveTo', None)
|
||||
if move_to is None:
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
|
||||
obj = self.get_object_or_none()
|
||||
|
||||
moveItem = get_object_or_404(self.model, project=obj.project, id=moveTo)
|
||||
move_item = get_object_or_404(self.model, project=obj.project, id=move_to)
|
||||
|
||||
self.check_permissions(request, 'destroy', obj)
|
||||
kwargs = {self.move_on_destroy_related_field: moveItem}
|
||||
kwargs = {self.move_on_destroy_related_field: move_item}
|
||||
self.move_on_destroy_related_class.objects.filter(project=obj.project, **{self.move_on_destroy_related_field: obj}).update(**kwargs)
|
||||
if getattr(obj.project, self.move_on_destroy_project_default_field) == obj:
|
||||
setattr(obj.project, self.move_on_destroy_project_default_field, moveItem)
|
||||
setattr(obj.project, self.move_on_destroy_project_default_field, move_item)
|
||||
obj.project.save()
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
|
||||
|
|
|
@ -171,8 +171,8 @@ class HistoryEntry(models.Model):
|
|||
|
||||
if changes:
|
||||
change = {
|
||||
"filename": newattachs[aid]["filename"],
|
||||
"url": newattachs[aid]["url"],
|
||||
"filename": newattachs.get(aid, {}).get("filename", ""),
|
||||
"url": newattachs.get(aid, {}).get("url", ""),
|
||||
"changes": changes
|
||||
}
|
||||
attachments["changed"].append(change)
|
||||
|
|
|
@ -14,9 +14,13 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from contextlib import suppress
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import transaction
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
@ -62,6 +66,35 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi
|
|||
qs = qs.select_related("milestone", "project")
|
||||
return qs
|
||||
|
||||
def pre_save(self, obj):
|
||||
# This is very ugly hack, but having
|
||||
# restframework is the only way to do it.
|
||||
# NOTE: code moved as is from serializer
|
||||
# to api because is not serializer logic.
|
||||
related_data = getattr(obj, "_related_data", {})
|
||||
self._role_points = related_data.pop("role_points", None)
|
||||
|
||||
if not obj.id:
|
||||
obj.owner = self.request.user
|
||||
|
||||
super().pre_save(obj)
|
||||
|
||||
def post_save(self, obj, created=False):
|
||||
# Code related to the hack of pre_save method. Rather,
|
||||
# this is the continuation of it.
|
||||
|
||||
Points = apps.get_model("projects", "Points")
|
||||
RolePoints = apps.get_model("userstories", "RolePoints")
|
||||
|
||||
if self._role_points:
|
||||
with suppress(ObjectDoesNotExist):
|
||||
for role_id, points_id in self._role_points.items():
|
||||
role_points = RolePoints.objects.get(role__id=role_id, user_story_id=obj.pk)
|
||||
role_points.points = Points.objects.get(id=points_id, project_id=obj.project_id)
|
||||
role_points.save()
|
||||
|
||||
super().post_save(obj, created)
|
||||
|
||||
@list_route(methods=["POST"])
|
||||
def bulk_create(self, request, **kwargs):
|
||||
serializer = serializers.UserStoriesBulkSerializer(data=request.DATA)
|
||||
|
@ -145,8 +178,3 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi
|
|||
|
||||
return response
|
||||
|
||||
def pre_save(self, obj):
|
||||
if not obj.id:
|
||||
obj.owner = self.request.user
|
||||
|
||||
super().pre_save(obj)
|
||||
|
|
|
@ -52,19 +52,6 @@ class UserStorySerializer(serializers.ModelSerializer):
|
|||
depth = 0
|
||||
read_only_fields = ('created_date', 'modified_date')
|
||||
|
||||
def save_object(self, obj, **kwargs):
|
||||
role_points = obj._related_data.pop("role_points", None)
|
||||
super().save_object(obj, **kwargs)
|
||||
|
||||
points_modelcls = apps.get_model("projects", "Points")
|
||||
|
||||
if role_points:
|
||||
for role_id, points_id in role_points.items():
|
||||
role_points = obj.role_points.get(role__id=role_id)
|
||||
role_points.points = points_modelcls.objects.get(id=points_id,
|
||||
project=obj.project)
|
||||
role_points.save()
|
||||
|
||||
def get_total_points(self, obj):
|
||||
return obj.get_total_points()
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014 Anler Hernández <hello@anler.me>
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import factories as f
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
DUMMY_BMP_DATA = b'BM:\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x04\x00\x00\x00\x13\x0b\x00\x00\x13\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
|
||||
def test_change_avatar(client):
|
||||
url = reverse('users-change-avatar')
|
||||
|
||||
user = f.UserFactory()
|
||||
client.login(user)
|
||||
|
||||
with NamedTemporaryFile() as avatar:
|
||||
# Test no avatar send
|
||||
post_data = {}
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 400
|
||||
|
||||
# Test invalid file send
|
||||
post_data = {
|
||||
'avatar': avatar
|
||||
}
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 400
|
||||
|
||||
# Test empty valid avatar send
|
||||
avatar.write(DUMMY_BMP_DATA)
|
||||
avatar.seek(0)
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 200
|
|
@ -29,8 +29,7 @@ from .. import factories as f
|
|||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_update_milestone(client):
|
||||
def test_update_milestone_with_userstories_list(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user)
|
||||
role = f.RoleFactory.create(project=project)
|
||||
|
@ -39,7 +38,6 @@ def test_api_update_milestone(client):
|
|||
|
||||
points = f.PointsFactory.create(project=project, value=None)
|
||||
us = f.UserStoryFactory.create(project=project, owner=user)
|
||||
# role_points = f.RolePointsFactory.create(points=points, user_story=us, role=role)
|
||||
|
||||
url = reverse("milestones-detail", args=[sprint.pk])
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from taiga.base.utils import json
|
||||
from taiga.projects.issues.models import Issue
|
||||
from taiga.projects.wiki.models import WikiPage
|
||||
from taiga.projects.userstories.models import UserStory
|
||||
|
@ -58,7 +58,7 @@ def test_valid_concurrent_save_for_issue(client):
|
|||
url = reverse("issues-detail", args=(issue.id,))
|
||||
data = {"version": 10}
|
||||
response = client.patch(url, json.dumps(data), content_type="application/json")
|
||||
assert json.loads(response.content.decode('utf-8'))['version'] == 11
|
||||
assert json.loads(response.content)['version'] == 11
|
||||
assert response.status_code == 200
|
||||
issue = Issue.objects.get(id=issue.id)
|
||||
assert issue.version == 11
|
||||
|
@ -85,7 +85,7 @@ def test_valid_concurrent_save_for_wiki_page(client):
|
|||
url = reverse("wiki-detail", args=(wiki_page.id,))
|
||||
data = {"version": 10}
|
||||
response = client.patch(url, json.dumps(data), content_type="application/json")
|
||||
assert json.loads(response.content.decode('utf-8'))['version'] == 11
|
||||
assert json.loads(response.content)['version'] == 11
|
||||
assert response.status_code == 200
|
||||
wiki_page = WikiPage.objects.get(id=wiki_page.id)
|
||||
assert wiki_page.version == 11
|
||||
|
@ -128,7 +128,7 @@ def test_valid_concurrent_save_for_us(client):
|
|||
url = reverse("userstories-detail", args=(userstory.id,))
|
||||
data = {"version": 10}
|
||||
response = client.patch(url, json.dumps(data), content_type="application/json")
|
||||
assert json.loads(response.content.decode('utf-8'))['version'] == 11
|
||||
assert json.loads(response.content)['version'] == 11
|
||||
assert response.status_code == 200
|
||||
userstory = UserStory.objects.get(id=userstory.id)
|
||||
assert userstory.version == 11
|
||||
|
@ -159,7 +159,7 @@ def test_valid_concurrent_save_for_task(client):
|
|||
url = reverse("tasks-detail", args=(task.id,))
|
||||
data = {"version": 10}
|
||||
response = client.patch(url, json.dumps(data), content_type="application/json")
|
||||
assert json.loads(response.content.decode('utf-8'))['version'] == 11
|
||||
assert json.loads(response.content)['version'] == 11
|
||||
assert response.status_code == 200
|
||||
task = Task.objects.get(id=task.id)
|
||||
assert task.version == 11
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014 Anler Hernández <hello@anler.me>
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from .. import factories as f
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_archived_filter(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user)
|
||||
f.MembershipFactory.create(project=project, user=user)
|
||||
f.UserStoryFactory.create(project=project)
|
||||
f.UserStoryFactory.create(is_archived=True, project=project)
|
||||
|
||||
client.login(user)
|
||||
|
||||
url = reverse("userstories-list")
|
||||
|
||||
data = {}
|
||||
response = client.get(url, data)
|
||||
assert len(json.loads(response.content.decode('utf-8'))) == 2
|
||||
|
||||
data = {"is_archived": 0}
|
||||
response = client.get(url, data)
|
||||
assert len(json.loads(response.content.decode('utf-8'))) == 1
|
||||
|
||||
data = {"is_archived": 1}
|
||||
response = client.get(url, data)
|
||||
assert len(json.loads(response.content.decode('utf-8'))) == 1
|
|
@ -7,7 +7,7 @@ import pytest
|
|||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_create_project(client):
|
||||
def test_create_project(client):
|
||||
user = f.create_user()
|
||||
url = reverse("projects-list")
|
||||
data = {"name": "project name", "description": "project description"}
|
||||
|
@ -18,7 +18,7 @@ def test_api_create_project(client):
|
|||
assert response.status_code == 201
|
||||
|
||||
|
||||
def test_api_partially_update_project(client):
|
||||
def test_partially_update_project(client):
|
||||
project = f.create_project()
|
||||
url = reverse("projects-detail", kwargs={"pk": project.pk})
|
||||
data = {"name": ""}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014 Anler Hernández <hello@anler.me>
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from taiga.base.utils import json
|
||||
|
||||
from taiga.users.models import Role
|
||||
from taiga.projects.models import Membership
|
||||
from taiga.projects.models import Project
|
||||
from taiga.projects.userstories.serializers import UserStorySerializer
|
||||
|
||||
from .. import factories as f
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
def test_destroy_role_and_reassign_members(client):
|
||||
user1 = f.UserFactory.create()
|
||||
user2 = f.UserFactory.create()
|
||||
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=user2, role=role2)
|
||||
|
||||
url = reverse("roles-detail", args=[role2.pk]) + "?moveTo={}".format(role1.pk)
|
||||
|
||||
client.login(user1)
|
||||
|
||||
response = client.delete(url)
|
||||
assert response.status_code == 204
|
||||
|
||||
qs = Role.objects.filter(project=project)
|
||||
assert qs.count() == 1
|
||||
|
||||
qs = Membership.objects.filter(project=project, role_id=role2.pk)
|
||||
assert qs.count() == 0
|
||||
|
||||
qs = Membership.objects.filter(project=project, role_id=role1.pk)
|
||||
assert qs.count() == 2
|
||||
|
||||
def test_destroy_role_and_reassign_members_with_deleted_project(client):
|
||||
"""
|
||||
Regression test, that fixes some 500 errors on production
|
||||
"""
|
||||
|
||||
user1 = f.UserFactory.create()
|
||||
user2 = f.UserFactory.create()
|
||||
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=user2, role=role2)
|
||||
|
||||
Project.objects.filter(pk=project.id).delete()
|
||||
|
||||
url = reverse("roles-detail", args=[role2.pk]) + "?moveTo={}".format(role1.pk)
|
||||
client.login(user1)
|
||||
|
||||
response = client.delete(url)
|
||||
|
||||
# FIXME: really should return 403? I think it should be 404
|
||||
assert response.status_code == 403, response.content
|
|
@ -1,5 +1,6 @@
|
|||
import pytest
|
||||
import json
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
@ -11,7 +12,7 @@ from taiga.auth.tokens import get_token_for_user
|
|||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_user_normal_user(client):
|
||||
def test_users_create_through_standard_api(client):
|
||||
user = f.UserFactory.create(is_superuser=True)
|
||||
|
||||
url = reverse('users-list')
|
||||
|
@ -26,7 +27,7 @@ def test_api_user_normal_user(client):
|
|||
assert response.status_code == 405
|
||||
|
||||
|
||||
def test_api_user_patch_same_email(client):
|
||||
def test_update_user_with_same_email(client):
|
||||
user = f.UserFactory.create(email="same@email.com")
|
||||
url = reverse('users-detail', kwargs={"pk": user.pk})
|
||||
data = {"email": "same@email.com"}
|
||||
|
@ -38,7 +39,7 @@ def test_api_user_patch_same_email(client):
|
|||
assert response.data['_error_message'] == 'Duplicated email'
|
||||
|
||||
|
||||
def test_api_user_patch_duplicated_email(client):
|
||||
def test_update_user_with_duplicated_email(client):
|
||||
f.UserFactory.create(email="one@email.com")
|
||||
user = f.UserFactory.create(email="two@email.com")
|
||||
url = reverse('users-detail', kwargs={"pk": user.pk})
|
||||
|
@ -51,7 +52,7 @@ def test_api_user_patch_duplicated_email(client):
|
|||
assert response.data['_error_message'] == 'Duplicated email'
|
||||
|
||||
|
||||
def test_api_user_patch_invalid_email(client):
|
||||
def test_update_user_with_invalid_email(client):
|
||||
user = f.UserFactory.create(email="my@email.com")
|
||||
url = reverse('users-detail', kwargs={"pk": user.pk})
|
||||
data = {"email": "my@email"}
|
||||
|
@ -63,7 +64,7 @@ def test_api_user_patch_invalid_email(client):
|
|||
assert response.data['_error_message'] == 'Not valid email'
|
||||
|
||||
|
||||
def test_api_user_patch_valid_email(client):
|
||||
def test_update_user_with_valid_email(client):
|
||||
user = f.UserFactory.create(email="old@email.com")
|
||||
url = reverse('users-detail', kwargs={"pk": user.pk})
|
||||
data = {"email": "new@email.com"}
|
||||
|
@ -77,7 +78,7 @@ def test_api_user_patch_valid_email(client):
|
|||
assert user.new_email == "new@email.com"
|
||||
|
||||
|
||||
def test_api_user_action_change_email_ok(client):
|
||||
def test_validate_requested_email_change(client):
|
||||
user = f.UserFactory.create(email_token="change_email_token", new_email="new@email.com")
|
||||
url = reverse('users-change-email')
|
||||
data = {"email_token": "change_email_token"}
|
||||
|
@ -92,19 +93,17 @@ def test_api_user_action_change_email_ok(client):
|
|||
assert user.email == "new@email.com"
|
||||
|
||||
|
||||
def test_api_user_action_change_email_no_token(client):
|
||||
def test_validate_requested_email_change_without_token(client):
|
||||
user = f.UserFactory.create(email_token="change_email_token", new_email="new@email.com")
|
||||
url = reverse('users-change-email')
|
||||
data = {}
|
||||
|
||||
client.login(user)
|
||||
response = client.post(url, json.dumps(data), content_type="application/json")
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.data['_error_message'] == 'Invalid, are you sure the token is correct and you didn\'t use it before?'
|
||||
|
||||
|
||||
def test_api_user_action_change_email_invalid_token(client):
|
||||
def test_validate_requested_email_change_with_invalid_token(client):
|
||||
user = f.UserFactory.create(email_token="change_email_token", new_email="new@email.com")
|
||||
url = reverse('users-change-email')
|
||||
data = {"email_token": "invalid_email_token"}
|
||||
|
@ -113,10 +112,9 @@ def test_api_user_action_change_email_invalid_token(client):
|
|||
response = client.post(url, json.dumps(data), content_type="application/json")
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.data['_error_message'] == 'Invalid, are you sure the token is correct and you didn\'t use it before?'
|
||||
|
||||
|
||||
def test_api_user_delete(client):
|
||||
def test_delete_self_user(client):
|
||||
user = f.UserFactory.create()
|
||||
url = reverse('users-detail', kwargs={"pk": user.pk})
|
||||
|
||||
|
@ -128,7 +126,7 @@ def test_api_user_delete(client):
|
|||
assert user.full_name == "Deleted user"
|
||||
|
||||
|
||||
def test_api_user_cancel_valid_token(client):
|
||||
def test_cancel_self_user_with_valid_token(client):
|
||||
user = f.UserFactory.create()
|
||||
url = reverse('users-cancel')
|
||||
cancel_token = get_token_for_user(user, "cancel_account")
|
||||
|
@ -141,7 +139,7 @@ def test_api_user_cancel_valid_token(client):
|
|||
assert user.full_name == "Deleted user"
|
||||
|
||||
|
||||
def test_api_user_cancel_invalid_token(client):
|
||||
def test_cancel_self_user_with_invalid_token(client):
|
||||
user = f.UserFactory.create()
|
||||
url = reverse('users-cancel')
|
||||
data = {"cancel_token": "invalid_cancel_token"}
|
||||
|
@ -149,4 +147,32 @@ def test_api_user_cancel_invalid_token(client):
|
|||
response = client.post(url, json.dumps(data), content_type="application/json")
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.data['_error_message'] == "Invalid, are you sure the token is correct?"
|
||||
|
||||
|
||||
DUMMY_BMP_DATA = b'BM:\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x04\x00\x00\x00\x13\x0b\x00\x00\x13\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
|
||||
def test_change_avatar(client):
|
||||
url = reverse('users-change-avatar')
|
||||
|
||||
user = f.UserFactory()
|
||||
client.login(user)
|
||||
|
||||
with NamedTemporaryFile() as avatar:
|
||||
# Test no avatar send
|
||||
post_data = {}
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 400
|
||||
|
||||
# Test invalid file send
|
||||
post_data = {
|
||||
'avatar': avatar
|
||||
}
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 400
|
||||
|
||||
# Test empty valid avatar send
|
||||
avatar.write(DUMMY_BMP_DATA)
|
||||
avatar.seek(0)
|
||||
response = client.post(url, post_data)
|
||||
assert response.status_code == 200
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import copy
|
||||
from unittest import mock
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
@ -11,10 +12,7 @@ pytestmark = pytest.mark.django_db
|
|||
|
||||
|
||||
def test_get_userstories_from_bulk():
|
||||
data = """
|
||||
User Story #1
|
||||
User Story #2
|
||||
"""
|
||||
data = "User Story #1\nUser Story #2\n"
|
||||
userstories = services.get_userstories_from_bulk(data)
|
||||
|
||||
assert len(userstories) == 2
|
||||
|
@ -23,10 +21,7 @@ User Story #2
|
|||
|
||||
|
||||
def test_create_userstories_in_bulk():
|
||||
data = """
|
||||
User Story #1
|
||||
User Story #2
|
||||
"""
|
||||
data = "User Story #1\nUser Story #2\n"
|
||||
|
||||
with mock.patch("taiga.projects.userstories.services.db") as db:
|
||||
userstories = services.create_userstories_in_bulk(data)
|
||||
|
@ -41,7 +36,9 @@ def test_update_userstories_order_in_bulk():
|
|||
|
||||
with mock.patch("taiga.projects.userstories.services.db") as db:
|
||||
services.update_userstories_order_in_bulk(data, "backlog_order", project)
|
||||
db.update_in_bulk_with_ids.assert_called_once_with([1, 2], [{"backlog_order": 1}, {"backlog_order": 2}],
|
||||
db.update_in_bulk_with_ids.assert_called_once_with([1, 2],
|
||||
[{"backlog_order": 1},
|
||||
{"backlog_order": 2}],
|
||||
model=models.UserStory)
|
||||
|
||||
|
||||
|
@ -108,3 +105,76 @@ def test_api_update_backlog_order_in_bulk(client):
|
|||
assert response1.status_code == 204, response.data
|
||||
assert response2.status_code == 204, response.data
|
||||
assert response3.status_code == 204, response.data
|
||||
|
||||
|
||||
from taiga.projects.userstories.serializers import UserStorySerializer
|
||||
|
||||
|
||||
def test_update_userstory_points(client):
|
||||
user1 = f.UserFactory.create()
|
||||
user2 = f.UserFactory.create()
|
||||
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=user2, role=role2)
|
||||
|
||||
points1 = f.PointsFactory.create(project=project, value=None)
|
||||
points2 = f.PointsFactory.create(project=project, value=1)
|
||||
points3 = f.PointsFactory.create(project=project, value=2)
|
||||
|
||||
us = f.UserStoryFactory.create(project=project, owner=user1)
|
||||
url = reverse("userstories-detail", args=[us.pk])
|
||||
usdata = UserStorySerializer(us).data
|
||||
|
||||
client.login(user1)
|
||||
|
||||
# Api should ignore invalid values
|
||||
data = {}
|
||||
data["version"] = usdata["version"]
|
||||
data["points"] = copy.copy(usdata["points"])
|
||||
data["points"].update({'2000':points3.pk})
|
||||
|
||||
response = client.json.patch(url, json.dumps(data))
|
||||
assert response.status_code == 200, response.data
|
||||
|
||||
# Api should save successful
|
||||
data = {}
|
||||
data["version"] = usdata["version"]
|
||||
data["points"] = copy.copy(usdata["points"])
|
||||
data["points"].update({str(role1.pk):points3.pk})
|
||||
|
||||
response = client.json.patch(url, json.dumps(data))
|
||||
assert response.status_code == 200, response.data
|
||||
|
||||
us = models.UserStory.objects.get(pk=us.pk)
|
||||
rp = list(us.role_points.values_list("role_id", "points_id"))
|
||||
|
||||
assert rp == [(role1.pk, points3.pk), (role2.pk, points1.pk)]
|
||||
|
||||
|
||||
def test_archived_filter(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user)
|
||||
f.MembershipFactory.create(project=project, user=user)
|
||||
f.UserStoryFactory.create(project=project)
|
||||
f.UserStoryFactory.create(is_archived=True, project=project)
|
||||
|
||||
client.login(user)
|
||||
|
||||
url = reverse("userstories-list")
|
||||
|
||||
data = {}
|
||||
response = client.get(url, data)
|
||||
assert len(json.loads(response.content)) == 2
|
||||
|
||||
data = {"is_archived": 0}
|
||||
response = client.get(url, data)
|
||||
assert len(json.loads(response.content)) == 1
|
||||
|
||||
data = {"is_archived": 1}
|
||||
response = client.get(url, data)
|
||||
assert len(json.loads(response.content)) == 1
|
||||
|
||||
|
|
Loading…
Reference in New Issue