def test_subtasks_inherit_from_parent(subtask_scenario, subtask_lenses, sprint_history_lenses): raw_json, intermediate, final = subtask_scenario raw_json = sprint_history_lenses.raw.set([ { "created": "2020-01-01T11:00:00.000+0100", "items": [{ "field": "Sprint", "from": "1", "to": "1, 2", }] }, ])(raw_json) intermediate = sprint_history_lenses.intermediate.set([{ "timestamp": datetime(2020, 1, 1, 11, 0, 0, tzinfo=timezone(timedelta(hours=1))), "from": {1}, "to": {1, 2} }])(intermediate) sprint_metrics = SprintMetrics(sprint_additions=[{ "timestamp": datetime(2020, 1, 1, 11, 0, 0, tzinfo=timezone(timedelta(hours=1))), "sprint_id": 2 }]) final = sprint_history_lenses.final.set(sprint_metrics)(final) # Subtasks inherit their SprintMetrics from their parent # because the parent is the entity that you can physical move between # sprints. final = (subtask_lenses.final.Each() & sprint_history_lenses.final).set(sprint_metrics)(final) assert parse_issue(raw_json, mock_subtask_fetcher) == final assert final.subtasks[0].description == final.description != None assert final.subtasks[0].epic == final.epic != None
def test_sprint_history_addition(basic_scenario, sprint_history_lenses): raw_json, intermediate, final = basic_scenario raw_json = sprint_history_lenses.raw.set([ { "created": "2020-01-01T11:00:00.000+0100", "items": [{ "field": "Sprint", "from": "1", "to": "1, 2", }] }, ])(raw_json) intermediate = sprint_history_lenses.intermediate.set([{ "timestamp": datetime(2020, 1, 1, 11, 0, 0, tzinfo=timezone(timedelta(hours=1))), "from": {1}, "to": {1, 2} }])(intermediate) assert intermediate_parse(raw_json) == intermediate final = sprint_history_lenses.final.set( SprintMetrics(sprint_additions=[{ "timestamp": datetime(2020, 1, 1, 11, 0, 0, tzinfo=timezone(timedelta( hours=1))), "sprint_id": 2 }]))(final) assert parse_issue(raw_json) == final
def test_status_history_no_done_date_no_end(basic_scenario, status_lenses, status_history_lenses): ''' If a ticket hasn't been moved to done it has no days_taken metric or end date. ''' raw_json, intermediate, final = basic_scenario raw_json = status_lenses.raw.set('In Progress')(raw_json) raw_json = status_history_lenses.raw.set([ { "created": "2020-01-01T09:00:00.000+0100", "items": [{ "field": "status", "fieldId": "status", "fromString": "To Do", "toString": "In Progress" }] }, ])(raw_json) intermediate = status_lenses.intermediate.set( StatusTypes.inprogress)(intermediate) intermediate = status_history_lenses.intermediate.set([ { "timestamp": datetime(2020, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=1))), "from": StatusTypes.todo, "to": StatusTypes.inprogress }, ])(intermediate) final = status_lenses.final.set(StatusTypes.inprogress)(final) final = status_history_lenses.final.set( StatusMetrics(started=True, finished=False, start=datetime(2020, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=1))), end=None, days_taken=None))(final) assert intermediate_parse(raw_json) == intermediate assert parse_issue(raw_json) == final
def test_status_history_straight_to_done(basic_scenario, status_lenses, status_history_lenses): # TODO: do we artificially set days_taken to 1? raw_json, intermediate, final = basic_scenario raw_json = status_lenses.raw.set('Done')(raw_json) raw_json = status_history_lenses.raw.set([ { "created": "2020-01-01T09:00:00.000+0100", "items": [{ "field": "status", "fieldId": "status", "fromString": "To Do", "toString": "Done" }] }, ])(raw_json) intermediate = status_lenses.intermediate.set( StatusTypes.done)(intermediate) intermediate = status_history_lenses.intermediate.set([ { "timestamp": datetime(2020, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=1))), "from": StatusTypes.todo, "to": StatusTypes.done }, ])(intermediate) final = status_lenses.final.set(StatusTypes.done)(final) final = status_history_lenses.final.set( StatusMetrics(started=False, finished=True, start=None, end=datetime(2020, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=1))), days_taken=None))(final) assert intermediate_parse(raw_json) == intermediate assert parse_issue(raw_json) == final
def test_status_history_rounds_up_to_nearest_day(basic_scenario, status_lenses, status_history_lenses): ''' We only capture days_taken, and we always round up. This is subject to change. But finer grained time measures are likely not useful for forecasting. ''' raw_json, intermediate, final = basic_scenario issue = status_lenses.raw.set('Done')(raw_json) issue = status_history_lenses.raw.set([ { "created": "2020-01-01T09:01:00.000+0100", "items": [{ "field": "status", "fieldId": "status", "fromString": "In Progress", "toString": "Done" }] }, { "created": "2020-01-01T09:00:00.000+0100", "items": [{ "field": "status", "fieldId": "status", "fromString": "To Do", "toString": "In Progress" }] }, ])(issue) intermediate = status_lenses.intermediate.set( StatusTypes.done)(intermediate) intermediate = status_history_lenses.intermediate.set([{ "timestamp": datetime(2020, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=1))), "from": StatusTypes.todo, "to": StatusTypes.inprogress }, { "timestamp": datetime(2020, 1, 1, 9, 1, 0, tzinfo=timezone(timedelta(hours=1))), "from": StatusTypes.inprogress, "to": StatusTypes.done }])(intermediate) final = status_lenses.final.set(StatusTypes.done)(final) final = status_history_lenses.final.set( StatusMetrics(started=True, finished=True, start=datetime(2020, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=1))), end=datetime(2020, 1, 1, 9, 1, 0, tzinfo=timezone(timedelta(hours=1))), days_taken=1))(final) assert intermediate_parse(issue) == intermediate assert parse_issue(issue) == final
def test_basic_parsing(basic_scenario): raw_json, intermediate, final = basic_scenario assert intermediate_parse(raw_json) == intermediate assert parse_issue(raw_json) == final
def test_issue_with_subtasks(subtask_scenario): raw_json, intermediate, final = subtask_scenario assert intermediate_parse(raw_json) == intermediate assert parse_issue(raw_json, mock_subtask_fetcher) == final