def test_groupTogglByIssueId(self): entries = [ TogglEntry(None, 3600, None, 1, "#15", self.redmine_config), TogglEntry(None, 3600, None, 2, "#16", self.redmine_config), TogglEntry(None, 3600, None, 3, "#16", self.redmine_config), TogglEntry(None, 3600, None, 4, "#16", self.redmine_config), TogglEntry(None, 3600, None, 5, "#17", self.redmine_config), ] groups = Synchronizer.groupTogglByIssueId(entries) self.assertIsNotNone(groups) self.assertEqual(3, len(groups)) self.assertTrue("15" in groups) self.assertTrue("16" in groups) self.assertTrue("17" in groups) self.assertEquals(1, len(groups["15"])) self.assertEquals(3, len(groups["16"])) self.assertEquals(1, len(groups["17"])) self.assertEquals(1, groups["15"][0].id) self.assertEquals(2, groups["16"][0].id) self.assertEquals(3, groups["16"][1].id) self.assertEquals(4, groups["16"][2].id) self.assertEquals(5, groups["17"][0].id)
def test_filterToday(self): actual = MattermostNotifier.filterToday([ TogglEntry(None, 4 * 3600, self.today, 777, "#666 Hardwork", self.redmine_config), TogglEntry(None, 4 * 3600, None, 778, "#666 Hardwork", self.redmine_config), ]) self.assertEquals(1, len(actual)) self.assertEquals(actual[0].id, 777)
def test_filterWithRedmineId(self): entries = [ TogglEntry(None, 1, self.today, 1, "#666 Hardwork", self.redmine_config), TogglEntry(None, 1, self.today, 2, "Hardwork", self.redmine_config), TogglEntry(None, 1, self.today, 3, "#666 Hardwork", self.redmine_config), ] filtered = MattermostNotifier.filterWithRedmineId(entries) self.assertEquals(2, len(filtered)) self.assertEquals(1, filtered[0].id) self.assertEquals(3, filtered[1].id)
def test_sync_single_toggl_no_redmine(self): config = MagicMock() redmine = RedmineHelper("url", None, False) redmine.get = Mock() redmine.put = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 3600, "2016-01-01T01:01:01", 17, "#987 hard work", self.redmine_config, ) ] redmine.get.return_value = [] s = Synchronizer(config, redmine, toggl, None, raise_errors=True) s.start(1) toggl.get.assert_called_once_with(1) redmine.put.assert_called_once_with( issueId="987", spentOn="2016-01-01", hours=1.0, comment="#987 hard work [toggl#17]", )
def __append_redmine_summary(self, allEntries): redmineEntries = TogglHelper.filter_valid_entries(allEntries) if len(redmineEntries) > 0: self.append("---") self.append("**Redmine summary**") redmineIssuesSums = {} for e in redmineEntries: if e.taskId not in redmineIssuesSums: redmineIssuesSums[e.taskId] = 0 redmineIssuesSums[e.taskId] += e.duration longestTasks = sorted(redmineIssuesSums, key=lambda id: -redmineIssuesSums[id])[:3] self.append("You spent most time on:") for id in longestTasks: self.append("- #{}: {} h".format( id, TogglEntry.secondsToHours(redmineIssuesSums[id]))) self.append("")
def test_sync_single_toggl_already_inserted_in_redmine(self): redmine = RedmineHelper("url", None, False) redmine.get = Mock() redmine.put = Mock() redmine.update = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 3600, "2016-01-01T01:01:01", 17, "#987 hard work", self.redmine_config, ) ] redmine.get.return_value = [ RedmineTimeEntry( 222, "2016-05-01T04:02:22", "john doe", 1, "2016-01-01", "987", "#987 hard work [toggl#17]", ) ] s = Synchronizer(MagicMock(), redmine, toggl, None, raise_errors=True) s.start(1) redmine.update.assert_not_called() redmine.put.assert_not_called()
def test_sync_skipping_entries_under_1min(self): config = MagicMock() jira = JiraHelper(None, None, None, False) jira.get = Mock() jira.jira_api = Mock() # jira.put = Mock() jira.jira_api.add_worklog = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 29, # time rounds to under 60 sec "2016-01-01T01:01:01", 17, "SLUG-987 hard work", self.jira_config, ) ] jira.get.return_value = [] s = Synchronizer(config, jira, toggl, None, raise_errors=True) s.start(1) toggl.get.assert_called_once_with(1) jira.jira_api.add_worklog.assert_not_called()
def test_start_one_day_single(self): jira = JiraHelper(None, None, None, False) toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry(None, 3600, "2016-03-02T01:01:01", 777, "test SLUG-333", self.jira_config) ] jira.get = Mock() jira.get.return_value = [ JiraTimeEntry( 777, "2016-01-01T01:02:03", "john doe", 3600, "2016-03-02T01:01:01", "SLUG-333", "test SLUG-333 [toggl#777]", ) ] jira.update = MagicMock() s = Synchronizer(Mock(), jira, toggl, None, raise_errors=True) s.start(1) toggl.get.assert_called_once_with(1) jira.get.assert_called_once_with('SLUG-333') jira.update.assert_not_called()
def test_sync_single_toggl_update_to_under_1min_clears_worklog(self): jira = JiraHelper(None, None, None, False) jira.get = Mock() jira.delete = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 29, # time rounds to under 60 sec "2016-01-01T01:01:01", 17, "SLUG-987 hard work", self.jira_config, ) ] jira.get.return_value = [ JiraTimeEntry( 222, "2016-05-01T04:02:22", "john doe", 1000, "2016-01-01T01:01:01", "SLUG-987", "SLUG-987 hard work [toggl#17]", ) ] s = Synchronizer(MagicMock(), jira, toggl, None, raise_errors=True) s.start(1) jira.delete.assert_called_once_with(222, "SLUG-987")
def test_start_one_day_single(self): redmine = RedmineHelper("url", None, False) toggl = TogglHelper("url", None) toggl.get = MagicMock() toggl.get.return_value = [ TogglEntry(None, 3600, "2016-03-02T01:01:01", 777, "test #333", self.redmine_config) ] redmine.get = MagicMock() redmine.get.return_value = [ RedmineTimeEntry( 777, "2016-01-01T01:02:03", "john doe", 1.0, "2016-03-02", 333, "test #333 [toggl#777]", ) ] redmine.update = MagicMock() s = Synchronizer(Mock(), redmine, toggl, None, raise_errors=True) s.start(1) toggl.get.assert_called_once_with(1) redmine.get.assert_called_once_with('333') redmine.update.assert_not_called()
def test_sync_single_toggl_already_inserted_in_jira(self): jira = JiraHelper(None, None, None, False) jira.get = Mock() jira.put = Mock() jira.update = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 3600, "2016-01-01T01:01:01", 17, "SLUG-987 hard work", self.jira_config, ) ] jira.get.return_value = [ JiraTimeEntry( 222, "2016-05-01T04:02:22", "john doe", 3600, "2016-01-01T01:01:01", "SLUG-987", "SLUG-987 hard work [toggl#17]", ) ] s = Synchronizer(MagicMock(), jira, toggl, None, raise_errors=True) s.start(1) jira.update.assert_not_called() jira.put.assert_not_called()
def test_sync_single_toggl_no_jira(self): config = MagicMock() jira = JiraHelper(None, None, None, False) jira.get = Mock() jira.put = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 3600, "2016-01-01T01:01:01", 17, "SLUG-987 hard work", self.jira_config, ) ] jira.get.return_value = [] s = Synchronizer(config, jira, toggl, None, raise_errors=True) s.start(1) toggl.get.assert_called_once_with(1) jira.put.assert_called_once_with( issueId="SLUG-987", started="2016-01-01T01:01:01", seconds=3600, comment="SLUG-987 hard work [toggl#17]", )
def testDictFromTogglEntry(self): input = TogglEntry(None, 120, "2016-03-02T01:01:01", 777, "test SLUG-333", self.jira_config) result = JiraHelper.dictFromTogglEntry(input) self.assertEquals("SLUG-333", result["issueId"]) self.assertEquals("2016-03-02T01:01:01", result["started"]) self.assertEquals(120, result["seconds"]) self.assertEquals("test SLUG-333 [toggl#777]", result["comment"])
def test_append_redmine_summary_only_first_3(self): runner = MagicMock() mattermost = MattermostNotifier(runner) l = [ TogglEntry(None, 3600, self.today, 777, "test #333", self.redmine_config), TogglEntry(None, 3600, self.today, 777, "test #333", self.redmine_config), TogglEntry(None, 3600, self.today, 777, "test #333", self.redmine_config), TogglEntry(None, 3600, self.today, 777, "test #333", self.redmine_config), TogglEntry(None, 0.5 * 3600, self.today, 778, "test #334", self.redmine_config), TogglEntry(None, 2 * 3600, self.today, 778, "test #335", self.redmine_config), TogglEntry(None, 10 * 3600, self.today, 778, "test #400", self.redmine_config), ] mattermost._MattermostNotifier__append_redmine_summary(l) mattermost.send() text = """--- **Redmine summary** You spent most time on: - #400: 10.0 h - #333: 4.0 h - #335: 2.0 h """ runner.send.assert_called_with(text)
def test_parse(self): toggl_payload = { "id": 2121, "duration": 255, "start": "2016-01-01T09:09:09+02:00", "description": "entry description", } entry = TogglEntry.createFromEntry(toggl_payload, None) self.assertEquals(2121, entry.id) self.assertEquals("2016-01-01T07:09:09+00:00", entry.start)
def test_ignore_negative_duration(self): """ Synchronizer should ignore entries with negative durations (pending entries). From toggl docs: duration: time entry duration in seconds. If the time entry is currently running, the duration attribute contains a negative value, denoting the start of the time entry in seconds since epoch (Jan 1 1970). The correct duration can be calculated as current_time + duration, where current_time is the current time in seconds since epoch. (integer, required) """ redmine = RedmineHelper("url", None, False) redmine.get = Mock() redmine.put = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry(None, 3600, "2016-01-01T01:01:01", 777, "test #333", self.redmine_config), TogglEntry( None, -3600, "2016-01-01T01:01:01", 778, "test #334", self.redmine_config, ), ] redmine.get.return_value = [] s = Synchronizer(Mock(), redmine, toggl, None, raise_errors=True) s.start(1) toggl.get.assert_called_once_with(1) redmine.get.assert_called_once_with("333") redmine.put.assert_called_once_with( issueId="333", spentOn="2016-01-01", hours=1.0, comment="test #333 [toggl#777]", )
def test_append_summary_3_entries_1_redmine(self): runner = MagicMock() mattermost = MattermostNotifier(runner) l = [ TogglEntry(None, 60, self.today, 777, "#666 Hardwork", self.redmine_config), TogglEntry(None, 60, self.today, 777, "Hardwork", self.redmine_config), TogglEntry(None, 60, self.today, 777, "Hardwork", self.redmine_config), ] mattermost._MattermostNotifier__append_summary(l) mattermost.send() text = """You worked almost less than 4 hours today (exactly 3 m), not every day is a perfect day, right? :smirk:. Huh, not many entries. It means, you did only a couple of tasks, but did it right .. right? :open_mouth: Almost 50% of your today work had redmine id :blush:.""" runner.send.assert_called_with(text)
def test_repr(self): entry = TogglEntry( None, 8100, "2016-01-01T09:09:09+02:00", 999, "comment SLUG-123", self.sample_config, ) self.assertEquals( "toggl#999. 2016-01-01T09:09:09+02:00: comment SLUG-123 (time: 2.25 h, task id: SLUG-123)", repr(entry), )
def create_test_entries_pair(self): toggl = TogglEntry(None, 3600, "2020-01-13T08:11:04+00:00", 777, "test SLUG-333", self.jira_config) jira = JiraTimeEntry( "987654321", created_on="2020-01-13T08:11:04.000+00:00", user="******", seconds=3600, started="2020-01-13T08:11:04.000+00:00", issue="SLUG-333", comments="test SLUG-333 [toggl#777]", jira_issue_id="12345", ) return toggl, jira
def test_append_entries_two_one_with_redmine(self): runner = MagicMock() mattermost = MattermostNotifier(runner) mattermost.appendEntries([ TogglEntry(None, 60, self.today, 776, "", self.redmine_config), TogglEntry(None, 60, self.today, 777, "#666 Hardwork", self.redmine_config), ]) mattermost.send() text = """Found entries in toggl: **2** (filtered: **1**) You worked almost less than 4 hours today (exactly 2 m), not every day is a perfect day, right? :smirk:. Huh, not many entries. It means, you did only a couple of tasks, but did it right .. right? :open_mouth: It's gooood. A lot of today work had redmine id! Congrats :sunglasses:. --- **Redmine summary** You spent most time on: - #666: 0.02 h """ runner.send.assert_called_with(text)
def test_str(self): entry = TogglEntry( None, 8145, "2016-01-01T10:09:09-00:00", 999, "comment SLUG-123", self.sample_config, ) with mock.patch("dateutil.tz.tzlocal", return_value=dateutil.tz.gettz("EST")): self.assertEquals( "2016-01-01 05:09: comment SLUG-123, spent: 2:15:45, issue: SLUG-123 [toggl#999]", str(entry), )
def test_append_summary_two_one_with_redmine_4_hours(self): runner = MagicMock() mattermost = MattermostNotifier(runner) mattermost._MattermostNotifier__append_summary([ TogglEntry(None, 4 * 3123, self.today, 777, "#666 Hardwork", self.redmine_config) ]) mattermost.send() text = """You worked almost less than 4 hours today (exactly 3.47 h), not every day is a perfect day, right? :smirk:. Huh, not many entries. It means, you did only a couple of tasks, but did it right .. right? :open_mouth: It seems that more than 75% of your today work had redmine id! So .. you rock :rocket:!""" runner.send.assert_called_with(text)
def test_append_entries_one(self): runner = MagicMock() mattermost = MattermostNotifier(runner) mattermost.appendEntries( [TogglEntry(None, 60, self.today, 777, "", self.redmine_config)]) mattermost.send() text = """Found entries in toggl: **1** (filtered: **0**) You worked almost less than 4 hours today (exactly 1 m), not every day is a perfect day, right? :smirk:. Huh, not many entries. It means, you did only a couple of tasks, but did it right .. right? :open_mouth: Ugh. Less than 25% of your work had redmine id. Not so good :cry:. """ runner.send.assert_called_with(text)
def test_ignore_negative_duration(self): """ Mattermost should ignore entries with negative durations (pending entries). From toggl docs: duration: time entry duration in seconds. If the time entry is currently running, the duration attribute contains a negative value, denoting the start of the time entry in seconds since epoch (Jan 1 1970). The correct duration can be calculated as current_time + duration, where current_time is the current time in seconds since epoch. (integer, required) """ runner = MagicMock() mattermost = MattermostNotifier(runner) l = [ TogglEntry(None, 3600, self.today, 777, "test #333", self.redmine_config), TogglEntry(None, -300, self.today, 778, "test #334", self.redmine_config), ] mattermost.appendEntries(l) mattermost.send() text = """Found entries in toggl: **2** (filtered: **1**) You worked almost less than 4 hours today (exactly 1.00 h), not every day is a perfect day, right? :smirk:. Huh, not many entries. It means, you did only a couple of tasks, but did it right .. right? :open_mouth: It seems that more than 75% of your today work had redmine id! So .. you rock :rocket:! --- **Redmine summary** You spent most time on: - #333: 1.0 h """ runner.send.assert_called_with(text)
def test_append_redmine_summary_no_entries_no_summary(self): runner = MagicMock() mattermost = MattermostNotifier(runner) l = [ TogglEntry(None, 3600, self.today, 777, "test 333", self.redmine_config) ] mattermost._MattermostNotifier__append_redmine_summary(l) mattermost.send() text = "" runner.send.assert_called_with(text)
def test_append_summary_50_entries(self): runner = MagicMock() mattermost = MattermostNotifier(runner) e = TogglEntry(None, 60, self.today, 777, "#666 Hardwork", self.redmine_config) l = [] for i in range(50): l.append(e) mattermost._MattermostNotifier__append_summary(l) mattermost.send() text = """You worked almost less than 4 hours today (exactly 50 m), not every day is a perfect day, right? :smirk:. You did 50 entries like a boss :smirk: :boom:! It seems that more than 75% of your today work had redmine id! So .. you rock :rocket:!""" runner.send.assert_called_with(text)
def test_append_summary_10_entries(self): runner = MagicMock() mattermost = MattermostNotifier(runner) e = TogglEntry(None, 4 * 3600, self.today, 777, "#666 Hardwork", self.redmine_config) l = [] for i in range(1, 10): l.append(e) mattermost._MattermostNotifier__append_summary(l) mattermost.send() text = """Wow you did overtime today :rocket:! Doing overtime from time to time can be good, but life after work is also important. Remember this next time taking 36.00 h in work :sunglasses:! Average day. Not too few, not too many entries :sunglasses:. It seems that more than 75% of your today work had redmine id! So .. you rock :rocket:!""" runner.send.assert_called_with(text)
def test_sync_single_toggl_modified_entry(self): jira = JiraHelper(None, None, None, False) jira.get = Mock() jira.update = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 2 * 3600, "2016-01-01T01:01:01", 17, "SLUG-987 hard work", self.jira_config, ) ] jira.get.return_value = [ JiraTimeEntry( 222, "2016-05-01T04:02:22", "john doe", 1, "2016-01-01T01:01:01", "SLUG-987", "SLUG-987 hard work [toggl#17]", ) ] s = Synchronizer(MagicMock(), jira, toggl, None, raise_errors=True) s.start(1) jira.update.assert_called_once_with( id=222, issueId="SLUG-987", started="2016-01-01T01:01:01", seconds=2 * 3600, comment="SLUG-987 hard work [toggl#17]", )
def test_sync_single_toggl_modified_entry(self): redmine = RedmineHelper("url", None, False) redmine.get = Mock() redmine.update = Mock() toggl = TogglHelper("url", None) toggl.get = Mock() toggl.get.return_value = [ TogglEntry( None, 2 * 3600, "2016-01-01T01:01:01", 17, "#987 hard work", self.redmine_config, ) ] redmine.get.return_value = [ RedmineTimeEntry( 222, "2016-05-01T04:02:22", "john doe", 1, "2016-01-01", "987", "#987 hard work [toggl#17]", ) ] s = Synchronizer(MagicMock(), redmine, toggl, None, raise_errors=True) s.start(1) redmine.update.assert_called_once_with( id=222, issueId="987", spentOn="2016-01-01", hours=2.0, comment="#987 hard work [toggl#17]", )
def testDictFromTogglEntry_time_is_rounded(self): input = TogglEntry(None, 151, "2016-03-02T01:01:01", 777, "test SLUG-333", self.jira_config) result = JiraHelper.dictFromTogglEntry(input) self.assertEquals(180, result["seconds"])