905 lines
37 KiB
Python
905 lines
37 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
|
# Copyright (C) 2014-2017 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 uuid
|
|
import csv
|
|
import pytz
|
|
|
|
from datetime import datetime, timedelta
|
|
from urllib.parse import quote
|
|
|
|
from unittest import mock
|
|
|
|
from django.core.urlresolvers import reverse
|
|
|
|
from taiga.base.utils import json
|
|
from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS
|
|
from taiga.projects.issues import services, models
|
|
from taiga.projects.occ import OCCResourceMixin
|
|
|
|
from .. import factories as f
|
|
|
|
import pytest
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def create_filter_issues_context():
|
|
data = {}
|
|
|
|
data["project"] = f.ProjectFactory.create()
|
|
project = data["project"]
|
|
data["users"] = [f.UserFactory.create(is_superuser=True) for i in range(0, 3)]
|
|
data["roles"] = [f.RoleFactory.create() for i in range(0, 3)]
|
|
user_roles = zip(data["users"], data["roles"])
|
|
# Add membership fixtures
|
|
[f.MembershipFactory.create(user=user, project=project, role=role) for (user, role) in user_roles]
|
|
|
|
data["statuses"] = [f.IssueStatusFactory.create(project=project) for i in range(0, 4)]
|
|
data["types"] = [f.IssueTypeFactory.create(project=project) for i in range(0, 2)]
|
|
data["severities"] = [f.SeverityFactory.create(project=project) for i in range(0, 4)]
|
|
data["priorities"] = [f.PriorityFactory.create(project=project) for i in range(0, 4)]
|
|
data["tags"] = ["test1test2test3", "test1", "test2", "test3"]
|
|
|
|
# ------------------------------------------------------------------------------------------------
|
|
# | Issue | Owner | Assigned To | Status | Type | Priority | Severity | Tags |
|
|
# |-------#--------#-------------#---------#-------#-----------#-----------#---------------------|
|
|
# | 0 | user2 | None | status3 | type1 | priority2 | severity1 | tag1 |
|
|
# | 1 | user1 | None | status3 | type2 | priority2 | severity1 | tag2 |
|
|
# | 2 | user3 | None | status1 | type1 | priority3 | severity2 | tag1 tag2 |
|
|
# | 3 | user2 | None | status0 | type2 | priority3 | severity1 | tag3 |
|
|
# | 4 | user1 | user1 | status0 | type1 | priority2 | severity3 | tag1 tag2 tag3 |
|
|
# | 5 | user3 | user1 | status2 | type2 | priority3 | severity2 | tag3 |
|
|
# | 6 | user2 | user1 | status3 | type1 | priority2 | severity0 | tag1 tag2 |
|
|
# | 7 | user1 | user2 | status0 | type2 | priority1 | severity3 | tag3 |
|
|
# | 8 | user3 | user2 | status3 | type1 | priority0 | severity1 | tag1 |
|
|
# | 9 | user2 | user3 | status1 | type2 | priority0 | severity2 | tag0 |
|
|
# ------------------------------------------------------------------------------------------------
|
|
(user1, user2, user3, ) = data["users"]
|
|
(status0, status1, status2, status3 ) = data["statuses"]
|
|
(type1, type2, ) = data["types"]
|
|
(severity0, severity1, severity2, severity3, ) = data["severities"]
|
|
(priority0, priority1, priority2, priority3, ) = data["priorities"]
|
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
|
|
|
f.IssueFactory.create(project=project, owner=user2, assigned_to=None,
|
|
status=status3, type=type1, priority=priority2, severity=severity1,
|
|
tags=[tag1])
|
|
f.IssueFactory.create(project=project, owner=user1, assigned_to=None,
|
|
status=status3, type=type2, priority=priority2, severity=severity1,
|
|
tags=[tag2])
|
|
f.IssueFactory.create(project=project, owner=user3, assigned_to=None,
|
|
status=status1, type=type1, priority=priority3, severity=severity2,
|
|
tags=[tag1, tag2])
|
|
f.IssueFactory.create(project=project, owner=user2, assigned_to=None,
|
|
status=status0, type=type2, priority=priority3, severity=severity1,
|
|
tags=[tag3])
|
|
f.IssueFactory.create(project=project, owner=user1, assigned_to=user1,
|
|
status=status0, type=type1, priority=priority2, severity=severity3,
|
|
tags=[tag1, tag2, tag3])
|
|
f.IssueFactory.create(project=project, owner=user3, assigned_to=user1,
|
|
status=status2, type=type2, priority=priority3, severity=severity2,
|
|
tags=[tag3])
|
|
f.IssueFactory.create(project=project, owner=user2, assigned_to=user1,
|
|
status=status3, type=type1, priority=priority2, severity=severity0,
|
|
tags=[tag1, tag2])
|
|
f.IssueFactory.create(project=project, owner=user1, assigned_to=user2,
|
|
status=status0, type=type2, priority=priority1, severity=severity3,
|
|
tags=[tag3])
|
|
f.IssueFactory.create(project=project, owner=user3, assigned_to=user2,
|
|
status=status3, type=type1, priority=priority0, severity=severity1,
|
|
tags=[tag1])
|
|
f.IssueFactory.create(project=project, owner=user2, assigned_to=user3,
|
|
status=status1, type=type2, priority=priority0, severity=severity2,
|
|
tags=[tag0])
|
|
|
|
return data
|
|
|
|
|
|
def test_get_issues_from_bulk():
|
|
data = """
|
|
Issue #1
|
|
Issue #2
|
|
"""
|
|
issues = services.get_issues_from_bulk(data)
|
|
|
|
assert len(issues) == 2
|
|
assert issues[0].subject == "Issue #1"
|
|
assert issues[1].subject == "Issue #2"
|
|
|
|
|
|
def test_create_issues_in_bulk(db):
|
|
data = """
|
|
Issue #1
|
|
Issue #2
|
|
"""
|
|
|
|
with mock.patch("taiga.projects.issues.services.db") as db:
|
|
issues = services.create_issues_in_bulk(data)
|
|
db.save_in_bulk.assert_called_once_with(issues, None, None)
|
|
|
|
|
|
def test_create_issue_without_status(client):
|
|
user = f.UserFactory.create()
|
|
project = f.ProjectFactory.create(owner=user)
|
|
status = f.IssueStatusFactory.create(project=project)
|
|
priority = f.PriorityFactory.create(project=project)
|
|
severity = f.SeverityFactory.create(project=project)
|
|
type = f.IssueTypeFactory.create(project=project)
|
|
project.default_issue_status = status
|
|
project.default_priority = priority
|
|
project.default_severity = severity
|
|
project.default_issue_type = type
|
|
project.save()
|
|
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
|
url = reverse("issues-list")
|
|
|
|
data = {"subject": "Test user story", "project": project.id}
|
|
client.login(user)
|
|
response = client.json.post(url, json.dumps(data))
|
|
assert response.status_code == 201
|
|
assert response.data['status'] == project.default_issue_status.id
|
|
assert response.data['severity'] == project.default_severity.id
|
|
assert response.data['priority'] == project.default_priority.id
|
|
assert response.data['type'] == project.default_issue_type.id
|
|
|
|
|
|
def test_create_issue_without_status_in_project_without_default_values(client):
|
|
user = f.UserFactory.create()
|
|
project = f.ProjectFactory.create(owner=user,
|
|
default_issue_status=None,
|
|
default_priority=None,
|
|
default_severity=None,
|
|
default_issue_type = None)
|
|
|
|
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
|
url = reverse("issues-list")
|
|
|
|
data = {"subject": "Test user story", "project": project.id}
|
|
client.login(user)
|
|
response = client.json.post(url, json.dumps(data))
|
|
assert response.status_code == 201
|
|
assert response.data['status'] == None
|
|
assert response.data['severity'] == None
|
|
assert response.data['priority'] == None
|
|
assert response.data['type'] == None
|
|
|
|
|
|
def test_api_create_issues_in_bulk(client):
|
|
project = f.create_project()
|
|
f.MembershipFactory(project=project, user=project.owner, is_admin=True)
|
|
|
|
url = reverse("issues-bulk-create")
|
|
|
|
data = {"bulk_issues": "Issue #1\nIssue #2\n",
|
|
"project_id": project.id}
|
|
|
|
client.login(project.owner)
|
|
response = client.json.post(url, json.dumps(data))
|
|
|
|
assert response.status_code == 200, response.data
|
|
|
|
|
|
def test_api_filter_by_subject(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="some random subject", owner=user)
|
|
url = reverse("issues-list") + "?q=some subject"
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1, number_of_issues
|
|
|
|
|
|
def test_api_filter_by_text_1(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="this is the issue one", owner=user)
|
|
f.create_issue(subject="this is the issue two", owner=issue.owner)
|
|
url = reverse("issues-list") + "?q=one"
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
|
|
|
|
def test_api_filter_by_text_2(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="this is the issue one", owner=user)
|
|
f.create_issue(subject="this is the issue two", owner=issue.owner)
|
|
url = reverse("issues-list") + "?q=this is the issue one"
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
|
|
|
|
def test_api_filter_by_text_3(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="this is the issue one", owner=user)
|
|
f.create_issue(subject="this is the issue two", owner=issue.owner)
|
|
url = reverse("issues-list") + "?q=this is the issue"
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 2
|
|
|
|
|
|
def test_api_filter_by_text_4(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="this is the issue one", owner=user)
|
|
f.create_issue(subject="this is the issue two", owner=issue.owner)
|
|
url = reverse("issues-list") + "?q=one two"
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 0
|
|
|
|
|
|
def test_api_filter_by_text_5(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="python 3", owner=user)
|
|
url = reverse("issues-list") + "?q=python 3"
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
|
|
|
|
def test_api_filter_by_text_6(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
f.create_issue(owner=user)
|
|
issue = f.create_issue(subject="test", owner=user)
|
|
issue.ref = 123
|
|
issue.save()
|
|
url = reverse("issues-list") + "?q=%s" % (issue.ref)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
|
|
|
|
def test_api_filter_by_created_date(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
|
|
|
|
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
|
|
issue = f.create_issue(owner=user)
|
|
|
|
url = reverse("issues-list") + "?created_date=%s" % (
|
|
quote(issue.created_date.isoformat())
|
|
)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
assert response.data[0]["ref"] == issue.ref
|
|
|
|
|
|
def test_api_filter_by_created_date__gt(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
|
|
|
|
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
|
|
issue = f.create_issue(owner=user)
|
|
|
|
url = reverse("issues-list") + "?created_date__gt=%s" % (
|
|
quote(one_day_ago.isoformat())
|
|
)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
assert response.data[0]["ref"] == issue.ref
|
|
|
|
|
|
def test_api_filter_by_created_date__gte(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
|
|
|
|
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
|
|
issue = f.create_issue(owner=user)
|
|
|
|
url = reverse("issues-list") + "?created_date__gte=%s" % (
|
|
quote(one_day_ago.isoformat())
|
|
)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 2
|
|
|
|
|
|
def test_api_filter_by_created_date__lt(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
|
|
|
|
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
|
|
issue = f.create_issue(owner=user)
|
|
|
|
url = reverse("issues-list") + "?created_date__lt=%s" % (
|
|
quote(issue.created_date.isoformat())
|
|
)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert response.data[0]["ref"] == old_issue.ref
|
|
|
|
|
|
def test_api_filter_by_created_date__lte(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
one_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
|
|
|
|
old_issue = f.create_issue(owner=user, created_date=one_day_ago)
|
|
issue = f.create_issue(owner=user)
|
|
|
|
url = reverse("issues-list") + "?created_date__lte=%s" % (
|
|
quote(issue.created_date.isoformat())
|
|
)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 2
|
|
|
|
|
|
def test_api_filter_by_modified_date__gte(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
_day_ago = datetime.now(pytz.utc) - timedelta(days=1)
|
|
|
|
older_issue = f.create_issue(owner=user)
|
|
issue = f.create_issue(owner=user)
|
|
# we have to refresh as it slightly differs
|
|
issue.refresh_from_db()
|
|
|
|
assert older_issue.modified_date < issue.modified_date
|
|
|
|
url = reverse("issues-list") + "?modified_date__gte=%s" % (
|
|
quote(issue.modified_date.isoformat())
|
|
)
|
|
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
assert response.data[0]["ref"] == issue.ref
|
|
|
|
|
|
def test_api_filter_by_finished_date(client):
|
|
user = f.UserFactory(is_superuser=True)
|
|
project = f.ProjectFactory.create()
|
|
status0 = f.IssueStatusFactory.create(project=project, is_closed=True)
|
|
|
|
issue = f.create_issue(owner=user)
|
|
finished_issue = f.create_issue(owner=user, status=status0)
|
|
|
|
assert finished_issue.finished_date
|
|
|
|
url = reverse("issues-list") + "?finished_date__gte=%s" % (
|
|
quote(finished_issue.finished_date.isoformat())
|
|
)
|
|
client.login(issue.owner)
|
|
response = client.get(url)
|
|
number_of_issues = len(response.data)
|
|
|
|
assert response.status_code == 200
|
|
assert number_of_issues == 1
|
|
assert response.data[0]["ref"] == finished_issue.ref
|
|
|
|
|
|
@pytest.mark.parametrize("filter_name,collection,expected,exclude_expected,is_text", [
|
|
('type', 'types', 5, 5, False),
|
|
('severity', 'severities', 1, 9, False),
|
|
('priority', 'priorities', 2, 8, False),
|
|
('status', 'statuses', 3, 7, False),
|
|
('assigned_to', 'users', 3, 7, False),
|
|
('tags', 'tags', 1, 9, True),
|
|
('owner', 'users', 3, 7, False),
|
|
('role', 'roles', 3, 7, False),
|
|
])
|
|
def test_api_filters(client, filter_name, collection, expected, exclude_expected, is_text):
|
|
data = create_filter_issues_context()
|
|
project = data["project"]
|
|
options = data[collection]
|
|
|
|
client.login(data["users"][0])
|
|
if is_text:
|
|
param = options[0]
|
|
else:
|
|
param = options[0].id
|
|
|
|
# include test
|
|
url = "{}?project={}&{}={}".format(reverse('issues-list'), project.id, filter_name, param)
|
|
response = client.get(url)
|
|
assert response.status_code == 200
|
|
assert len(response.data) == expected
|
|
|
|
# exclude test
|
|
url = "{}?project={}&exclude_{}={}".format(reverse('issues-list'), project.id, filter_name, param)
|
|
response = client.get(url)
|
|
assert response.status_code == 200
|
|
assert len(response.data) == exclude_expected
|
|
|
|
|
|
def test_mulitple_exclude_filter_tags(client):
|
|
data = create_filter_issues_context()
|
|
project = data["project"]
|
|
client.login(data["users"][0])
|
|
tags = data["tags"]
|
|
|
|
url = "{}?project={}&exclude_tags={},{}".format(reverse('issues-list'), project.id, tags[1], tags[2])
|
|
response = client.get(url)
|
|
assert response.status_code == 200
|
|
assert len(response.data) == 4
|
|
|
|
|
|
def test_api_filters_data(client):
|
|
data = create_filter_issues_context()
|
|
project = data["project"]
|
|
(user1, user2, user3, ) = data["users"]
|
|
(status0, status1, status2, status3, ) = data["statuses"]
|
|
(type1, type2, ) = data["types"]
|
|
(priority0, priority1, priority2, priority3, ) = data["priorities"]
|
|
(severity0, severity1, severity2, severity3, ) = data["severities"]
|
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
|
|
|
url = reverse("issues-filters-data") + "?project={}".format(project.id)
|
|
client.login(user1)
|
|
|
|
## No filter
|
|
response = client.get(url)
|
|
assert response.status_code == 200
|
|
|
|
assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 3
|
|
assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 4
|
|
assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 3
|
|
|
|
assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 4
|
|
assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 3
|
|
assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 1
|
|
|
|
assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 3
|
|
assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 4
|
|
|
|
assert next(filter(lambda i: i['id'] == type1.id, response.data["types"]))["count"] == 5
|
|
assert next(filter(lambda i: i['id'] == type2.id, response.data["types"]))["count"] == 5
|
|
|
|
assert next(filter(lambda i: i['id'] == priority0.id, response.data["priorities"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == priority1.id, response.data["priorities"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == priority2.id, response.data["priorities"]))["count"] == 4
|
|
assert next(filter(lambda i: i['id'] == priority3.id, response.data["priorities"]))["count"] == 3
|
|
|
|
assert next(filter(lambda i: i['id'] == severity0.id, response.data["severities"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == severity1.id, response.data["severities"]))["count"] == 4
|
|
assert next(filter(lambda i: i['id'] == severity2.id, response.data["severities"]))["count"] == 3
|
|
assert next(filter(lambda i: i['id'] == severity3.id, response.data["severities"]))["count"] == 2
|
|
|
|
assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 1
|
|
assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 5
|
|
assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 4
|
|
assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 4
|
|
|
|
## Filter ((status0 or status3) and type1)
|
|
response = client.get(url + "&status={},{}&type={}".format(status3.id, status0.id, type1.id))
|
|
assert response.status_code == 200
|
|
|
|
assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 1
|
|
|
|
assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 0
|
|
|
|
assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 3
|
|
|
|
assert next(filter(lambda i: i['id'] == type1.id, response.data["types"]))["count"] == 4
|
|
assert next(filter(lambda i: i['id'] == type2.id, response.data["types"]))["count"] == 3
|
|
|
|
assert next(filter(lambda i: i['id'] == priority0.id, response.data["priorities"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == priority1.id, response.data["priorities"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == priority2.id, response.data["priorities"]))["count"] == 3
|
|
assert next(filter(lambda i: i['id'] == priority3.id, response.data["priorities"]))["count"] == 0
|
|
|
|
assert next(filter(lambda i: i['id'] == severity0.id, response.data["severities"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == severity1.id, response.data["severities"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == severity2.id, response.data["severities"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == severity3.id, response.data["severities"]))["count"] == 1
|
|
|
|
assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 0
|
|
assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 4
|
|
assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 2
|
|
assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 1
|
|
|
|
## Filter ((tag1 and tag2) and (user1 or user2))
|
|
response = client.get(url + "&tags={},{}&owner={},{}".format(tag1, tag2, user1.id, user2.id))
|
|
assert response.status_code == 200
|
|
|
|
assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 1
|
|
|
|
assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 0
|
|
|
|
assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 1
|
|
|
|
assert next(filter(lambda i: i['id'] == type1.id, response.data["types"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == type2.id, response.data["types"]))["count"] == 0
|
|
|
|
assert next(filter(lambda i: i['id'] == priority0.id, response.data["priorities"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == priority1.id, response.data["priorities"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == priority2.id, response.data["priorities"]))["count"] == 2
|
|
assert next(filter(lambda i: i['id'] == priority3.id, response.data["priorities"]))["count"] == 0
|
|
|
|
assert next(filter(lambda i: i['id'] == severity0.id, response.data["severities"]))["count"] == 1
|
|
assert next(filter(lambda i: i['id'] == severity1.id, response.data["severities"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == severity2.id, response.data["severities"]))["count"] == 0
|
|
assert next(filter(lambda i: i['id'] == severity3.id, response.data["severities"]))["count"] == 1
|
|
|
|
assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 0
|
|
assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 2
|
|
assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 2
|
|
assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 1
|
|
|
|
|
|
def test_get_invalid_csv(client):
|
|
url = reverse("issues-csv")
|
|
|
|
response = client.get(url)
|
|
assert response.status_code == 404
|
|
|
|
response = client.get("{}?uuid={}".format(url, "not-valid-uuid"))
|
|
assert response.status_code == 404
|
|
|
|
|
|
def test_get_valid_csv(client):
|
|
url = reverse("issues-csv")
|
|
project = f.ProjectFactory.create(issues_csv_uuid=uuid.uuid4().hex)
|
|
|
|
response = client.get("{}?uuid={}".format(url, project.issues_csv_uuid))
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_custom_fields_csv_generation():
|
|
project = f.ProjectFactory.create(issues_csv_uuid=uuid.uuid4().hex)
|
|
attr = f.IssueCustomAttributeFactory.create(project=project, name="attr1", description="desc")
|
|
issue = f.IssueFactory.create(project=project)
|
|
attr_values = issue.custom_attributes_values
|
|
attr_values.attributes_values = {str(attr.id):"val1"}
|
|
attr_values.save()
|
|
queryset = project.issues.all()
|
|
data = services.issues_to_csv(project, queryset)
|
|
data.seek(0)
|
|
reader = csv.reader(data)
|
|
row = next(reader)
|
|
|
|
assert row[27] == attr.name
|
|
row = next(reader)
|
|
assert row[27] == "val1"
|
|
|
|
|
|
def test_api_validator_assigned_to_when_update_issues(client):
|
|
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
|
|
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
|
|
project_member_owner = f.MembershipFactory.create(project=project,
|
|
user=project.owner,
|
|
is_admin=True,
|
|
role__project=project,
|
|
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
|
|
project_member = f.MembershipFactory.create(project=project,
|
|
is_admin=True,
|
|
role__project=project,
|
|
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
|
|
project_no_member = f.MembershipFactory.create(is_admin=True)
|
|
|
|
issue = f.create_issue(project=project, owner=project.owner)
|
|
|
|
url = reverse('issues-detail', kwargs={"pk": issue.pk})
|
|
|
|
# assign
|
|
data = {
|
|
"assigned_to": project_member.user.id,
|
|
}
|
|
|
|
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
|
|
client.login(project.owner)
|
|
|
|
response = client.json.patch(url, json.dumps(data))
|
|
assert response.status_code == 200, response.data
|
|
assert "assigned_to" in response.data
|
|
assert response.data["assigned_to"] == project_member.user.id
|
|
|
|
# unassign
|
|
data = {
|
|
"assigned_to": None,
|
|
}
|
|
|
|
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
|
|
client.login(project.owner)
|
|
|
|
response = client.json.patch(url, json.dumps(data))
|
|
assert response.status_code == 200, response.data
|
|
assert "assigned_to" in response.data
|
|
assert response.data["assigned_to"] == None
|
|
|
|
# assign to invalid user
|
|
data = {
|
|
"assigned_to": project_no_member.user.id,
|
|
}
|
|
|
|
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
|
|
client.login(project.owner)
|
|
|
|
response = client.json.patch(url, json.dumps(data))
|
|
assert response.status_code == 400, response.data
|
|
|
|
|
|
def test_api_validator_assigned_to_when_create_issues(client):
|
|
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
|
|
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
|
|
project_member_owner = f.MembershipFactory.create(project=project,
|
|
user=project.owner,
|
|
is_admin=True,
|
|
role__project=project,
|
|
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
|
|
project_member = f.MembershipFactory.create(project=project,
|
|
is_admin=True,
|
|
role__project=project,
|
|
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
|
|
project_no_member = f.MembershipFactory.create(is_admin=True)
|
|
|
|
url = reverse('issues-list')
|
|
|
|
# assign
|
|
data = {
|
|
"subject": "test",
|
|
"project": project.id,
|
|
"assigned_to": project_member.user.id,
|
|
}
|
|
|
|
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
|
|
client.login(project.owner)
|
|
|
|
response = client.json.post(url, json.dumps(data))
|
|
assert response.status_code == 201, response.data
|
|
assert "assigned_to" in response.data
|
|
assert response.data["assigned_to"] == project_member.user.id
|
|
|
|
# unassign
|
|
data = {
|
|
"subject": "test",
|
|
"project": project.id,
|
|
"assigned_to": None,
|
|
}
|
|
|
|
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
|
|
client.login(project.owner)
|
|
|
|
response = client.json.post(url, json.dumps(data))
|
|
assert response.status_code == 201, response.data
|
|
assert "assigned_to" in response.data
|
|
assert response.data["assigned_to"] == None
|
|
|
|
# assign to invalid user
|
|
data = {
|
|
"subject": "test",
|
|
"project": project.id,
|
|
"assigned_to": project_no_member.user.id,
|
|
}
|
|
|
|
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
|
|
client.login(project.owner)
|
|
|
|
response = client.json.post(url, json.dumps(data))
|
|
assert response.status_code == 400, response.data
|
|
|
|
|
|
def test_create_issue_in_milestone(client):
|
|
user = f.UserFactory.create()
|
|
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
|
project.default_issue_status = f.IssueStatusFactory.create(project=project)
|
|
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
|
milestone = f.MilestoneFactory(project=project)
|
|
project.save()
|
|
|
|
url = reverse("issues-list")
|
|
|
|
data = {"subject": "Test issue with milestone", "project": project.id,
|
|
"milestone": milestone.id}
|
|
client.login(user)
|
|
response = client.json.post(url, json.dumps(data))
|
|
assert response.status_code == 201
|
|
assert response.data['status'] == project.default_issue_status.id
|
|
assert response.data['project'] == project.id
|
|
assert response.data['milestone'] == milestone.id
|
|
|
|
|
|
def test_api_create_in_bulk_with_status_milestone(client):
|
|
user = f.UserFactory.create()
|
|
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
|
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
|
|
|
project.default_issue_status = f.IssueStatusFactory.create(project=project)
|
|
project.save()
|
|
milestone = f.MilestoneFactory(project=project)
|
|
|
|
url = reverse("issues-bulk-create")
|
|
data = {
|
|
"bulk_issues": "Issue #1\nIssue #2",
|
|
"project_id": project.id,
|
|
"milestone_id": milestone.id,
|
|
"status_id": project.default_issue_status.id
|
|
}
|
|
|
|
client.login(user)
|
|
response = client.json.post(url, json.dumps(data))
|
|
|
|
assert response.status_code == 200
|
|
assert response.data[0]["status"] == project.default_issue_status.id
|
|
assert response.data[0]["milestone"] == milestone.id
|
|
|
|
|
|
def test_api_update_milestone_in_bulk(client):
|
|
project = f.create_project()
|
|
f.MembershipFactory.create(project=project, user=project.owner, is_admin=True)
|
|
|
|
milestone1 = f.MilestoneFactory(project=project)
|
|
milestone2 = f.MilestoneFactory(project=project)
|
|
|
|
i1 = f.create_issue(project=project, milestone=milestone1)
|
|
i2 = f.create_issue(project=project, milestone=milestone1)
|
|
i3 = f.create_issue(project=project, milestone=milestone1)
|
|
|
|
assert project.milestones.get(id=milestone1.id).issues.count() == 3
|
|
|
|
url = reverse("issues-bulk-update-milestone")
|
|
data = {
|
|
"project_id": project.id,
|
|
"milestone_id": milestone2.id,
|
|
"bulk_issues": [
|
|
{"issue_id": i1.id},
|
|
{"issue_id": i2.id},
|
|
{"issue_id": i3.id}
|
|
]
|
|
}
|
|
|
|
client.login(project.owner)
|
|
|
|
response = client.json.post(url, json.dumps(data))
|
|
|
|
assert response.status_code == 200, response.data
|
|
assert response.data[i1.id] == milestone2.id
|
|
assert response.data[i2.id] == milestone2.id
|
|
assert response.data[i3.id] == milestone2.id
|
|
|
|
|
|
def test_api_update_milestone_in_bulk_invalid_milestone(client):
|
|
project = f.create_project()
|
|
f.MembershipFactory.create(project=project, user=project.owner, is_admin=True)
|
|
|
|
milestone1 = f.MilestoneFactory(project=project)
|
|
milestone2 = f.MilestoneFactory()
|
|
|
|
i1 = f.create_issue(project=project, milestone=milestone1)
|
|
i2 = f.create_issue(project=project, milestone=milestone1)
|
|
i3 = f.create_issue(project=project, milestone=milestone1)
|
|
|
|
url = reverse("issues-bulk-update-milestone")
|
|
data = {
|
|
"project_id": project.id,
|
|
"milestone_id": milestone2.id,
|
|
"bulk_issues": [
|
|
{"issue_id": i1.id},
|
|
{"issue_id": i2.id},
|
|
{"issue_id": i3.id}
|
|
]
|
|
}
|
|
|
|
client.login(project.owner)
|
|
response = client.json.post(url, json.dumps(data))
|
|
|
|
assert response.status_code == 400
|
|
assert "milestone_id" in response.data
|
|
|
|
|
|
def test_get_issues(client):
|
|
user = f.UserFactory.create()
|
|
project = f.ProjectFactory.create(owner=user)
|
|
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
|
|
|
f.IssueFactory.create(project=project)
|
|
url = reverse("issues-list")
|
|
|
|
client.login(project.owner)
|
|
|
|
response = client.get(url)
|
|
|
|
assert response.status_code == 200
|
|
assert response.data[0].get("milestone")
|
|
|
|
|
|
def test_get_issues_in_milestone(client):
|
|
user = f.UserFactory.create()
|
|
project = f.ProjectFactory.create(owner=user)
|
|
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
|
project.save()
|
|
milestone = f.MilestoneFactory(project=project)
|
|
f.IssueFactory.create(project=project, milestone=milestone)
|
|
f.IssueFactory.create(project=project)
|
|
url = reverse("issues-list") + "?milestone={}".format(milestone.id)
|
|
|
|
client.login(project.owner)
|
|
response = client.get(url)
|
|
|
|
assert response.status_code == 200
|
|
assert len(response.data) == 1
|
|
assert response.data[0].get("milestone") == milestone.id
|