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():
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')
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')
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)
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):
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)
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, [])
# 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'},
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
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'}
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])
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]
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)
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)