* Add timeline tests
* Improve factory * Add default to exclude fieldsremotes/origin/3.4.0rc
parent
de7ab4c474
commit
f95d9f28be
|
@ -81,7 +81,8 @@ _values_impl_map = {}
|
||||||
# this fields are marked as hidden).
|
# this fields are marked as hidden).
|
||||||
_not_important_fields = {
|
_not_important_fields = {
|
||||||
"epics.epic": frozenset(["epics_order", "user_stories"]),
|
"epics.epic": frozenset(["epics_order", "user_stories"]),
|
||||||
"userstories.userstory": frozenset(["backlog_order", "sprint_order", "kanban_order"]),
|
"userstories.userstory": frozenset(
|
||||||
|
["backlog_order", "sprint_order", "kanban_order"]),
|
||||||
"tasks.task": frozenset(["us_order", "taskboard_order"]),
|
"tasks.task": frozenset(["us_order", "taskboard_order"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +196,8 @@ def freeze_model_instance(obj: object) -> FrozenObj:
|
||||||
key = make_key_from_model_object(obj)
|
key = make_key_from_model_object(obj)
|
||||||
impl_fn = _freeze_impl_map[typename]
|
impl_fn = _freeze_impl_map[typename]
|
||||||
snapshot = impl_fn(obj)
|
snapshot = impl_fn(obj)
|
||||||
assert isinstance(snapshot, dict), "freeze handlers should return always a dict"
|
assert isinstance(snapshot, dict), \
|
||||||
|
"freeze handlers should return always a dict"
|
||||||
|
|
||||||
return FrozenObj(key, snapshot)
|
return FrozenObj(key, snapshot)
|
||||||
|
|
||||||
|
@ -224,7 +226,7 @@ def get_excluded_fields(typename: str) -> tuple:
|
||||||
"""
|
"""
|
||||||
Get excluded and deprected fields to avoid in the diff
|
Get excluded and deprected fields to avoid in the diff
|
||||||
"""
|
"""
|
||||||
return _deprecated_fields.get(typename)
|
return _deprecated_fields.get(typename, ())
|
||||||
|
|
||||||
|
|
||||||
def make_diff(oldobj: FrozenObj, newobj: FrozenObj,
|
def make_diff(oldobj: FrozenObj, newobj: FrozenObj,
|
||||||
|
@ -233,7 +235,8 @@ def make_diff(oldobj: FrozenObj, newobj: FrozenObj,
|
||||||
Compute a diff between two frozen objects.
|
Compute a diff between two frozen objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert isinstance(newobj, FrozenObj), "newobj parameter should be instance of FrozenObj"
|
assert isinstance(newobj, FrozenObj), \
|
||||||
|
"newobj parameter should be instance of FrozenObj"
|
||||||
|
|
||||||
if oldobj is None:
|
if oldobj is None:
|
||||||
return FrozenDiff(newobj.key, {}, newobj.snapshot)
|
return FrozenDiff(newobj.key, {}, newobj.snapshot)
|
||||||
|
@ -254,7 +257,8 @@ def make_diff_values(typename: str, fdiff: FrozenDiff) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if typename not in _values_impl_map:
|
if typename not in _values_impl_map:
|
||||||
log.warning("No implementation found of '{}' for values.".format(typename))
|
log.warning(
|
||||||
|
"No implementation found of '{}' for values.".format(typename))
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
impl_fn = _values_impl_map[typename]
|
impl_fn = _values_impl_map[typename]
|
||||||
|
@ -306,10 +310,12 @@ def get_modified_fields(obj: object, last_modifications):
|
||||||
"""
|
"""
|
||||||
key = make_key_from_model_object(obj)
|
key = make_key_from_model_object(obj)
|
||||||
entry_model = apps.get_model("history", "HistoryEntry")
|
entry_model = apps.get_model("history", "HistoryEntry")
|
||||||
history_entries = (entry_model.objects
|
history_entries = (
|
||||||
.filter(key=key)
|
entry_model.objects.filter(key=key)
|
||||||
.order_by("-created_at")
|
.order_by("-created_at")
|
||||||
.values_list("diff", flat=True)[0:last_modifications])
|
.values_list("diff",
|
||||||
|
flat=True)[0:last_modifications]
|
||||||
|
)
|
||||||
|
|
||||||
modified_fields = []
|
modified_fields = []
|
||||||
for history_entry in history_entries:
|
for history_entry in history_entries:
|
||||||
|
@ -319,7 +325,8 @@ def get_modified_fields(obj: object, last_modifications):
|
||||||
|
|
||||||
|
|
||||||
@tx.atomic
|
@tx.atomic
|
||||||
def take_snapshot(obj: object, *, comment: str="", user=None, delete: bool=False):
|
def take_snapshot(obj: object, *, comment: str="", user=None,
|
||||||
|
delete: bool=False):
|
||||||
"""
|
"""
|
||||||
Given any model instance with registred content type,
|
Given any model instance with registred content type,
|
||||||
create new history entry of "change" type.
|
create new history entry of "change" type.
|
||||||
|
@ -355,7 +362,9 @@ def take_snapshot(obj: object, *, comment: str="", user=None, delete: bool=False
|
||||||
|
|
||||||
# If diff and comment are empty, do
|
# If diff and comment are empty, do
|
||||||
# not create empty history entry
|
# not create empty history entry
|
||||||
if (not fdiff.diff and not comment and old_fobj is not None and entry_type != HistoryType.delete):
|
if (not fdiff.diff and
|
||||||
|
not comment and old_fobj is not None and
|
||||||
|
entry_type != HistoryType.delete):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
fvals = make_diff_values(typename, fdiff)
|
fvals = make_diff_values(typename, fdiff)
|
||||||
|
@ -384,7 +393,8 @@ def take_snapshot(obj: object, *, comment: str="", user=None, delete: bool=False
|
||||||
|
|
||||||
# High level query api
|
# High level query api
|
||||||
|
|
||||||
def get_history_queryset_by_model_instance(obj: object, types=(HistoryType.change,),
|
def get_history_queryset_by_model_instance(obj: object,
|
||||||
|
types=(HistoryType.change,),
|
||||||
include_hidden=False):
|
include_hidden=False):
|
||||||
"""
|
"""
|
||||||
Get one page of history for specified object.
|
Get one page of history for specified object.
|
||||||
|
@ -404,16 +414,18 @@ def prefetch_owners_in_history_queryset(qs):
|
||||||
users = get_user_model().objects.filter(id__in=user_ids)
|
users = get_user_model().objects.filter(id__in=user_ids)
|
||||||
users_by_id = {u.id: u for u in users}
|
users_by_id = {u.id: u for u in users}
|
||||||
for history_entry in qs:
|
for history_entry in qs:
|
||||||
history_entry.prefetch_owner(users_by_id.get(history_entry.user["pk"], None))
|
history_entry.prefetch_owner(users_by_id.get(history_entry.user["pk"],
|
||||||
|
None))
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
# Freeze & value register
|
# Freeze & value register
|
||||||
register_freeze_implementation("projects.project", project_freezer)
|
register_freeze_implementation("projects.project", project_freezer)
|
||||||
register_freeze_implementation("milestones.milestone", milestone_freezer,)
|
register_freeze_implementation("milestones.milestone", milestone_freezer)
|
||||||
register_freeze_implementation("epics.epic", epic_freezer)
|
register_freeze_implementation("epics.epic", epic_freezer)
|
||||||
register_freeze_implementation("epics.relateduserstory", epic_related_userstory_freezer)
|
register_freeze_implementation("epics.relateduserstory",
|
||||||
|
epic_related_userstory_freezer)
|
||||||
register_freeze_implementation("userstories.userstory", userstory_freezer)
|
register_freeze_implementation("userstories.userstory", userstory_freezer)
|
||||||
register_freeze_implementation("issues.issue", issue_freezer)
|
register_freeze_implementation("issues.issue", issue_freezer)
|
||||||
register_freeze_implementation("tasks.task", task_freezer)
|
register_freeze_implementation("tasks.task", task_freezer)
|
||||||
|
@ -422,7 +434,8 @@ register_freeze_implementation("wiki.wikipage", wikipage_freezer)
|
||||||
register_values_implementation("projects.project", project_values)
|
register_values_implementation("projects.project", project_values)
|
||||||
register_values_implementation("milestones.milestone", milestone_values)
|
register_values_implementation("milestones.milestone", milestone_values)
|
||||||
register_values_implementation("epics.epic", epic_values)
|
register_values_implementation("epics.epic", epic_values)
|
||||||
register_values_implementation("epics.relateduserstory", epic_related_userstory_values)
|
register_values_implementation("epics.relateduserstory",
|
||||||
|
epic_related_userstory_values)
|
||||||
register_values_implementation("userstories.userstory", userstory_values)
|
register_values_implementation("userstories.userstory", userstory_values)
|
||||||
register_values_implementation("issues.issue", issue_values)
|
register_values_implementation("issues.issue", issue_values)
|
||||||
register_values_implementation("tasks.task", task_values)
|
register_values_implementation("tasks.task", task_values)
|
||||||
|
|
|
@ -295,6 +295,15 @@ class UserStoryFactory(Factory):
|
||||||
due_date = factory.LazyAttribute(lambda o: date.today() + timedelta(days=7))
|
due_date = factory.LazyAttribute(lambda o: date.today() + timedelta(days=7))
|
||||||
due_date_reason = factory.Faker("words")
|
due_date_reason = factory.Faker("words")
|
||||||
|
|
||||||
|
@factory.post_generation
|
||||||
|
def assigned_users(self, create, users_list, **kwargs):
|
||||||
|
if not create:
|
||||||
|
return
|
||||||
|
|
||||||
|
if users_list:
|
||||||
|
for user in users_list:
|
||||||
|
self.assigned_users.add(user)
|
||||||
|
|
||||||
|
|
||||||
class TaskFactory(Factory):
|
class TaskFactory(Factory):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -448,10 +448,12 @@ def test_delete_membership_timeline():
|
||||||
def test_comment_user_story_timeline():
|
def test_comment_user_story_timeline():
|
||||||
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner)
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner, comment="testing comment")
|
history_services.take_snapshot(user_story, user=user_story.owner,
|
||||||
|
comment="testing comment")
|
||||||
project_timeline = service.get_project_timeline(user_story.project)
|
project_timeline = service.get_project_timeline(user_story.project)
|
||||||
assert project_timeline[0].event_type == "userstories.userstory.change"
|
assert project_timeline[0].event_type == "userstories.userstory.change"
|
||||||
assert project_timeline[0].data["userstory"]["subject"] == "test us timeline"
|
assert project_timeline[0].data["userstory"]["subject"] \
|
||||||
|
== "test us timeline"
|
||||||
assert project_timeline[0].data["comment"] == "testing comment"
|
assert project_timeline[0].data["comment"] == "testing comment"
|
||||||
|
|
||||||
|
|
||||||
|
@ -465,7 +467,9 @@ def test_owner_user_story_timeline():
|
||||||
|
|
||||||
def test_assigned_to_user_story_timeline():
|
def test_assigned_to_user_story_timeline():
|
||||||
membership = factories.MembershipFactory.create()
|
membership = factories.MembershipFactory.create()
|
||||||
user_story = factories.UserStoryFactory.create(subject="test us timeline", assigned_to=membership.user, project=membership.project)
|
user_story = factories.UserStoryFactory.create(subject="test us timeline",
|
||||||
|
assigned_to=membership.user,
|
||||||
|
project=membership.project)
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner)
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
user_timeline = service.get_profile_timeline(user_story.assigned_to)
|
user_timeline = service.get_profile_timeline(user_story.assigned_to)
|
||||||
assert user_timeline[0].event_type == "userstories.userstory.create"
|
assert user_timeline[0].event_type == "userstories.userstory.create"
|
||||||
|
@ -492,6 +496,30 @@ def test_due_date_user_story_timeline():
|
||||||
str(new_due_date.date())]
|
str(new_due_date.date())]
|
||||||
|
|
||||||
|
|
||||||
|
def test_assigned_users_user_story_timeline():
|
||||||
|
membership = factories.MembershipFactory.create()
|
||||||
|
user_story = factories.UserStoryFactory.create(subject="test us timeline",
|
||||||
|
project=membership.project)
|
||||||
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
|
user_timeline = service.get_profile_timeline(user_story.owner)
|
||||||
|
|
||||||
|
assert user_timeline[0].event_type == "userstories.userstory.create"
|
||||||
|
assert user_timeline[0].data["userstory"]["subject"] == "test us timeline"
|
||||||
|
|
||||||
|
user_story.assigned_to = membership.user
|
||||||
|
user_story.assigned_users = (membership.user,)
|
||||||
|
user_story.save()
|
||||||
|
|
||||||
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
|
|
||||||
|
user_timeline = service.get_profile_timeline(user_story.owner)
|
||||||
|
|
||||||
|
assert user_timeline[0].event_type == "userstories.userstory.change"
|
||||||
|
assert "assigned_to" not in user_timeline[0].data["values_diff"].keys()
|
||||||
|
assert user_timeline[0].data["values_diff"]['assigned_users'] == \
|
||||||
|
[None, membership.user.username]
|
||||||
|
|
||||||
|
|
||||||
def test_user_data_for_non_system_users():
|
def test_user_data_for_non_system_users():
|
||||||
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
user_story = factories.UserStoryFactory.create(subject="test us timeline")
|
||||||
history_services.take_snapshot(user_story, user=user_story.owner)
|
history_services.take_snapshot(user_story, user=user_story.owner)
|
||||||
|
@ -520,8 +548,10 @@ def test_user_data_for_unactived_users():
|
||||||
serialized_obj.data["data"]["user"]["is_profile_visible"] = False
|
serialized_obj.data["data"]["user"]["is_profile_visible"] = False
|
||||||
serialized_obj.data["data"]["user"]["username"] = "deleted-user"
|
serialized_obj.data["data"]["user"]["username"] = "deleted-user"
|
||||||
|
|
||||||
|
|
||||||
def test_timeline_error_use_member_ids_instead_of_memberships_ids():
|
def test_timeline_error_use_member_ids_instead_of_memberships_ids():
|
||||||
user_story = factories.UserStoryFactory.create(subject="test error use member ids instead of "
|
user_story = factories.UserStoryFactory.create(
|
||||||
|
subject="test error use member ids instead of "
|
||||||
"memberships ids")
|
"memberships ids")
|
||||||
|
|
||||||
member_user = user_story.owner
|
member_user = user_story.owner
|
||||||
|
|
Loading…
Reference in New Issue