Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
|
120442206b | |
|
d6d3f8c6a8 | |
|
3cebad87eb | |
|
f45f5ae08a | |
|
0434e8b78b | |
|
0c0e09819a | |
|
2b38fefa13 | |
|
7c5ba16d24 | |
|
b0d065167c | |
|
4bb12d73d9 | |
|
e736846562 | |
|
2bdd652ea7 | |
|
483d3ffd5f | |
|
2d77f8974b | |
|
77fa09a953 |
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -2,6 +2,17 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 4.1.0 (2019-02-04)
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
|
||||||
|
- Fix Close sprints
|
||||||
|
|
||||||
|
### Features:
|
||||||
|
|
||||||
|
- Negative filters
|
||||||
|
- Activate the Ukrainian language
|
||||||
|
|
||||||
## 4.0.4 (2019-01-15)
|
## 4.0.4 (2019-01-15)
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
|
@ -148,7 +148,7 @@ LANGUAGES = [
|
||||||
("tr", "Türkçe"), # Turkish
|
("tr", "Türkçe"), # Turkish
|
||||||
#("tt", "татар теле"), # Tatar
|
#("tt", "татар теле"), # Tatar
|
||||||
#("udm", "удмурт кыл"), # Udmurt
|
#("udm", "удмурт кыл"), # Udmurt
|
||||||
#("uk", "Українська"), # Ukrainian
|
("uk", "Українська"), # Ukrainian
|
||||||
#("ur", "اردو"), # Urdu
|
#("ur", "اردو"), # Urdu
|
||||||
#("vi", "Tiếng Việt"), # Vietnamese
|
#("vi", "Tiếng Việt"), # Vietnamese
|
||||||
("zh-hans", "中文(简体)"), # Simplified Chinese
|
("zh-hans", "中文(简体)"), # Simplified Chinese
|
||||||
|
|
|
@ -375,14 +375,18 @@ class IsProjectAdminFromWebhookLogFilterBackend(FilterBackend, BaseIsProjectAdmi
|
||||||
class BaseRelatedFieldsFilter(FilterBackend):
|
class BaseRelatedFieldsFilter(FilterBackend):
|
||||||
filter_name = None
|
filter_name = None
|
||||||
param_name = None
|
param_name = None
|
||||||
|
exclude_param_name = None
|
||||||
|
|
||||||
def __init__(self, filter_name=None, param_name=None):
|
def __init__(self, filter_name=None, param_name=None, exclude_param_name=None):
|
||||||
if filter_name:
|
if filter_name:
|
||||||
self.filter_name = filter_name
|
self.filter_name = filter_name
|
||||||
|
|
||||||
if param_name:
|
if param_name:
|
||||||
self.param_name = param_name
|
self.param_name = param_name
|
||||||
|
|
||||||
|
if exclude_param_name:
|
||||||
|
self.exclude_param_name
|
||||||
|
|
||||||
def _prepare_filter_data(self, query_param_value):
|
def _prepare_filter_data(self, query_param_value):
|
||||||
def _transform_value(value):
|
def _transform_value(value):
|
||||||
try:
|
try:
|
||||||
|
@ -396,48 +400,57 @@ class BaseRelatedFieldsFilter(FilterBackend):
|
||||||
values = map(_transform_value, values)
|
values = map(_transform_value, values)
|
||||||
return list(values)
|
return list(values)
|
||||||
|
|
||||||
def _get_queryparams(self, params):
|
def _get_queryparams(self, params, mode=''):
|
||||||
param_name = self.param_name or self.filter_name
|
param_name = self.exclude_param_name if mode == 'exclude' else self.param_name or self.filter_name
|
||||||
raw_value = params.get(param_name, None)
|
raw_value = params.get(param_name, None)
|
||||||
|
|
||||||
if raw_value:
|
if raw_value:
|
||||||
value = self._prepare_filter_data(raw_value)
|
value = self._prepare_filter_data(raw_value)
|
||||||
|
|
||||||
if None in value:
|
if None in value:
|
||||||
qs_in_kwargs = {"{}__in".format(self.filter_name): [v for v in value if v is not None]}
|
qs_in_kwargs = {"{}__in".format(self.filter_name): [v for v in value if v is not None]}
|
||||||
qs_isnull_kwargs = {"{}__isnull".format(self.filter_name): True}
|
qs_isnull_kwargs = {"{}__isnull".format(self.filter_name): True}
|
||||||
return Q(**qs_in_kwargs) | Q(**qs_isnull_kwargs)
|
return Q(**qs_in_kwargs) | Q(**qs_isnull_kwargs)
|
||||||
else:
|
else:
|
||||||
return {"{}__in".format(self.filter_name): value}
|
return Q(**{"{}__in".format(self.filter_name): value})
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _prepare_filter_query(self, query):
|
||||||
|
return query
|
||||||
|
|
||||||
|
def _prepare_exclude_query(self, query):
|
||||||
|
return ~Q(query)
|
||||||
|
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
query = self._get_queryparams(request.QUERY_PARAMS)
|
operations = {
|
||||||
if query:
|
"filter": self._prepare_filter_query,
|
||||||
if isinstance(query, dict):
|
"exclude": self._prepare_exclude_query,
|
||||||
queryset = queryset.filter(**query)
|
}
|
||||||
else:
|
|
||||||
queryset = queryset.filter(query)
|
for mode, prepare_method in operations.items():
|
||||||
|
query = self._get_queryparams(request.QUERY_PARAMS, mode=mode)
|
||||||
|
if query:
|
||||||
|
queryset = queryset.filter(prepare_method(query))
|
||||||
|
|
||||||
return super().filter_queryset(request, queryset, view)
|
return super().filter_queryset(request, queryset, view)
|
||||||
|
|
||||||
|
|
||||||
class OwnersFilter(BaseRelatedFieldsFilter):
|
class OwnersFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = 'owner'
|
filter_name = 'owner'
|
||||||
|
exclude_param_name = 'exclude_owner'
|
||||||
|
|
||||||
|
|
||||||
class AssignedToFilter(BaseRelatedFieldsFilter):
|
class AssignedToFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = 'assigned_to'
|
filter_name = 'assigned_to'
|
||||||
|
exclude_param_name = 'exclude_assigned_to'
|
||||||
|
|
||||||
|
|
||||||
class AssignedUsersFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter):
|
class AssignedUsersFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter):
|
||||||
filter_name = 'assigned_users'
|
filter_name = 'assigned_users'
|
||||||
|
exclude_param_name = 'exclude_assigned_users'
|
||||||
|
|
||||||
def _get_queryparams(self, params):
|
def _get_queryparams(self, params, mode=''):
|
||||||
param_name = self.param_name or self.filter_name
|
param_name = self.exclude_param_name if mode == 'exclude' else self.param_name or self.filter_name
|
||||||
raw_value = params.get(param_name, None)
|
raw_value = params.get(param_name, None)
|
||||||
|
|
||||||
if raw_value:
|
if raw_value:
|
||||||
value = self._prepare_filter_data(raw_value)
|
value = self._prepare_filter_data(raw_value)
|
||||||
UserStoryModel = apps.get_model("userstories", "UserStory")
|
UserStoryModel = apps.get_model("userstories", "UserStory")
|
||||||
|
@ -461,38 +474,65 @@ class AssignedUsersFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter):
|
||||||
|
|
||||||
class StatusesFilter(BaseRelatedFieldsFilter):
|
class StatusesFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = 'status'
|
filter_name = 'status'
|
||||||
|
exclude_param_name = 'exclude_status'
|
||||||
|
|
||||||
|
|
||||||
class IssueTypesFilter(BaseRelatedFieldsFilter):
|
class IssueTypesFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = 'type'
|
filter_name = 'type'
|
||||||
|
param_name = 'type'
|
||||||
|
exclude_param_name = 'exclude_type'
|
||||||
|
|
||||||
|
|
||||||
class PrioritiesFilter(BaseRelatedFieldsFilter):
|
class PrioritiesFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = 'priority'
|
filter_name = 'priority'
|
||||||
|
exclude_param_name = 'exclude_priority'
|
||||||
|
|
||||||
|
|
||||||
class SeveritiesFilter(BaseRelatedFieldsFilter):
|
class SeveritiesFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = 'severity'
|
filter_name = 'severity'
|
||||||
|
exclude_param_name = 'exclude_severity'
|
||||||
|
|
||||||
|
|
||||||
class TagsFilter(FilterBackend):
|
class TagsFilter(FilterBackend):
|
||||||
filter_name = 'tags'
|
filter_name = 'tags'
|
||||||
|
exclude_param_name = 'exclude_tags'
|
||||||
|
|
||||||
def __init__(self, filter_name=None):
|
def __init__(self, filter_name=None, exclude_param_name=None):
|
||||||
if filter_name:
|
if filter_name:
|
||||||
self.filter_name = filter_name
|
self.filter_name = filter_name
|
||||||
|
|
||||||
def _get_tags_queryparams(self, params):
|
if exclude_param_name:
|
||||||
tags = params.get(self.filter_name, None)
|
self.exclude_param_name = exclude_param_name
|
||||||
|
|
||||||
|
def _get_tags_queryparams(self, params, mode=''):
|
||||||
|
param_name = self.exclude_param_name if mode == "exclude" else self.filter_name
|
||||||
|
tags = params.get(param_name, None)
|
||||||
if tags:
|
if tags:
|
||||||
return tags.split(",")
|
return tags.split(",")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _prepare_filter_query(self, query):
|
||||||
|
return Q(tags__contains=query)
|
||||||
|
|
||||||
|
def _prepare_exclude_query(self, tags):
|
||||||
|
queries = [Q(tags__contains=[tag]) for tag in tags]
|
||||||
|
query = queries.pop()
|
||||||
|
for item in queries:
|
||||||
|
query |= item
|
||||||
|
|
||||||
|
return ~Q(query)
|
||||||
|
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
query_tags = self._get_tags_queryparams(request.QUERY_PARAMS)
|
operations = {
|
||||||
if query_tags:
|
"filter": self._prepare_filter_query,
|
||||||
queryset = queryset.filter(tags__contains=query_tags)
|
"exclude": self._prepare_exclude_query,
|
||||||
|
}
|
||||||
|
|
||||||
|
for mode, prepare_method in operations.items():
|
||||||
|
query = self._get_tags_queryparams(request.QUERY_PARAMS, mode=mode)
|
||||||
|
if query:
|
||||||
|
queryset = queryset.filter(prepare_method(query))
|
||||||
|
|
||||||
return super().filter_queryset(request, queryset, view)
|
return super().filter_queryset(request, queryset, view)
|
||||||
|
|
||||||
|
@ -631,18 +671,22 @@ class QFilter(FilterBackend):
|
||||||
class RoleFilter(BaseRelatedFieldsFilter):
|
class RoleFilter(BaseRelatedFieldsFilter):
|
||||||
filter_name = "role_id"
|
filter_name = "role_id"
|
||||||
param_name = "role"
|
param_name = "role"
|
||||||
|
exclude_param_name = "exclude_role"
|
||||||
|
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
Membership = apps.get_model('projects', 'Membership')
|
Membership = apps.get_model('projects', 'Membership')
|
||||||
query = self._get_queryparams(request.QUERY_PARAMS)
|
|
||||||
if query:
|
operations = {
|
||||||
if isinstance(query, dict):
|
"filter": self._prepare_filter_query,
|
||||||
memberships = Membership.objects.filter(**query).values_list("user_id", flat=True)
|
"exclude": self._prepare_exclude_query,
|
||||||
queryset = queryset.filter(assigned_to__in=memberships)
|
}
|
||||||
else:
|
|
||||||
memberships = Membership.objects.filter(query).values_list("user_id", flat=True)
|
for mode, qs_method in operations.items():
|
||||||
if memberships:
|
query = self._get_queryparams(request.QUERY_PARAMS, mode=mode)
|
||||||
queryset = queryset.filter(assigned_to__in=memberships)
|
if query:
|
||||||
|
memberships = Membership.objects.filter(query).exclude(user__isnull=True).values_list("user_id", flat=True)
|
||||||
|
if memberships:
|
||||||
|
queryset = queryset.filter(qs_method(Q(assigned_to__in=memberships)))
|
||||||
|
|
||||||
return FilterBackend.filter_queryset(self, request, queryset, view)
|
return FilterBackend.filter_queryset(self, request, queryset, view)
|
||||||
|
|
||||||
|
@ -650,20 +694,24 @@ class RoleFilter(BaseRelatedFieldsFilter):
|
||||||
class UserStoriesRoleFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter):
|
class UserStoriesRoleFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter):
|
||||||
filter_name = "role_id"
|
filter_name = "role_id"
|
||||||
param_name = "role"
|
param_name = "role"
|
||||||
|
exclude_param_name = 'exclude_role'
|
||||||
|
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
Membership = apps.get_model('projects', 'Membership')
|
Membership = apps.get_model('projects', 'Membership')
|
||||||
query = self._get_queryparams(request.QUERY_PARAMS)
|
|
||||||
|
|
||||||
if query:
|
operations = {
|
||||||
if isinstance(query, dict):
|
"filter": self._prepare_filter_query,
|
||||||
memberships = Membership.objects.filter(**query).values_list("user_id", flat=True)
|
"exclude": self._prepare_exclude_query,
|
||||||
else:
|
}
|
||||||
memberships = Membership.objects.filter(query).values_list("user_id", flat=True)
|
|
||||||
if memberships:
|
for mode, qs_method in operations.items():
|
||||||
user_story_model = apps.get_model("userstories", "UserStory")
|
query = self._get_queryparams(request.QUERY_PARAMS, mode=mode)
|
||||||
queryset = queryset.filter(
|
if query:
|
||||||
self.get_assigned_users_filter(user_story_model, memberships)
|
memberships = Membership.objects.filter(query).exclude(user__isnull=True).values_list("user_id", flat=True)
|
||||||
)
|
if memberships:
|
||||||
|
user_story_model = apps.get_model("userstories", "UserStory")
|
||||||
|
queryset = queryset.filter(
|
||||||
|
qs_method(Q(self.get_assigned_users_filter(user_story_model, memberships)))
|
||||||
|
)
|
||||||
|
|
||||||
return FilterBackend.filter_queryset(self, request, queryset, view)
|
return FilterBackend.filter_queryset(self, request, queryset, view)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,18 +26,17 @@ from taiga.projects.userstories.models import UserStory
|
||||||
|
|
||||||
|
|
||||||
def calculate_milestone_is_closed(milestone):
|
def calculate_milestone_is_closed(milestone):
|
||||||
uss_check = milestone.user_stories.all().count() > 0 and all(
|
all_us_closed = all([user_story.is_closed for user_story in milestone.user_stories.all()])
|
||||||
[task.status is not None and task.status.is_closed for task in
|
all_tasks_closed = all([task.status is not None and task.status.is_closed for task in
|
||||||
milestone.tasks.all()]) and all(
|
milestone.tasks.all()])
|
||||||
[user_story.is_closed for user_story in milestone.user_stories.all()])
|
all_issues_closed = all([issue.is_closed for issue in milestone.issues.all()])
|
||||||
|
|
||||||
issues_check = milestone.issues.all().count() > 0 and all(
|
uss_check = milestone.user_stories.all().count() > 0 \
|
||||||
[issue.is_closed for issue in milestone.issues.all()]) and all(
|
and all_tasks_closed and all_us_closed and all_issues_closed
|
||||||
[task.is_closed for task in milestone.tasks.all()])
|
issues_check = milestone.issues.all().count() > 0 and all_issues_closed \
|
||||||
|
and all_tasks_closed and all_us_closed
|
||||||
tasks_check = milestone.tasks.all().count() > 0 and all(
|
tasks_check = milestone.tasks.all().count() > 0 and all_tasks_closed \
|
||||||
[task.is_closed for task in milestone.tasks.all()]) and all(
|
and all_issues_closed and all_us_closed
|
||||||
[issue.is_closed for issue in milestone.issues.all()])
|
|
||||||
|
|
||||||
return uss_check or issues_check or tasks_check
|
return uss_check or issues_check or tasks_check
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,10 @@ from .models import HistoryChangeNotification, Watched
|
||||||
from .squashing import squash_history_entries
|
from .squashing import squash_history_entries
|
||||||
|
|
||||||
|
|
||||||
|
def remove_lr_cr(s):
|
||||||
|
return s.replace("\n", "").replace("\r", "")
|
||||||
|
|
||||||
|
|
||||||
def notify_policy_exists(project, user) -> bool:
|
def notify_policy_exists(project, user) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if policy exists for specified project
|
Check if policy exists for specified project
|
||||||
|
@ -313,10 +317,11 @@ def send_sync_notifications(notification_id):
|
||||||
msg_id = 'taiga-system'
|
msg_id = 'taiga-system'
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
project_name = remove_lr_cr(notification.project.name)
|
||||||
format_args = {
|
format_args = {
|
||||||
"unsubscribe_url": resolve_front_url('settings-mail-notifications'),
|
"unsubscribe_url": resolve_front_url('settings-mail-notifications'),
|
||||||
"project_slug": notification.project.slug,
|
"project_slug": notification.project.slug,
|
||||||
"project_name": notification.project.name,
|
"project_name": project_name,
|
||||||
"msg_id": msg_id,
|
"msg_id": msg_id,
|
||||||
"time": int(now.timestamp()),
|
"time": int(now.timestamp()),
|
||||||
"domain": domain
|
"domain": domain
|
||||||
|
|
|
@ -39,6 +39,78 @@ import pytest
|
||||||
pytestmark = pytest.mark.django_db
|
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():
|
def test_get_issues_from_bulk():
|
||||||
data = """
|
data = """
|
||||||
Issue #1
|
Issue #1
|
||||||
|
@ -370,86 +442,63 @@ def test_api_filter_by_finished_date(client):
|
||||||
assert response.data[0]["ref"] == finished_issue.ref
|
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):
|
def test_api_filters_data(client):
|
||||||
project = f.ProjectFactory.create()
|
data = create_filter_issues_context()
|
||||||
user1 = f.UserFactory.create(is_superuser=True)
|
project = data["project"]
|
||||||
f.MembershipFactory.create(user=user1, project=project)
|
(user1, user2, user3, ) = data["users"]
|
||||||
user2 = f.UserFactory.create(is_superuser=True)
|
(status0, status1, status2, status3, ) = data["statuses"]
|
||||||
f.MembershipFactory.create(user=user2, project=project)
|
(type1, type2, ) = data["types"]
|
||||||
user3 = f.UserFactory.create(is_superuser=True)
|
(priority0, priority1, priority2, priority3, ) = data["priorities"]
|
||||||
f.MembershipFactory.create(user=user3, project=project)
|
(severity0, severity1, severity2, severity3, ) = data["severities"]
|
||||||
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
||||||
status0 = f.IssueStatusFactory.create(project=project)
|
|
||||||
status1 = f.IssueStatusFactory.create(project=project)
|
|
||||||
status2 = f.IssueStatusFactory.create(project=project)
|
|
||||||
status3 = f.IssueStatusFactory.create(project=project)
|
|
||||||
|
|
||||||
type1 = f.IssueTypeFactory.create(project=project)
|
|
||||||
type2 = f.IssueTypeFactory.create(project=project)
|
|
||||||
|
|
||||||
severity0 = f.SeverityFactory.create(project=project)
|
|
||||||
severity1 = f.SeverityFactory.create(project=project)
|
|
||||||
severity2 = f.SeverityFactory.create(project=project)
|
|
||||||
severity3 = f.SeverityFactory.create(project=project)
|
|
||||||
|
|
||||||
priority0 = f.PriorityFactory.create(project=project)
|
|
||||||
priority1 = f.PriorityFactory.create(project=project)
|
|
||||||
priority2 = f.PriorityFactory.create(project=project)
|
|
||||||
priority3 = f.PriorityFactory.create(project=project)
|
|
||||||
|
|
||||||
tag0 = "test1test2test3"
|
|
||||||
tag1 = "test1"
|
|
||||||
tag2 = "test2"
|
|
||||||
tag3 = "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 |
|
|
||||||
# ------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
issue0 = f.IssueFactory.create(project=project, owner=user2, assigned_to=None,
|
|
||||||
status=status3, type=type1, priority=priority2, severity=severity1,
|
|
||||||
tags=[tag1])
|
|
||||||
issue1 = f.IssueFactory.create(project=project, owner=user1, assigned_to=None,
|
|
||||||
status=status3, type=type2, priority=priority2, severity=severity1,
|
|
||||||
tags=[tag2])
|
|
||||||
issue2 = f.IssueFactory.create(project=project, owner=user3, assigned_to=None,
|
|
||||||
status=status1, type=type1, priority=priority3, severity=severity2,
|
|
||||||
tags=[tag1, tag2])
|
|
||||||
issue3 = f.IssueFactory.create(project=project, owner=user2, assigned_to=None,
|
|
||||||
status=status0, type=type2, priority=priority3, severity=severity1,
|
|
||||||
tags=[tag3])
|
|
||||||
issue4 = f.IssueFactory.create(project=project, owner=user1, assigned_to=user1,
|
|
||||||
status=status0, type=type1, priority=priority2, severity=severity3,
|
|
||||||
tags=[tag1, tag2, tag3])
|
|
||||||
issue5 = f.IssueFactory.create(project=project, owner=user3, assigned_to=user1,
|
|
||||||
status=status2, type=type2, priority=priority3, severity=severity2,
|
|
||||||
tags=[tag3])
|
|
||||||
issue6 = f.IssueFactory.create(project=project, owner=user2, assigned_to=user1,
|
|
||||||
status=status3, type=type1, priority=priority2, severity=severity0,
|
|
||||||
tags=[tag1, tag2])
|
|
||||||
issue7 = f.IssueFactory.create(project=project, owner=user1, assigned_to=user2,
|
|
||||||
status=status0, type=type2, priority=priority1, severity=severity3,
|
|
||||||
tags=[tag3])
|
|
||||||
issue8 = f.IssueFactory.create(project=project, owner=user3, assigned_to=user2,
|
|
||||||
status=status3, type=type1, priority=priority0, severity=severity1,
|
|
||||||
tags=[tag1])
|
|
||||||
issue9 = f.IssueFactory.create(project=project, owner=user2, assigned_to=user3,
|
|
||||||
status=status1, type=type2, priority=priority0, severity=severity2,
|
|
||||||
tags=[tag0])
|
|
||||||
|
|
||||||
url = reverse("issues-filters-data") + "?project={}".format(project.id)
|
url = reverse("issues-filters-data") + "?project={}".format(project.id)
|
||||||
|
|
||||||
client.login(user1)
|
client.login(user1)
|
||||||
|
|
||||||
## No filter
|
## No filter
|
||||||
|
|
|
@ -307,8 +307,9 @@ def test_api_move_tasks_to_another_sprint_close_previous(client):
|
||||||
closed_status = f.TaskStatusFactory.create(project=project, is_closed=True)
|
closed_status = f.TaskStatusFactory.create(project=project, is_closed=True)
|
||||||
|
|
||||||
task1 = f.create_task(project=project, milestone=milestone1, taskboard_order=1,
|
task1 = f.create_task(project=project, milestone=milestone1, taskboard_order=1,
|
||||||
status=closed_status)
|
status=closed_status, user_story=None)
|
||||||
task2 = f.create_task(project=project, milestone=milestone1, taskboard_order=2)
|
task2 = f.create_task(project=project, milestone=milestone1, taskboard_order=2,
|
||||||
|
user_story=None)
|
||||||
|
|
||||||
assert project.milestones.get(id=milestone1.id).tasks.count() == 2
|
assert project.milestones.get(id=milestone1.id).tasks.count() == 2
|
||||||
assert not milestone1.closed
|
assert not milestone1.closed
|
||||||
|
|
|
@ -55,6 +55,20 @@ def mail():
|
||||||
return mail
|
return mail
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"header, expected",
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("One line", "One line"),
|
||||||
|
("Two \nlines", "Two lines"),
|
||||||
|
("Mix \r\nCR and LF \rin the string", "Mix CR and LF in the string"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_remove_lr_cr(header, expected):
|
||||||
|
rv = services.remove_lr_cr(header)
|
||||||
|
assert rv == expected
|
||||||
|
|
||||||
|
|
||||||
def test_create_retrieve_notify_policy():
|
def test_create_retrieve_notify_policy():
|
||||||
project = f.ProjectFactory.create()
|
project = f.ProjectFactory.create()
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,62 @@ import pytest
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
def create_tasks_fixtures():
|
||||||
|
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.TaskStatusFactory.create(project=project) for i in range(0, 4)]
|
||||||
|
data["tags"] = ["test1test2test3", "test1", "test2", "test3"]
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# | Task | Owner | Assigned To | Tags | Status |
|
||||||
|
# |-------#--------#-------------#---------------------|---------|
|
||||||
|
# | 0 | user2 | None | tag1 | status3 |
|
||||||
|
# | 1 | user1 | None | tag2 | status3 |
|
||||||
|
# | 2 | user3 | None | tag1 tag2 | status1 |
|
||||||
|
# | 3 | user2 | None | tag3 | status0 |
|
||||||
|
# | 4 | user1 | user1 | tag1 tag2 tag3 | status0 |
|
||||||
|
# | 5 | user3 | user1 | tag3 | status2 |
|
||||||
|
# | 6 | user2 | user1 | tag1 tag2 | status3 |
|
||||||
|
# | 7 | user1 | user2 | tag3 | status0 |
|
||||||
|
# | 8 | user3 | user2 | tag1 | status3 |
|
||||||
|
# | 9 | user2 | user3 | tag0 | status1 |
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
(user1, user2, user3, ) = data["users"]
|
||||||
|
(status0, status1, status2, status3 ) = data["statuses"]
|
||||||
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
||||||
|
|
||||||
|
f.TaskFactory.create(project=project, owner=user2, assigned_to=None,
|
||||||
|
status=status3, tags=[tag1])
|
||||||
|
f.TaskFactory.create(project=project, owner=user1, assigned_to=None,
|
||||||
|
status=status3, tags=[tag2])
|
||||||
|
f.TaskFactory.create(project=project, owner=user3, assigned_to=None,
|
||||||
|
status=status1, tags=[tag1, tag2])
|
||||||
|
f.TaskFactory.create(project=project, owner=user2, assigned_to=None,
|
||||||
|
status=status0, tags=[tag3])
|
||||||
|
f.TaskFactory.create(project=project, owner=user1, assigned_to=user1,
|
||||||
|
status=status0, tags=[tag1, tag2, tag3])
|
||||||
|
f.TaskFactory.create(project=project, owner=user3, assigned_to=user1,
|
||||||
|
status=status2, tags=[tag3])
|
||||||
|
f.TaskFactory.create(project=project, owner=user2, assigned_to=user1,
|
||||||
|
status=status3, tags=[tag1, tag2])
|
||||||
|
f.TaskFactory.create(project=project, owner=user1, assigned_to=user2,
|
||||||
|
status=status0, tags=[tag3])
|
||||||
|
f.TaskFactory.create(project=project, owner=user3, assigned_to=user2,
|
||||||
|
status=status3, tags=[tag1])
|
||||||
|
f.TaskFactory.create(project=project, owner=user2, assigned_to=user3,
|
||||||
|
status=status1, tags=[tag0])
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def test_get_tasks_from_bulk():
|
def test_get_tasks_from_bulk():
|
||||||
data = """
|
data = """
|
||||||
Task #1
|
Task #1
|
||||||
|
@ -796,63 +852,45 @@ def test_api_filter_by_milestone__estimated_start_and_end(client, field_name):
|
||||||
assert response.data[0]["subject"] == task.subject
|
assert response.data[0]["subject"] == task.subject
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("filter_name,collection,expected,exclude_expected,is_text", [
|
||||||
|
('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_tasks_fixtures()
|
||||||
|
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('tasks-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('tasks-list'), project.id, filter_name, param)
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert len(response.data) == exclude_expected
|
||||||
|
|
||||||
|
|
||||||
def test_api_filters_data(client):
|
def test_api_filters_data(client):
|
||||||
project = f.ProjectFactory.create()
|
data = create_tasks_fixtures()
|
||||||
user1 = f.UserFactory.create(is_superuser=True)
|
project = data["project"]
|
||||||
f.MembershipFactory.create(user=user1, project=project)
|
(user1, user2, user3, ) = data["users"]
|
||||||
user2 = f.UserFactory.create(is_superuser=True)
|
(status0, status1, status2, status3, ) = data["statuses"]
|
||||||
f.MembershipFactory.create(user=user2, project=project)
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
||||||
user3 = f.UserFactory.create(is_superuser=True)
|
|
||||||
f.MembershipFactory.create(user=user3, project=project)
|
|
||||||
|
|
||||||
status0 = f.TaskStatusFactory.create(project=project)
|
|
||||||
status1 = f.TaskStatusFactory.create(project=project)
|
|
||||||
status2 = f.TaskStatusFactory.create(project=project)
|
|
||||||
status3 = f.TaskStatusFactory.create(project=project)
|
|
||||||
|
|
||||||
tag0 = "test1test2test3"
|
|
||||||
tag1 = "test1"
|
|
||||||
tag2 = "test2"
|
|
||||||
tag3 = "test3"
|
|
||||||
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# | Task | Owner | Assigned To | Tags |
|
|
||||||
# |-------#--------#-------------#---------------------|
|
|
||||||
# | 0 | user2 | None | tag1 |
|
|
||||||
# | 1 | user1 | None | tag2 |
|
|
||||||
# | 2 | user3 | None | tag1 tag2 |
|
|
||||||
# | 3 | user2 | None | tag3 |
|
|
||||||
# | 4 | user1 | user1 | tag1 tag2 tag3 |
|
|
||||||
# | 5 | user3 | user1 | tag3 |
|
|
||||||
# | 6 | user2 | user1 | tag1 tag2 |
|
|
||||||
# | 7 | user1 | user2 | tag3 |
|
|
||||||
# | 8 | user3 | user2 | tag1 |
|
|
||||||
# | 9 | user2 | user3 | tag0 |
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
task0 = f.TaskFactory.create(project=project, owner=user2, assigned_to=None,
|
|
||||||
status=status3, tags=[tag1])
|
|
||||||
task1 = f.TaskFactory.create(project=project, owner=user1, assigned_to=None,
|
|
||||||
status=status3, tags=[tag2])
|
|
||||||
task2 = f.TaskFactory.create(project=project, owner=user3, assigned_to=None,
|
|
||||||
status=status1, tags=[tag1, tag2])
|
|
||||||
task3 = f.TaskFactory.create(project=project, owner=user2, assigned_to=None,
|
|
||||||
status=status0, tags=[tag3])
|
|
||||||
task4 = f.TaskFactory.create(project=project, owner=user1, assigned_to=user1,
|
|
||||||
status=status0, tags=[tag1, tag2, tag3])
|
|
||||||
task5 = f.TaskFactory.create(project=project, owner=user3, assigned_to=user1,
|
|
||||||
status=status2, tags=[tag3])
|
|
||||||
task6 = f.TaskFactory.create(project=project, owner=user2, assigned_to=user1,
|
|
||||||
status=status3, tags=[tag1, tag2])
|
|
||||||
task7 = f.TaskFactory.create(project=project, owner=user1, assigned_to=user2,
|
|
||||||
status=status0, tags=[tag3])
|
|
||||||
task8 = f.TaskFactory.create(project=project, owner=user3, assigned_to=user2,
|
|
||||||
status=status3, tags=[tag1])
|
|
||||||
task9 = f.TaskFactory.create(project=project, owner=user2, assigned_to=user3,
|
|
||||||
status=status1, tags=[tag0])
|
|
||||||
|
|
||||||
url = reverse("tasks-filters-data") + "?project={}".format(project.id)
|
url = reverse("tasks-filters-data") + "?project={}".format(project.id)
|
||||||
|
|
||||||
client.login(user1)
|
client.login(user1)
|
||||||
|
|
||||||
## No filter
|
## No filter
|
||||||
|
|
|
@ -38,6 +38,71 @@ import pytest
|
||||||
pytestmark = pytest.mark.django_db(transaction=True)
|
pytestmark = pytest.mark.django_db(transaction=True)
|
||||||
|
|
||||||
|
|
||||||
|
def create_uss_fixtures():
|
||||||
|
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.UserStoryStatusFactory.create(project=project) for i in range(0, 4)]
|
||||||
|
data["epics"] = [f.EpicFactory.create(project=project) for i in range(0, 3)]
|
||||||
|
data["tags"] = ["test1test2test3", "test1", "test2", "test3"]
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------------------
|
||||||
|
# | US | Status | Owner | Assigned To | Assigned Users | Tags | Epic |
|
||||||
|
# |-------#---------#--------#-------------#---------------------#---------------------#--------------
|
||||||
|
# | 0 | status3 | user2 | None | None | tag1 | epic0 |
|
||||||
|
# | 1 | status3 | user1 | None | user1 | tag2 | None |
|
||||||
|
# | 2 | status1 | user3 | None | None | tag1 tag2 | epic1 |
|
||||||
|
# | 3 | status0 | user2 | None | None | tag3 | None |
|
||||||
|
# | 4 | status0 | user1 | user1 | None | tag1 tag2 tag3 | epic0 |
|
||||||
|
# | 5 | status2 | user3 | user1 | None | tag3 | None |
|
||||||
|
# | 6 | status3 | user2 | user1 | None | tag1 tag2 | epic0 epic2 |
|
||||||
|
# | 7 | status0 | user1 | user2 | None | tag3 | None |
|
||||||
|
# | 8 | status3 | user3 | user2 | None | tag1 | epic2 |
|
||||||
|
# | 9 | status1 | user2 | user3 | user1 | tag0 | None |
|
||||||
|
# ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(user1, user2, user3, ) = data["users"]
|
||||||
|
(status0, status1, status2, status3 ) = data["statuses"]
|
||||||
|
(epic0, epic1, epic2) = data["epics"]
|
||||||
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
||||||
|
|
||||||
|
us0 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None,
|
||||||
|
status=status3, tags=[tag1])
|
||||||
|
f.RelatedUserStory.create(user_story=us0, epic=epic0)
|
||||||
|
us1 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=None,
|
||||||
|
status=status3, tags=[tag2], assigned_users=[user1])
|
||||||
|
us2 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=None,
|
||||||
|
status=status1, tags=[tag1, tag2])
|
||||||
|
f.RelatedUserStory.create(user_story=us2, epic=epic1)
|
||||||
|
us3 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None,
|
||||||
|
status=status0, tags=[tag3])
|
||||||
|
us4 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user1,
|
||||||
|
status=status0, tags=[tag1, tag2, tag3])
|
||||||
|
f.RelatedUserStory.create(user_story=us4, epic=epic0)
|
||||||
|
us5 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user1,
|
||||||
|
status=status2, tags=[tag3])
|
||||||
|
us6 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user1,
|
||||||
|
status=status3, tags=[tag1, tag2])
|
||||||
|
f.RelatedUserStory.create(user_story=us6, epic=epic0)
|
||||||
|
f.RelatedUserStory.create(user_story=us6, epic=epic2)
|
||||||
|
us7 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user2,
|
||||||
|
status=status0, tags=[tag3])
|
||||||
|
us8 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user2,
|
||||||
|
status=status3, tags=[tag1])
|
||||||
|
f.RelatedUserStory.create(user_story=us8, epic=epic2)
|
||||||
|
us9 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user3,
|
||||||
|
status=status1, tags=[tag0], assigned_users=[user1])
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def test_get_userstories_from_bulk():
|
def test_get_userstories_from_bulk():
|
||||||
data = "User Story #1\nUser Story #2\n"
|
data = "User Story #1\nUser Story #2\n"
|
||||||
userstories = services.get_userstories_from_bulk(data)
|
userstories = services.get_userstories_from_bulk(data)
|
||||||
|
@ -777,72 +842,14 @@ def test_api_filter_by_milestone__estimated_start_and_end(client, field_name):
|
||||||
|
|
||||||
|
|
||||||
def test_api_filters_data(client):
|
def test_api_filters_data(client):
|
||||||
project = f.ProjectFactory.create()
|
data = create_uss_fixtures()
|
||||||
user1 = f.UserFactory.create(is_superuser=True)
|
project = data["project"]
|
||||||
f.MembershipFactory.create(user=user1, project=project)
|
(user1, user2, user3, ) = data["users"]
|
||||||
user2 = f.UserFactory.create(is_superuser=True)
|
(status0, status1, status2, status3, ) = data["statuses"]
|
||||||
f.MembershipFactory.create(user=user2, project=project)
|
(epic0, epic1, epic2, ) = data["epics"]
|
||||||
user3 = f.UserFactory.create(is_superuser=True)
|
(tag0, tag1, tag2, tag3, ) = data["tags"]
|
||||||
f.MembershipFactory.create(user=user3, project=project)
|
|
||||||
|
|
||||||
status0 = f.UserStoryStatusFactory.create(project=project)
|
|
||||||
status1 = f.UserStoryStatusFactory.create(project=project)
|
|
||||||
status2 = f.UserStoryStatusFactory.create(project=project)
|
|
||||||
status3 = f.UserStoryStatusFactory.create(project=project)
|
|
||||||
|
|
||||||
epic0 = f.EpicFactory.create(project=project)
|
|
||||||
epic1 = f.EpicFactory.create(project=project)
|
|
||||||
epic2 = f.EpicFactory.create(project=project)
|
|
||||||
|
|
||||||
tag0 = "test1test2test3"
|
|
||||||
tag1 = "test1"
|
|
||||||
tag2 = "test2"
|
|
||||||
tag3 = "test3"
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------------------------
|
|
||||||
# | US | Status | Owner | Assigned To | Assigned Users | Tags | Epic |
|
|
||||||
# |-------#---------#--------#-------------#---------------------#---------------------#--------------
|
|
||||||
# | 0 | status3 | user2 | None | None | tag1 | epic0 |
|
|
||||||
# | 1 | status3 | user1 | None | user1 | tag2 | None |
|
|
||||||
# | 2 | status1 | user3 | None | None | tag1 tag2 | epic1 |
|
|
||||||
# | 3 | status0 | user2 | None | None | tag3 | None |
|
|
||||||
# | 4 | status0 | user1 | user1 | None | tag1 tag2 tag3 | epic0 |
|
|
||||||
# | 5 | status2 | user3 | user1 | None | tag3 | None |
|
|
||||||
# | 6 | status3 | user2 | user1 | None | tag1 tag2 | epic0 epic2 |
|
|
||||||
# | 7 | status0 | user1 | user2 | None | tag3 | None |
|
|
||||||
# | 8 | status3 | user3 | user2 | None | tag1 | epic2 |
|
|
||||||
# | 9 | status1 | user2 | user3 | user1 | tag0 | None |
|
|
||||||
# ----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
us0 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None,
|
|
||||||
status=status3, tags=[tag1])
|
|
||||||
f.RelatedUserStory.create(user_story=us0, epic=epic0)
|
|
||||||
us1 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=None,
|
|
||||||
status=status3, tags=[tag2], assigned_users=[user1])
|
|
||||||
us2 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=None,
|
|
||||||
status=status1, tags=[tag1, tag2])
|
|
||||||
f.RelatedUserStory.create(user_story=us2, epic=epic1)
|
|
||||||
us3 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None,
|
|
||||||
status=status0, tags=[tag3])
|
|
||||||
us4 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user1,
|
|
||||||
status=status0, tags=[tag1, tag2, tag3])
|
|
||||||
f.RelatedUserStory.create(user_story=us4, epic=epic0)
|
|
||||||
us5 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user1,
|
|
||||||
status=status2, tags=[tag3])
|
|
||||||
us6 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user1,
|
|
||||||
status=status3, tags=[tag1, tag2])
|
|
||||||
f.RelatedUserStory.create(user_story=us6, epic=epic0)
|
|
||||||
f.RelatedUserStory.create(user_story=us6, epic=epic2)
|
|
||||||
us7 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user2,
|
|
||||||
status=status0, tags=[tag3])
|
|
||||||
us8 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user2,
|
|
||||||
status=status3, tags=[tag1])
|
|
||||||
f.RelatedUserStory.create(user_story=us8, epic=epic2)
|
|
||||||
us9 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user3,
|
|
||||||
status=status1, tags=[tag0], assigned_users=[user1])
|
|
||||||
|
|
||||||
url = reverse("userstories-filters-data") + "?project={}".format(project.id)
|
url = reverse("userstories-filters-data") + "?project={}".format(project.id)
|
||||||
|
|
||||||
client.login(user1)
|
client.login(user1)
|
||||||
|
|
||||||
# No filter
|
# No filter
|
||||||
|
@ -961,6 +968,37 @@ def test_api_filters_data(client):
|
||||||
assert next(filter(lambda i: i['id'] == epic2.id, response.data["epics"]))["count"] == 2
|
assert next(filter(lambda i: i['id'] == epic2.id, response.data["epics"]))["count"] == 2
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("filter_name,collection,expected,exclude_expected,is_text", [
|
||||||
|
('status', 'statuses', 3, 7, False),
|
||||||
|
('tags', 'tags', 1, 9, True),
|
||||||
|
('owner', 'users', 3, 7, False),
|
||||||
|
('role', 'roles', 5, 5, False),
|
||||||
|
('assigned_users', 'users', 5, 5, False),
|
||||||
|
])
|
||||||
|
def test_api_filters(client, filter_name, collection, expected, exclude_expected, is_text):
|
||||||
|
data = create_uss_fixtures()
|
||||||
|
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('userstories-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('userstories-list'), project.id, filter_name, param)
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert len(response.data) == exclude_expected
|
||||||
|
|
||||||
|
|
||||||
def test_api_filters_data_with_assigned_users(client):
|
def test_api_filters_data_with_assigned_users(client):
|
||||||
project = f.ProjectFactory.create()
|
project = f.ProjectFactory.create()
|
||||||
user1 = f.UserFactory.create(is_superuser=True)
|
user1 = f.UserFactory.create(is_superuser=True)
|
||||||
|
|
|
@ -3,7 +3,6 @@ import pytest
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
from taiga.projects.milestones import services
|
from taiga.projects.milestones import services
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,6 +50,54 @@ def test_stay_open_with_issues_but_closed_tasks():
|
||||||
assert not services.calculate_milestone_is_closed(milestone1)
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_issues_but_closed_uss():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
|
||||||
|
us_closed_status = f.UserStoryStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_userstory(project=project, milestone=milestone1,
|
||||||
|
status=us_closed_status, is_closed=True)
|
||||||
|
|
||||||
|
f.create_issue(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_closed_issues_but_open_uss():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
|
||||||
|
closed_status = f.IssueStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_issue(project=project, milestone=milestone1,
|
||||||
|
status=closed_status)
|
||||||
|
|
||||||
|
f.create_userstory(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_closed_issues_but_open_tasks():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
|
||||||
|
closed_status = f.IssueStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_issue(project=project, milestone=milestone1,
|
||||||
|
status=closed_status)
|
||||||
|
|
||||||
|
f.create_task(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
def test_tasks_not_closed():
|
def test_tasks_not_closed():
|
||||||
project = f.ProjectFactory()
|
project = f.ProjectFactory()
|
||||||
f.MembershipFactory.create(project=project, user=project.owner,
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
@ -75,7 +122,7 @@ def test_tasks_closed():
|
||||||
closed_status = f.TaskStatusFactory.create(project=project,
|
closed_status = f.TaskStatusFactory.create(project=project,
|
||||||
is_closed=True)
|
is_closed=True)
|
||||||
f.create_task(project=project, milestone=milestone1,
|
f.create_task(project=project, milestone=milestone1,
|
||||||
status=closed_status)
|
status=closed_status, user_story=None)
|
||||||
|
|
||||||
assert services.calculate_milestone_is_closed(milestone1)
|
assert services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
@ -95,6 +142,51 @@ def test_stay_open_with_tasks_but_closed_issues():
|
||||||
assert not services.calculate_milestone_is_closed(milestone1)
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_tasks_but_closed_uss():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
us_closed_status = f.UserStoryStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_userstory(project=project, milestone=milestone1,
|
||||||
|
status=us_closed_status, is_closed=True)
|
||||||
|
|
||||||
|
f.create_task(project=project, milestone=milestone1, user_story=None)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_closed_tasks_but_open_uss():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
closed_status = f.TaskStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_task(project=project, milestone=milestone1,
|
||||||
|
status=closed_status, user_story=None)
|
||||||
|
|
||||||
|
f.create_userstory(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_closed_tasks_but_open_issues():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
closed_status = f.TaskStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_task(project=project, milestone=milestone1,
|
||||||
|
status=closed_status, user_story=None)
|
||||||
|
|
||||||
|
f.create_issue(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
def test_uss_not_closed():
|
def test_uss_not_closed():
|
||||||
project = f.ProjectFactory()
|
project = f.ProjectFactory()
|
||||||
f.MembershipFactory.create(project=project, user=project.owner,
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
@ -104,7 +196,7 @@ def test_uss_not_closed():
|
||||||
closed_status = f.UserStoryStatusFactory.create(project=project,
|
closed_status = f.UserStoryStatusFactory.create(project=project,
|
||||||
is_closed=True)
|
is_closed=True)
|
||||||
f.create_userstory(project=project, milestone=milestone1,
|
f.create_userstory(project=project, milestone=milestone1,
|
||||||
status=closed_status)
|
status=closed_status, is_closed=True)
|
||||||
f.create_userstory(project=project, milestone=milestone1)
|
f.create_userstory(project=project, milestone=milestone1)
|
||||||
|
|
||||||
assert not services.calculate_milestone_is_closed(milestone1)
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
@ -124,3 +216,53 @@ def test_uss_closed():
|
||||||
|
|
||||||
assert services.calculate_milestone_is_closed(milestone1)
|
assert services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_uss_but_closed_tasks_and_us():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
|
||||||
|
us_closed_status = f.UserStoryStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
us = f.create_userstory(project=project, milestone=milestone1,
|
||||||
|
status=us_closed_status, is_closed=True)
|
||||||
|
|
||||||
|
task_closed_status = f.TaskStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_task(project=project, milestone=milestone1, user_story=us,
|
||||||
|
status=task_closed_status)
|
||||||
|
|
||||||
|
f.create_userstory(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_uss_but_closed_tasks():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
|
||||||
|
closed_status = f.TaskStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_task(project=project, milestone=milestone1,
|
||||||
|
status=closed_status, user_story=None)
|
||||||
|
f.create_userstory(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stay_open_with_uss_but_closed_issues():
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
f.MembershipFactory.create(project=project, user=project.owner,
|
||||||
|
is_admin=True)
|
||||||
|
milestone1 = f.MilestoneFactory.create(project=project)
|
||||||
|
|
||||||
|
closed_status = f.IssueStatusFactory.create(project=project,
|
||||||
|
is_closed=True)
|
||||||
|
f.create_issue(project=project, milestone=milestone1,
|
||||||
|
status=closed_status)
|
||||||
|
f.create_userstory(project=project, milestone=milestone1)
|
||||||
|
|
||||||
|
assert not services.calculate_milestone_is_closed(milestone1)
|
||||||
|
|
Loading…
Reference in New Issue