1 """Test Todos module."""
2 from tests.utils import TestCaseWithDB, TestCaseWithServer
3 from plomtask.todos import Todo
4 from plomtask.days import Day
5 from plomtask.processes import Process
6 from plomtask.conditions import Condition
7 from plomtask.exceptions import (NotFoundException, BadFormatException,
11 class TestsWithDB(TestCaseWithDB):
12 """Tests requiring DB, but not server setup."""
14 def setUp(self) -> None:
16 self.day1 = Day('2024-01-01')
17 self.day1.save(self.db_conn)
18 self.day2 = Day('2024-01-02')
19 self.day2.save(self.db_conn)
20 self.proc = Process(None)
21 self.proc.save_without_steps(self.db_conn)
22 self.cond1 = Condition(None)
23 self.cond1.save(self.db_conn)
24 self.cond2 = Condition(None)
25 self.cond2.save(self.db_conn)
27 def test_Todo_by_id(self) -> None:
28 """Test creation and findability of Todos."""
29 process_unsaved = Process(None)
30 todo = Todo(None, process_unsaved, False, self.day1)
31 with self.assertRaises(NotFoundException):
32 todo.save(self.db_conn)
33 process_unsaved.save_without_steps(self.db_conn)
34 todo.save(self.db_conn)
35 self.assertEqual(Todo.by_id(self.db_conn, 1), todo)
36 with self.assertRaises(NotFoundException):
37 self.assertEqual(Todo.by_id(self.db_conn, 0), todo)
38 with self.assertRaises(NotFoundException):
39 self.assertEqual(Todo.by_id(self.db_conn, 2), todo)
41 def test_Todo_by_date(self) -> None:
42 """Test findability of Todos by date."""
43 t1 = Todo(None, self.proc, False, self.day1)
45 t2 = Todo(None, self.proc, False, self.day1)
47 self.assertEqual(Todo.by_date(self.db_conn, self.day1.date), [t1, t2])
48 self.assertEqual(Todo.by_date(self.db_conn, self.day2.date), [])
49 self.assertEqual(Todo.by_date(self.db_conn, 'foo'), [])
51 def test_Todo_from_process(self) -> None:
52 """Test spawning of Todo attributes from Process."""
53 assert self.cond1.id_ is not None
54 assert self.cond2.id_ is not None
55 self.proc.set_conditions(self.db_conn, [self.cond1.id_])
56 todo = Todo(None, self.proc, False, self.day1)
57 self.assertEqual(todo.conditions, [self.cond1])
58 todo.set_conditions(self.db_conn, [self.cond2.id_])
59 self.assertEqual(todo.conditions, [self.cond2])
60 self.assertEqual(self.proc.conditions, [self.cond1])
61 self.proc.set_fulfills(self.db_conn, [self.cond1.id_])
62 todo = Todo(None, self.proc, False, self.day1)
63 self.assertEqual(todo.fulfills, [self.cond1])
64 todo.set_fulfills(self.db_conn, [self.cond2.id_])
65 self.assertEqual(todo.fulfills, [self.cond2])
66 self.assertEqual(self.proc.fulfills, [self.cond1])
67 self.proc.set_undoes(self.db_conn, [self.cond1.id_])
68 todo = Todo(None, self.proc, False, self.day1)
69 self.assertEqual(todo.undoes, [self.cond1])
70 todo.set_undoes(self.db_conn, [self.cond2.id_])
71 self.assertEqual(todo.undoes, [self.cond2])
72 self.assertEqual(self.proc.undoes, [self.cond1])
74 def test_Todo_on_conditions(self) -> None:
75 """Test effect of Todos on Conditions."""
76 assert self.cond1.id_ is not None
77 assert self.cond2.id_ is not None
78 todo = Todo(None, self.proc, False, self.day1)
79 todo.save(self.db_conn)
80 todo.set_fulfills(self.db_conn, [self.cond1.id_])
81 todo.set_undoes(self.db_conn, [self.cond2.id_])
83 self.assertEqual(self.cond1.is_active, True)
84 self.assertEqual(self.cond2.is_active, False)
86 self.assertEqual(self.cond1.is_active, True)
87 self.assertEqual(self.cond2.is_active, False)
89 def test_Todo_enablers_disablers(self) -> None:
90 """Test Todo.enablers_for_at/disablers_for_at."""
91 assert self.cond1.id_ is not None
92 assert self.cond2.id_ is not None
93 todo1 = Todo(None, self.proc, False, self.day1)
94 todo1.save(self.db_conn)
95 todo1.set_fulfills(self.db_conn, [self.cond1.id_])
96 todo1.set_undoes(self.db_conn, [self.cond2.id_])
97 todo1.save(self.db_conn)
98 todo2 = Todo(None, self.proc, False, self.day1)
99 todo2.save(self.db_conn)
100 todo2.set_fulfills(self.db_conn, [self.cond2.id_])
101 todo2.save(self.db_conn)
102 todo3 = Todo(None, self.proc, False, self.day2)
103 todo3.save(self.db_conn)
104 todo3.set_fulfills(self.db_conn, [self.cond2.id_])
105 todo3.save(self.db_conn)
106 date1 = self.day1.date
107 date2 = self.day2.date
108 enablers = Todo.enablers_for_at(self.db_conn, self.cond1, date1)
109 self.assertEqual(enablers, [todo1])
110 enablers = Todo.enablers_for_at(self.db_conn, self.cond1, date2)
111 self.assertEqual(enablers, [])
112 disablers = Todo.disablers_for_at(self.db_conn, self.cond1, date1)
113 self.assertEqual(disablers, [])
114 disablers = Todo.disablers_for_at(self.db_conn, self.cond1, date2)
115 self.assertEqual(disablers, [])
116 enablers = Todo.enablers_for_at(self.db_conn, self.cond2, date1)
117 self.assertEqual(enablers, [todo2])
118 enablers = Todo.enablers_for_at(self.db_conn, self.cond2, date2)
119 self.assertEqual(enablers, [todo3])
120 disablers = Todo.disablers_for_at(self.db_conn, self.cond2, date1)
121 self.assertEqual(disablers, [todo1])
122 disablers = Todo.disablers_for_at(self.db_conn, self.cond2, date2)
123 self.assertEqual(disablers, [])
125 def test_Todo_children(self) -> None:
126 """Test Todo.children relations."""
127 todo_1 = Todo(None, self.proc, False, self.day1)
128 todo_2 = Todo(None, self.proc, False, self.day1)
129 with self.assertRaises(HandledException):
130 todo_1.add_child(todo_2)
131 todo_1.save(self.db_conn)
132 with self.assertRaises(HandledException):
133 todo_1.add_child(todo_2)
134 todo_2.save(self.db_conn)
135 todo_1.add_child(todo_2)
136 todo_1.save(self.db_conn)
137 todo_retrieved = Todo.by_id(self.db_conn, todo_1.id_)
138 self.assertEqual(todo_retrieved.children, [todo_2])
139 with self.assertRaises(BadFormatException):
140 todo_2.add_child(todo_1)
142 def test_Todo_conditioning(self) -> None:
143 """Test Todo.doability conditions."""
144 assert self.cond1.id_ is not None
145 todo_1 = Todo(None, self.proc, False, self.day1)
146 todo_1.save(self.db_conn)
147 todo_2 = Todo(None, self.proc, False, self.day1)
148 todo_2.save(self.db_conn)
149 todo_2.add_child(todo_1)
150 with self.assertRaises(BadFormatException):
151 todo_2.is_done = True
152 todo_1.is_done = True
153 todo_2.is_done = True
154 todo_2.is_done = False
155 todo_2.set_conditions(self.db_conn, [self.cond1.id_])
156 with self.assertRaises(BadFormatException):
157 todo_2.is_done = True
158 self.cond1.is_active = True
159 todo_2.is_done = True
161 def test_Todo_singularity(self) -> None:
162 """Test pointers made for single object keep pointing to it."""
163 todo = Todo(None, self.proc, False, self.day1)
164 todo.save(self.db_conn)
165 retrieved_todo = Todo.by_id(self.db_conn, 1)
167 self.assertEqual(retrieved_todo.is_done, True)
168 retrieved_todo = Todo.by_date(self.db_conn, self.day1.date)[0]
169 retrieved_todo.is_done = False
170 self.assertEqual(todo.is_done, False)
173 class TestsWithServer(TestCaseWithServer):
174 """Tests against our HTTP server/handler (and database)."""
176 def test_do_POST_day(self) -> None:
177 """Test Todo posting of POST /day."""
178 form_data = {'title': '', 'description': '', 'effort': 1}
179 self.check_post(form_data, '/process?id=', 302, '/')
180 self.check_post(form_data, '/process?id=', 302, '/')
181 proc = Process.by_id(self.db_conn, 1)
182 proc2 = Process.by_id(self.db_conn, 2)
183 form_data = {'comment': ''}
184 self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
185 self.assertEqual(Todo.by_date(self.db_conn, '2024-01-01'), [])
186 form_data['new_todo'] = str(proc.id_)
187 self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
188 todos = Todo.by_date(self.db_conn, '2024-01-01')
189 self.assertEqual(1, len(todos))
191 self.assertEqual(todo1.id_, 1)
192 self.assertEqual(todo1.process.id_, proc.id_)
193 self.assertEqual(todo1.is_done, False)
194 form_data['new_todo'] = str(proc2.id_)
195 self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
196 todos = Todo.by_date(self.db_conn, '2024-01-01')
198 self.assertEqual(todo1.id_, 2)
199 self.assertEqual(todo1.process.id_, proc2.id_)
200 self.assertEqual(todo1.is_done, False)
202 def test_do_POST_todo(self) -> None:
203 """Test POST /todo."""
204 def post_and_reload(form_data: dict[str, object],
205 status: int = 302) -> Todo:
206 self.check_post(form_data, '/todo?id=1', status, '/')
207 self.db_conn.cached_todos = {}
208 return Todo.by_date(self.db_conn, '2024-01-01')[0]
209 form_data = {'title': '', 'description': '', 'effort': 1}
210 self.check_post(form_data, '/process', 302, '/')
211 form_data = {'comment': '', 'new_todo': 1}
212 self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
214 self.check_post(form_data, '/todo=', 404)
215 self.check_post(form_data, '/todo?id=', 404)
216 self.check_post(form_data, '/todo?id=FOO', 400)
217 self.check_post(form_data, '/todo?id=0', 404)
218 todo1 = post_and_reload(form_data)
219 self.assertEqual(todo1.children, [])
220 self.assertEqual(todo1.parents, [])
221 self.assertEqual(todo1.is_done, False)
222 form_data = {'done': ''}
223 todo1 = post_and_reload(form_data)
224 self.assertEqual(todo1.is_done, True)
226 todo1 = post_and_reload(form_data)
227 self.assertEqual(todo1.is_done, False)
228 form_data = {'adopt': 'foo'}
229 self.check_post(form_data, '/todo?id=1', 400)
230 form_data = {'adopt': 1}
231 self.check_post(form_data, '/todo?id=1', 400)
232 form_data = {'adopt': 2}
233 self.check_post(form_data, '/todo?id=1', 404)
234 form_data = {'comment': '', 'new_todo': 1}
235 self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
236 form_data = {'adopt': 2}
237 todo1 = post_and_reload(form_data)
238 todo2 = Todo.by_date(self.db_conn, '2024-01-01')[1]
239 self.assertEqual(todo1.children, [todo2])
240 self.assertEqual(todo1.parents, [])
241 self.assertEqual(todo2.children, [])
242 self.assertEqual(todo2.parents, [todo1])
243 self.check_post(form_data, '/todo?id=1', 400, '/')
244 form_data = {'done': ''}
245 todo1 = post_and_reload(form_data, 400)
246 self.assertEqual(todo1.is_done, False)
248 def test_do_GET_todo(self) -> None:
249 """Test GET /todo response codes."""
250 form_data = {'title': '', 'description': '', 'effort': 1}
251 self.check_post(form_data, '/process?id=', 302, '/')
252 form_data = {'comment': '', 'new_todo': 1}
253 self.check_post(form_data, '/day?date=2024-01-01', 302, '/')
254 self.check_get('/todo', 404)
255 self.check_get('/todo?id=', 404)
256 self.check_get('/todo?id=foo', 400)
257 self.check_get('/todo?id=0', 404)
258 self.check_get('/todo?id=1', 200)