From f95d9f28be9c7fbf46e8b0ed3e7a627730575673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Hermida?= Date: Mon, 9 Apr 2018 21:39:10 +0200 Subject: [PATCH] * Add timeline tests * Improve factory * Add default to exclude fields --- taiga/projects/history/services.py | 45 +++++++++++++++++++----------- tests/factories.py | 9 ++++++ tests/integration/test_timeline.py | 40 ++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/taiga/projects/history/services.py b/taiga/projects/history/services.py index ebd1bf60..ef684f1e 100644 --- a/taiga/projects/history/services.py +++ b/taiga/projects/history/services.py @@ -81,7 +81,8 @@ _values_impl_map = {} # this fields are marked as hidden). _not_important_fields = { "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"]), } @@ -195,7 +196,8 @@ def freeze_model_instance(obj: object) -> FrozenObj: key = make_key_from_model_object(obj) impl_fn = _freeze_impl_map[typename] 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) @@ -224,7 +226,7 @@ def get_excluded_fields(typename: str) -> tuple: """ 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, @@ -233,7 +235,8 @@ def make_diff(oldobj: FrozenObj, newobj: FrozenObj, 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: 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: - log.warning("No implementation found of '{}' for values.".format(typename)) + log.warning( + "No implementation found of '{}' for values.".format(typename)) return {} 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) entry_model = apps.get_model("history", "HistoryEntry") - history_entries = (entry_model.objects - .filter(key=key) - .order_by("-created_at") - .values_list("diff", flat=True)[0:last_modifications]) + history_entries = ( + entry_model.objects.filter(key=key) + .order_by("-created_at") + .values_list("diff", + flat=True)[0:last_modifications] + ) modified_fields = [] for history_entry in history_entries: @@ -319,7 +325,8 @@ def get_modified_fields(obj: object, last_modifications): @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, 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 # 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 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 -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): """ 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_by_id = {u.id: u for u in users} 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 # Freeze & value register 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.relateduserstory", epic_related_userstory_freezer) +register_freeze_implementation("epics.relateduserstory", + epic_related_userstory_freezer) register_freeze_implementation("userstories.userstory", userstory_freezer) register_freeze_implementation("issues.issue", issue_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("milestones.milestone", milestone_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("issues.issue", issue_values) register_values_implementation("tasks.task", task_values) diff --git a/tests/factories.py b/tests/factories.py index 90a20b20..409a818e 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -295,6 +295,15 @@ class UserStoryFactory(Factory): due_date = factory.LazyAttribute(lambda o: date.today() + timedelta(days=7)) 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 Meta: diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 64d656fe..971ced4d 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -448,10 +448,12 @@ def test_delete_membership_timeline(): def test_comment_user_story_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, 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) 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" @@ -465,7 +467,9 @@ def test_owner_user_story_timeline(): def test_assigned_to_user_story_timeline(): 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) user_timeline = service.get_profile_timeline(user_story.assigned_to) 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())] +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(): user_story = factories.UserStoryFactory.create(subject="test us timeline") history_services.take_snapshot(user_story, user=user_story.owner) @@ -520,9 +548,11 @@ def test_user_data_for_unactived_users(): serialized_obj.data["data"]["user"]["is_profile_visible"] = False serialized_obj.data["data"]["user"]["username"] = "deleted-user" + def test_timeline_error_use_member_ids_instead_of_memberships_ids(): - user_story = factories.UserStoryFactory.create(subject="test error use member ids instead of " - "memberships ids") + user_story = factories.UserStoryFactory.create( + subject="test error use member ids instead of " + "memberships ids") member_user = user_story.owner external_user = factories.UserFactory.create()