From 5cb55b2945857dc891142172c46c7234a97bac31 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Wed, 17 Jul 2024 22:19:54 +0200 Subject: [PATCH] Extend Todo tests. --- tests/processes.py | 12 ---- tests/todos.py | 166 +++++++++++++++++++++++++++++++++++---------- tests/utils.py | 17 ++++- 3 files changed, 147 insertions(+), 48 deletions(-) diff --git a/tests/processes.py b/tests/processes.py index 501a163..973ba3b 100644 --- a/tests/processes.py +++ b/tests/processes.py @@ -415,18 +415,6 @@ class TestsWithServer(TestCaseWithServer): '_library': library} return d - @staticmethod - def procstep_as_dict(id_: int, - owner_id: int, - step_process_id: int, - parent_step_id: int | None = None - ) -> dict[str, object]: - """Return JSON of Process to expect.""" - return {'id': id_, - 'owner_id': owner_id, - 'step_process_id': step_process_id, - 'parent_step_id': parent_step_id} - def test_GET_processes(self) -> None: """Test GET /processes.""" # pylint: disable=too-many-statements diff --git a/tests/todos.py b/tests/todos.py index aec7366..7e27636 100644 --- a/tests/todos.py +++ b/tests/todos.py @@ -254,16 +254,20 @@ class TestsWithServer(TestCaseWithServer): def setUp(self) -> None: super().setUp() self._proc1_form_data: Any = self.post_process(1) + self._date = '2024-01-01' @classmethod def GET_todo_dict(cls, target_id: int, todos: list[dict[str, object]], - processes: list[dict[str, object]] + processes: list[dict[str, object]], + process_steps: list[dict[str, object]] | None = None, ) -> dict[str, object]: """Return JSON of GET /todo to expect.""" library = {'Todo': cls.as_refs(todos), 'Process': cls.as_refs(processes)} + if process_steps: + library['ProcessStep'] = cls.as_refs(process_steps) return {'todo': target_id, 'steps_todo_to_process': [], 'adoption_candidates_for': {}, @@ -272,6 +276,25 @@ class TestsWithServer(TestCaseWithServer): 'condition_candidates': [], '_library': library} + @staticmethod + def _step_as_dict(node_id: int, + children: list[dict[str, object]], + process: int | None = None, + todo: int | None = None, + fillable: bool = False, + ) -> dict[str, object]: + return {'node_id': node_id, + 'children': children, + 'process': process, + 'fillable': fillable, + 'todo': todo} + + def _make_todo_via_day_post(self, proc_id: int) -> None: + payload = {'day_comment': '', + 'new_todo': proc_id, + 'make_type': 'empty'} + self.check_post(payload, f'/day?date={self._date}&make_type=empty') + def test_basic_fail_POST_todo(self) -> None: """Test basic malformed/illegal POST /todo requests.""" # test we cannot just POST into non-existing Todo @@ -280,8 +303,7 @@ class TestsWithServer(TestCaseWithServer): self.check_post({}, '/todo?id=0', 404) self.check_post({}, '/todo?id=1', 404) # test malformed values on existing Todo - day_post = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} - self.check_post(day_post, '/day?date=2024-01-01&make_type=full') + self._make_todo_via_day_post(1) for name in ['adopt', 'effort', 'make_full', 'make_empty', 'conditions', 'disables', 'blockers', 'enables']: self.check_post({name: 'x'}, '/todo?id=1', 400, '/todo') @@ -291,12 +313,10 @@ class TestsWithServer(TestCaseWithServer): '/todo?id=1', 400, '/todo') def test_basic_POST_todo(self) -> None: - """Test POST /todo.""" - date = '2024-01-01' - day_post = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} - self.check_post(day_post, f'/day?date={date}&make_type=full') + """Test basic POST /todo manipulations.""" + self._make_todo_via_day_post(1) # test posting naked entity at first changes nothing - todo_dict = self.todo_as_dict(1, process_id=1, date=date) + todo_dict = self.todo_as_dict(1, 1, self._date) proc_dict = self.proc_as_dict(**self._proc1_form_data) expected = self.GET_todo_dict(1, [todo_dict], [proc_dict]) self.check_json_get('/todo?id=1', expected) @@ -306,33 +326,40 @@ class TestsWithServer(TestCaseWithServer): todo_post = {'done': '', 'calendarize': '', 'comment': 'foo', 'effort': 2.3} todo_dict = self.todo_as_dict( - 1, process_id=1, date=date, is_done=True, calendarize=True, + 1, 1, self._date, is_done=True, calendarize=True, comment='foo', effort=2.3) expected = self.GET_todo_dict(1, [todo_dict], [proc_dict]) self.check_post(todo_post, '/todo?id=1') self.check_json_get('/todo?id=1', expected) # test implicitly un-setting all of those except effort by empty post self.check_post({}, '/todo?id=1') - todo_dict = self.todo_as_dict(1, process_id=1, date=date, effort=2.3) + todo_dict = self.todo_as_dict(1, 1, date=self._date, effort=2.3) expected = self.GET_todo_dict(1, [todo_dict], [proc_dict]) self.check_json_get('/todo?id=1', expected) # test empty effort post can be explicitly unset by "" post self.check_post({'effort': ''}, '/todo?id=1') todo_dict['effort'] = None self.check_json_get('/todo?id=1', expected) + + def test_POST_todo_deletion(self) -> None: + """Test deletions via POST /todo.""" + self._make_todo_via_day_post(1) + todo_dict = self.todo_as_dict(1, process_id=1, date=self._date) + proc_dict = self.proc_as_dict(**self._proc1_form_data) + expected = self.GET_todo_dict(1, [todo_dict], [proc_dict]) # test failure of deletion on non-existing Todo self.check_post({'delete': ''}, '/todo?id=2', 404, '/') # test deletion of existing Todo self.check_post({'delete': ''}, '/todo?id=1', 302, '/') self.check_get('/todo?id=1', 404) # test deletion of adopted Todo - self.check_post(day_post, f'/day?date={date}&make_type=full') - self.check_post(day_post, f'/day?date={date}&make_type=full') + self._make_todo_via_day_post(1) + self._make_todo_via_day_post(1) self.check_post({'adopt': 2}, '/todo?id=1') self.check_post({'delete': ''}, '/todo?id=2', 302, '/') self.check_json_get('/todo?id=1', expected) # test deletion of adopting Todo - self.check_post(day_post, f'/day?date={date}&make_type=full') + self._make_todo_via_day_post(1) self.check_post({'adopt': 2}, '/todo?id=1') self.check_post({'delete': ''}, '/todo?id=1', 302, '/') todo_dict['id'] = 2 @@ -340,22 +367,18 @@ class TestsWithServer(TestCaseWithServer): self.check_json_get('/todo?id=2', expected) def test_POST_todo_adoption(self) -> None: - """Test POST /todo.""" - date = '2024-01-01' + """Test adoption via POST /todo with "adopt".""" # post two Todos to Day, have first adopt second + self._make_todo_via_day_post(1) + self._make_todo_via_day_post(1) proc_dict = self.proc_as_dict(**self._proc1_form_data) - day_post = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} - self.check_post(day_post, f'/day?date={date}&make_type=full') - self.check_post(day_post, f'/day?date={date}&make_type=full') - todo1_dict = self.todo_as_dict(1, process_id=1, date=date) + todo1_dict = self.todo_as_dict(1, process_id=1, date=self._date) todo1_dict['children'] = [2] - todo2_dict = self.todo_as_dict(2, process_id=1, date=date) + todo2_dict = self.todo_as_dict(2, process_id=1, date=self._date) todo2_dict['parents'] = [1] expected = self.GET_todo_dict(1, [todo1_dict, todo2_dict], [proc_dict]) expected['todo_candidates'] = [2] - expected['steps_todo_to_process'] = [{ - 'children': [], 'fillable': False, - 'node_id': 1, 'process': None, 'todo': 2}] + expected['steps_todo_to_process'] = [self._step_as_dict(1, [], todo=2)] self.check_post({'adopt': 2}, '/todo?id=1') self.check_json_get('/todo?id=1', expected) # test Todo cannot be set done with adopted Todo not done yet @@ -375,16 +398,56 @@ class TestsWithServer(TestCaseWithServer): self.check_post({'adopt': 1}, '/todo?id=2') self.check_post({'adopt': 2}, '/todo?id=1', 400) # test cannot do 2-step circular adoption - day_post = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} - self.check_post(day_post, f'/day?date={date}&make_type=full') + self._make_todo_via_day_post(1) self.check_post({'adopt': 2}, '/todo?id=3') self.check_post({'adopt': 3}, '/todo?id=1', 400) + def test_POST_todo_make_full(self) -> None: + """Test creation and adoption via POST /todo with "make_full".""" + # pylint: disable=too-many-locals + # create chain of Processes + proc_post = {'title': '', 'description': '', 'effort': 0.9} + self.post_process(2, proc_post | {'new_top_step': 1}) + self.post_process(3, proc_post | {'new_top_step': 2}) + self.post_process(4, proc_post | {'new_top_step': 3}) + proc1_dict = self.proc_as_dict(**self._proc1_form_data) + proc2_dict = self.proc_as_dict(2, '', '', 0.9) + proc2_dict['explicit_steps'] = [1] + proc3_dict = self.proc_as_dict(3, '', '', 0.9) + proc3_dict['explicit_steps'] = [2] + proc4_dict = self.proc_as_dict(4, '', '', 0.9) + proc4_dict['explicit_steps'] = [3] + proc_dicts = [proc1_dict, proc2_dict, proc3_dict, proc4_dict] + procstep_dicts = [self.procstep_as_dict(1, 2, 1), + self.procstep_as_dict(2, 3, 2), + self.procstep_as_dict(3, 4, 3)] + # post (childless) Todo of chain end, then make_full on next in line + self._make_todo_via_day_post(4) + todo1_dict = self.todo_as_dict(1, 4, self._date) + todo1_dict['children'] = [2] + todo2_dict = self.todo_as_dict(2, 3, self._date) + todo2_dict['parents'] = [1] + todo2_dict['children'] = [3] + todo3_dict = self.todo_as_dict(3, 2, self._date) + todo3_dict['parents'] = [2] + todo3_dict['children'] = [4] + todo4_dict = self.todo_as_dict(4, 1, self._date) + todo4_dict['parents'] = [3] + todo_dicts = [todo1_dict, todo2_dict, todo3_dict, todo4_dict] + expected = self.GET_todo_dict( + 1, todo_dicts, proc_dicts, procstep_dicts) + step_proc1 = self._step_as_dict(3, [], 1, 4, True) + step_proc2 = self._step_as_dict(2, [step_proc1], 2, 3, True) + step_proc3 = self._step_as_dict(1, [step_proc2], 3, 2, True) + expected['steps_todo_to_process'] = [step_proc3] + expected['process_candidates'] = [4, 3, 2, 1] + expected['todo_candidates'] = [2, 3, 4] + self.check_post({'fill_for_3': 'make_full_3'}, '/todo?id=1') + self.check_json_get('/todo?id=1', expected) + def test_do_GET_todo(self) -> None: """Test GET /todo response codes.""" - date = '2024-01-01' - day_post = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} - self.check_post(day_post, f'/day?date={date}&make_type=full') + self._make_todo_via_day_post(1) # test malformed or illegal parameter values self.check_get('/todo', 404) self.check_get('/todo?id=', 404) @@ -392,13 +455,48 @@ class TestsWithServer(TestCaseWithServer): self.check_get('/todo?id=0', 404) self.check_get('/todo?id=2', 404) # test all existing Processes are shown as available - p2_post: Any = {'title': 'bar', 'description': 'baz', 'effort': 0.9} - self.post_process(2, p2_post) - todo1_dict = self.todo_as_dict(1, process_id=1, date=date) + proc_post = {'title': '', 'description': '', 'effort': 0.9} + self.post_process(2, proc_post) + todo1_dict = self.todo_as_dict(1, process_id=1, date=self._date) proc1_dict = self.proc_as_dict(1, **self._proc1_form_data) - proc2_dict = self.proc_as_dict(2, **p2_post) - expected = self.GET_todo_dict(1, [todo1_dict], [proc1_dict, - proc2_dict]) + proc2_dict = self.proc_as_dict(2, '', '', 0.9) + proc_dicts = [proc1_dict, proc2_dict] + expected = self.GET_todo_dict(1, [todo1_dict], proc_dicts) + self.check_json_get('/todo?id=1', expected) + # test chain of Processes shown as potential step nodes + self.post_process(2, proc_post) + self.post_process(3, proc_post) + self.post_process(4, proc_post) + self.post_process(1, self._proc1_form_data | {'new_top_step': 2}) + self.post_process(2, proc_post | {'new_top_step': 3, 'step_of': [1]}) + self.post_process(3, proc_post | {'new_top_step': 4, 'step_of': [2]}) + proc1_dict['explicit_steps'] = [1] + proc2_dict['explicit_steps'] = [2] + proc3_dict = self.proc_as_dict(3, '', '', 0.9, explicit_steps=[3]) + proc4_dict = self.proc_as_dict(4, '', '', 0.9) + proc_dicts = [proc1_dict, proc2_dict, proc3_dict, proc4_dict] + procstep_dicts = [self.procstep_as_dict(1, 1, 2, None), + self.procstep_as_dict(2, 2, 3, None), + self.procstep_as_dict(3, 3, 4, None)] + expected = self.GET_todo_dict( + 1, [todo1_dict], proc_dicts, procstep_dicts) + step_proc4 = self._step_as_dict(3, [], 4) + step_proc3 = self._step_as_dict(2, [step_proc4], 3) + step_proc2 = self._step_as_dict(1, [step_proc3], 2, fillable=True) + expected['steps_todo_to_process'] = [step_proc2] + expected['adoption_candidates_for'] = {'2': [], '3': [], '4': []} + self.check_json_get('/todo?id=1', expected) + # test display of parallel chains + steps_d = {'new_top_step': 4, 'keep_step': [1], + 'step_1_process_id': 2, 'steps': [1, 4]} + self.post_process(1, self._proc1_form_data | steps_d) + proc1_dict['explicit_steps'] = [1, 4] + step2_proc4 = self._step_as_dict(4, [], 4, fillable=True) + procstep_dicts += [self.procstep_as_dict(4, 1, 4, None)] + expected = self.GET_todo_dict( + 1, [todo1_dict], proc_dicts, procstep_dicts) + expected['steps_todo_to_process'] = [step_proc2, step2_proc4] + expected['adoption_candidates_for'] = {'2': [], '3': [], '4': []} self.check_json_get('/todo?id=1', expected) def test_do_POST_day(self) -> None: diff --git a/tests/utils.py b/tests/utils.py index 19bcc59..dc78c15 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -575,6 +575,18 @@ class TestCaseWithServer(TestCaseWithDB): d['_versioned']['description'][i] = description return d + @staticmethod + def procstep_as_dict(id_: int, + owner_id: int, + step_process_id: int, + parent_step_id: int | None = None + ) -> dict[str, object]: + """Return JSON of Process to expect.""" + return {'id': id_, + 'owner_id': owner_id, + 'step_process_id': step_process_id, + 'parent_step_id': parent_step_id} + @staticmethod def todo_as_dict(id_: int = 1, process_id: int = 1, @@ -613,14 +625,15 @@ class TestCaseWithServer(TestCaseWithDB): conditions: None | list[int] = None, disables: None | list[int] = None, blockers: None | list[int] = None, - enables: None | list[int] = None + enables: None | list[int] = None, + explicit_steps: None | list[int] = None ) -> dict[str, object]: """Return JSON of Process to expect.""" # pylint: disable=too-many-arguments d = {'id': id_, 'calendarize': False, 'suppressed_steps': [], - 'explicit_steps': [], + 'explicit_steps': explicit_steps if explicit_steps else [], '_versioned': { 'title': {0: title}, 'description': {0: description}, -- 2.30.2