From e62108445e196482a952ba87dfe1c206da72142a Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Sun, 14 Jul 2024 22:15:11 +0200 Subject: [PATCH] Add InputsParser.get_float_or_none. --- plomtask/http.py | 24 +++++++++++++++++++++--- tests/misc.py | 25 +++++++++++++++++++++++++ tests/todos.py | 45 ++++++++++++++++++++++++++------------------- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/plomtask/http.py b/plomtask/http.py index 8752600..0d58af3 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -123,6 +123,17 @@ class InputsParser: msg = f'cannot float form field value for key {key}: {val}' raise BadFormatException(msg) from e + def get_float_or_none(self, key: str) -> float | None: + """Retrieve float value of key from self.postvars, None if empty.""" + val = self.get_str(key) + if '' == val: + return None + try: + return float(val) + except ValueError as e: + msg = f'cannot float form field value for key {key}: {val}' + raise BadFormatException(msg) from e + def get_all_str(self, key: str) -> list[str]: """Retrieve list of string values at key.""" if key not in self.inputs.keys(): @@ -565,7 +576,7 @@ class TaskHandler(BaseHTTPRequestHandler): day_comment = self._form_data.get_str('day_comment') make_type = self._form_data.get_str('make_type') except NotFoundException as e: - raise BadFormatException(e) from e + raise BadFormatException(e) old_todos = self._form_data.get_all_int('todo_id') new_todos = self._form_data.get_all_int('new_todo') comments = self._form_data.get_all_str('comment') @@ -601,11 +612,17 @@ class TaskHandler(BaseHTTPRequestHandler): def do_POST_todo(self, todo: Todo) -> str: """Update Todo and its children.""" # pylint: disable=too-many-locals + # pylint: disable=too-many-branches + # pylint: disable=too-many-branches adopted_child_ids = self._form_data.get_all_int('adopt') processes_to_make_full = self._form_data.get_all_int('make_full') processes_to_make_empty = self._form_data.get_all_int('make_empty') fill_fors = self._form_data.get_first_strings_starting('fill_for_') - effort = self._form_data.get_str('effort', ignore_strict=True) + with_effort_post = True + try: + effort = self._form_data.get_float_or_none('effort') + except NotFoundException: + with_effort_post = False conditions = self._form_data.get_all_int('conditions') disables = self._form_data.get_all_int('disables') blockers = self._form_data.get_all_int('blockers') @@ -641,7 +658,8 @@ class TaskHandler(BaseHTTPRequestHandler): for process_id in processes_to_make_full: made = Todo.create_with_children(self.conn, process_id, todo.date) todo.add_child(made) - todo.effort = float(effort) if effort else None + if with_effort_post: + todo.effort = effort todo.set_conditions(self.conn, conditions) todo.set_blockers(self.conn, blockers) todo.set_enables(self.conn, enables) diff --git a/tests/misc.py b/tests/misc.py index e72c6d7..1713432 100644 --- a/tests/misc.py +++ b/tests/misc.py @@ -120,6 +120,31 @@ class TestsSansServer(TestCase): with self.assertRaises(BadFormatException): InputsParser({'foo': []}, strictness).get_float('foo') + def test_InputsParser_get_float_or_none(self) -> None: + """Test InputsParser.get_float_or_none on strict and non-strict.""" + for strictness in (False, True): + with self.assertRaises(BadFormatException): + InputsParser({'foo': ['bar']}, strictness).\ + get_float_or_none('foo') + parser = InputsParser({'foo': ['']}, strictness) + self.assertEqual(None, parser.get_float_or_none('foo')) + parser = InputsParser({'foo': ['0']}, strictness) + self.assertEqual(0, parser.get_float_or_none('foo')) + parser = InputsParser({'foo': ['0.1']}, strictness) + self.assertEqual(0.1, parser.get_float_or_none('foo')) + parser = InputsParser({'foo': ['1.23', '456']}, strictness) + self.assertEqual(1.23, parser.get_float_or_none('foo')) + if strictness: + with self.assertRaises(NotFoundException): + InputsParser({}, strictness).get_float_or_none('foo') + with self.assertRaises(NotFoundException): + InputsParser({'foo': []}, strictness).get_float_or_none('foo') + else: + parser = InputsParser({}, strictness) + self.assertEqual(None, parser.get_float_or_none('foo')) + parser = InputsParser({'foo': []}, strictness) + self.assertEqual(None, parser.get_float_or_none('foo')) + def test_InputsParser_get_all_str(self) -> None: """Test InputsParser.get_all_str on strict and non-strict.""" for strictness in (False, True): diff --git a/tests/todos.py b/tests/todos.py index dcd4856..c5c29d4 100644 --- a/tests/todos.py +++ b/tests/todos.py @@ -236,9 +236,29 @@ class TestsWithDB(TestCaseWithDB, TestCaseSansDB): class TestsWithServer(TestCaseWithServer): """Tests against our HTTP server/handler (and database).""" + def setUp(self) -> None: + super().setUp() + self._proc1_form_data = self.post_process(1) + + def test_basic_fail_POST_todo(self) -> None: + """Test basic malformed/illegal POST /todo requests.""" + # test we cannot just POST into non-existing Todo + self.check_post({}, '/todo', 404) + self.check_post({}, '/todo?id=FOO', 400) + 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') + for name in ['adopt', 'effort', 'make_full', 'make_empty', + 'conditions', 'disables', 'blockers', 'enables']: + self.check_post({name: 'x'}, '/todo?id=1', 400, '/todo') + # test we cannot POST adoption of self or non-existing Todo + self.check_post({'adopt': 1}, '/todo?id=1', 400) + self.check_post({'adopt': 2}, '/todo?id=1', 404) + def test_do_POST_day(self) -> None: """Test Todo posting of POST /day.""" - self.post_process() self.post_process(2) proc = Process.by_id(self.db_conn, 1) proc2 = Process.by_id(self.db_conn, 2) @@ -273,16 +293,9 @@ class TestsWithServer(TestCaseWithServer): redir_url: str = '/todo?id=1') -> Todo: self.check_post(form_data, '/todo?id=1', status, redir_url) return Todo.by_date(self.db_conn, '2024-01-01')[0] - # test minimum - self.post_process() self.check_post({'day_comment': '', 'new_todo': 1, 'make_type': 'full'}, '/day?date=2024-01-01&make_type=full', 302) - # test posting to bad URLs - self.check_post({}, '/todo=', 404) - self.check_post({}, '/todo?id=', 404) - self.check_post({}, '/todo?id=FOO', 400) - self.check_post({}, '/todo?id=0', 404) # test posting naked entity todo1 = post_and_reload({}) self.assertEqual(todo1.children, []) @@ -294,10 +307,6 @@ class TestsWithServer(TestCaseWithServer): # test implicitly posting non-doneness todo1 = post_and_reload({}) self.assertEqual(todo1.is_done, False) - # test malformed adoptions - self.check_post({'adopt': 'foo'}, '/todo?id=1', 400) - self.check_post({'adopt': 1}, '/todo?id=1', 400) - self.check_post({'adopt': 2}, '/todo?id=1', 404) # test posting second todo of same process self.check_post({'day_comment': '', 'new_todo': 1, 'make_type': 'full'}, @@ -324,8 +333,8 @@ class TestsWithServer(TestCaseWithServer): def test_do_POST_day_todo_adoption(self) -> None: """Test Todos posted to Day view may adopt existing Todos.""" - form_data = self.post_process() - form_data = self.post_process(2, form_data | {'new_top_step': 1}) + form_data = self.post_process( + 2, self._proc1_form_data | {'new_top_step': 1}) form_data = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} self.check_post(form_data, '/day?date=2024-01-01&make_type=full', 302) form_data['new_todo'] = 2 @@ -339,7 +348,7 @@ class TestsWithServer(TestCaseWithServer): def test_do_POST_day_todo_multiple(self) -> None: """Test multiple Todos can be posted to Day view.""" - form_data = self.post_process() + # form_data = self.post_process() form_data = self.post_process(2) form_data = {'day_comment': '', 'new_todo': [1, 2], 'make_type': 'full'} @@ -393,8 +402,7 @@ class TestsWithServer(TestCaseWithServer): self.assertEqual(todo3.children, []) self.assertEqual(sorted(todo3.parents), sorted([todo2, todo1])) - form_data = self.post_process() - form_data = self.post_process(2, form_data | {'new_top_step': 1}) + self.post_process(2, self._proc1_form_data | {'new_top_step': 1}) check_adoption('2024-01-01', [1, 2]) check_adoption('2024-01-02', [2, 1]) check_nesting_adoption(3, '2024-01-03', [1, 2]) @@ -402,7 +410,6 @@ class TestsWithServer(TestCaseWithServer): def test_do_POST_day_todo_doneness(self) -> None: """Test Todo doneness can be posted to Day view.""" - self.post_process() form_data = {'day_comment': '', 'new_todo': [1], 'make_type': 'full'} self.check_post(form_data, '/day?date=2024-01-01&make_type=full', 302) todo = Todo.by_date(self.db_conn, '2024-01-01')[0] @@ -419,7 +426,6 @@ class TestsWithServer(TestCaseWithServer): def test_do_GET_todo(self) -> None: """Test GET /todo response codes.""" - self.post_process() form_data = {'day_comment': '', 'new_todo': 1, 'make_type': 'full'} self.check_post(form_data, '/day?date=2024-01-01&make_type=full', 302) self.check_get('/todo', 404) @@ -427,3 +433,4 @@ class TestsWithServer(TestCaseWithServer): self.check_get('/todo?id=foo', 400) self.check_get('/todo?id=0', 404) self.check_get('/todo?id=1', 200) + self.check_get('/todo?id=2', 404) -- 2.30.2