home · contact · privacy
Extend Conditions POST test to use new JSON interface.
[plomtask] / tests / conditions.py
1 """Test Conditions module."""
2 from json import loads as json_loads
3 from tests.utils import TestCaseWithDB, TestCaseWithServer, TestCaseSansDB
4 from plomtask.conditions import Condition
5 from plomtask.processes import Process
6 from plomtask.todos import Todo
7 from plomtask.exceptions import HandledException
8
9
10 class TestsSansDB(TestCaseSansDB):
11     """Tests requiring no DB setup."""
12     checked_class = Condition
13     do_id_test = True
14     versioned_defaults_to_test = {'title': 'UNNAMED', 'description': ''}
15
16
17 class TestsWithDB(TestCaseWithDB):
18     """Tests requiring DB, but not server setup."""
19     checked_class = Condition
20     default_init_kwargs = {'is_active': False}
21     test_versioneds = {'title': str, 'description': str}
22
23     def test_Condition_from_table_row(self) -> None:
24         """Test .from_table_row() properly reads in class from DB"""
25         self.check_from_table_row()
26         self.check_versioned_from_table_row('title', str)
27         self.check_versioned_from_table_row('description', str)
28
29     def test_Condition_by_id(self) -> None:
30         """Test .by_id(), including creation."""
31         self.check_by_id()
32
33     def test_Condition_all(self) -> None:
34         """Test .all()."""
35         self.check_all()
36
37     def test_Condition_singularity(self) -> None:
38         """Test pointers made for single object keep pointing to it."""
39         self.check_singularity('is_active', True)
40
41     def test_Condition_versioned_attributes_singularity(self) -> None:
42         """Test behavior of VersionedAttributes on saving (with .title)."""
43         self.check_versioned_singularity()
44
45     def test_Condition_remove(self) -> None:
46         """Test .remove() effects on DB and cache."""
47         self.check_remove()
48         proc = Process(None)
49         proc.save(self.db_conn)
50         todo = Todo(None, proc, False, '2024-01-01')
51         for depender in (proc, todo):
52             assert hasattr(depender, 'save')
53             assert hasattr(depender, 'set_conditions')
54             c = Condition(None)
55             c.save(self.db_conn)
56             depender.save(self.db_conn)
57             depender.set_conditions(self.db_conn, [c.id_], 'conditions')
58             depender.save(self.db_conn)
59             with self.assertRaises(HandledException):
60                 c.remove(self.db_conn)
61             depender.set_conditions(self.db_conn, [], 'conditions')
62             depender.save(self.db_conn)
63             c.remove(self.db_conn)
64
65
66 class TestsWithServer(TestCaseWithServer):
67     """Module tests against our HTTP server/handler (and database)."""
68
69     def test_do_POST_condition(self) -> None:
70         """Test POST /condition and its effect on the database."""
71
72         def check(path: str, expected: dict[str, object]) -> None:
73             self.conn.request('GET', path)
74             response = self.conn.getresponse()
75             self.assertEqual(response.status, 200)
76             retrieved = json_loads(response.read().decode())
77             self.blank_history_keys_in(retrieved)
78             self.assertEqual(expected, retrieved)
79
80         # check empty POST fails
81         self.check_post({}, '/condition', 400)
82         # test valid POST's effect on …
83         post = {'title': 'foo', 'description': 'oof', 'is_active': False}
84         self.check_post(post, '/condition', 302, '/condition?id=1')
85         # … single /condition
86         cond = {'id': 1, 'is_active': False,
87                 'title': {'parent_id': 1, 'history': {'[0]': 'foo'}},
88                 'description': {'parent_id': 1, 'history': {'[0]': 'oof'}}}
89         expected_single = {'is_new': False,
90                            'enabled_processes': [],
91                            'disabled_processes': [],
92                            'enabling_processes': [],
93                            'disabling_processes': [],
94                            'condition': cond}
95         check('/condition?id=1', expected_single)
96         # … full /conditions
97         expected_all = {'conditions': [cond], 'sort_by': 'title', 'pattern': ''}
98         check('/conditions', expected_all)
99         # test effect of invalid POST to existing Condition on /condition
100         self.check_post({}, '/condition?id=1', 400)
101         check('/condition?id=1', expected_single)
102         # test deletion POST's effect on …
103         self.check_post({'delete': ''}, '/condition?id=1', 302, '/conditions')
104         cond['title']['history'] = {}
105         cond['description']['history'] = {}
106         check('/condition?id=1', expected_single)
107         # … full /conditions
108         expected_all['conditions'] = []
109         check('/conditions', expected_all)
110
111     def test_do_GET_condition(self) -> None:
112         """Test GET /condition."""
113         form_data = {'title': 'foo', 'description': 'foo', 'is_active': False}
114         self.check_post(form_data, '/condition', 302, '/condition?id=1')
115         self.check_get_defaults('/condition')
116
117     def test_do_GET_conditions(self) -> None:
118         """Test GET /conditions."""
119
120         def check(params: str, expected_json: dict[str, object]) -> None:
121             self.conn.request('GET', f'/conditions{params}')
122             response = self.conn.getresponse()
123             self.assertEqual(response.status, 200)
124             retrieved_json = json_loads(response.read().decode())
125             self.blank_history_keys_in(retrieved_json)
126             self.assertEqual(expected_json, retrieved_json)
127
128         # test empty result on empty DB, default-settings on empty params
129         expected_json: dict[str, object] = {'conditions': [],
130                                             'sort_by': 'title',
131                                             'pattern': ''}
132         check('', expected_json)
133         # test on meaningless non-empty params (incl. entirely un-used key)
134         expected_json = {'conditions': [],
135                          'sort_by': 'title',  # nonsense "foo" defaulting
136                          'pattern': 'bar'}  # preserved despite zero effect
137         check('?sort_by=foo&pattern=bar&foo=x', expected_json)
138         # test non-empty result, automatic (positive) sorting by title
139         post_1 = {'title': 'foo', 'description': 'oof', 'is_active': False}
140         self.check_post(post_1, '/condition', 302, '/condition?id=1')
141         post_2 = {'title': 'bar', 'description': 'rab', 'is_active': False}
142         self.check_post(post_2, '/condition', 302, '/condition?id=2')
143         post_3 = {'title': 'baz', 'description': 'zab', 'is_active': True}
144         self.check_post(post_3, '/condition', 302, '/condition?id=3')
145         cond_1 = {'id': 1, 'is_active': False,
146                   'title': {'history': {'[0]': 'foo'},
147                             'parent_id': 1},
148                            'description': {'history': {'[0]': 'oof'},
149                                            'parent_id': 1}}
150         cond_2 = {'id': 2, 'is_active': False,
151                   'title': {'history': {'[0]': 'bar'},
152                             'parent_id': 2},
153                   'description': {'history': {'[0]': 'rab'},
154                                   'parent_id': 2}}
155         cond_3 = {'id': 3, 'is_active': True,
156                   'title': {'history': {'[0]': 'baz'},
157                             'parent_id': 3},
158                   'description': {'history': {'[0]': 'zab'},
159                                   'parent_id': 3}}
160         cons = [cond_2, cond_3, cond_1]
161         expected_json = {'conditions': cons, 'sort_by': 'title', 'pattern': ''}
162         check('', expected_json)
163         # test other sortings
164         # (NB: by .is_active has two items of =False, their order currently
165         # is not explicitly made predictable, so mail fail until we do)
166         expected_json['conditions'] = [cond_1, cond_3, cond_2]
167         expected_json['sort_by'] = '-title'
168         check('?sort_by=-title', expected_json)
169         expected_json['conditions'] = [cond_1, cond_2, cond_3]
170         expected_json['sort_by'] = 'is_active'
171         check('?sort_by=is_active', expected_json)
172         expected_json['conditions'] = [cond_3, cond_1, cond_2]
173         expected_json['sort_by'] = '-is_active'
174         check('?sort_by=-is_active', expected_json)
175         # test pattern matching on title
176         expected_json = {'conditions': [cond_2, cond_3],
177                          'sort_by': 'title', 'pattern': 'ba'}
178         check('?pattern=ba', expected_json)
179         # test pattern matching on description
180         expected_json['conditions'] = [cond_1]
181         expected_json['pattern'] = 'oo'
182         check('?pattern=oo', expected_json)