Coverage for cc_modules/tests/cc_forms_tests.py : 19%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/tests/cc_forms_tests.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
12 CamCOPS is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
17 CamCOPS is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
25===============================================================================
27"""
29import json
30from pprint import pformat
31from typing import Any, Dict
32from unittest import mock, TestCase
34# noinspection PyProtectedMember
35from colander import Invalid, null, Schema
36from pendulum import Duration
38from camcops_server.cc_modules.cc_baseconstants import TEMPLATE_DIR
39from camcops_server.cc_modules.cc_forms import (
40 DurationType,
41 DurationWidget,
42 GroupIpUseWidget,
43 IpUseType,
44 JsonType,
45 JsonWidget,
46 LoginSchema,
47 TaskScheduleItemSchema,
48 TaskScheduleNode,
49 TaskScheduleSchema,
50)
51from camcops_server.cc_modules.cc_ipuse import IpContexts
52from camcops_server.cc_modules.cc_pyramid import ViewParam
53from camcops_server.cc_modules.cc_taskschedule import TaskSchedule
54from camcops_server.cc_modules.cc_unittest import (
55 DemoDatabaseTestCase,
56 DemoRequestTestCase,
57)
60# =============================================================================
61# Unit tests
62# =============================================================================
64class SchemaTestCase(DemoRequestTestCase):
65 """
66 Unit tests.
67 """
68 def serialize_deserialize(self,
69 schema: Schema,
70 appstruct: Dict[str, Any]) -> None:
71 cstruct = schema.serialize(appstruct)
72 final = schema.deserialize(cstruct)
73 mismatch = False
74 for k, v in appstruct.items():
75 if final[k] != v:
76 mismatch = True
77 break
78 self.assertFalse(mismatch, msg=(
79 "Elements of final don't match corresponding elements of starting "
80 "appstruct:\n"
81 f"final = {pformat(final)}\n"
82 f"start = {pformat(appstruct)}"
83 ))
86class LoginSchemaTests(SchemaTestCase):
87 def test_serialize_deserialize(self) -> None:
88 appstruct = {
89 ViewParam.USERNAME: "testuser",
90 ViewParam.PASSWORD: "testpw",
91 }
92 schema = LoginSchema().bind(request=self.req)
94 self.serialize_deserialize(schema, appstruct)
97class TaskScheduleSchemaTests(DemoDatabaseTestCase):
98 def test_invalid_for_bad_template_placeholder(self) -> None:
99 schema = TaskScheduleSchema().bind(request=self.req)
100 appstruct = {
101 ViewParam.NAME: "test",
102 ViewParam.GROUP_ID: str(self.group.id),
103 ViewParam.EMAIL_SUBJECT: "Subject",
104 ViewParam.EMAIL_TEMPLATE: "{bad_key}",
105 }
107 cstruct = schema.serialize(appstruct)
108 with self.assertRaises(Invalid) as cm:
109 schema.deserialize(cstruct)
111 self.assertIn("'bad_key' is not a valid placeholder",
112 cm.exception.children[0].messages()[0])
114 def test_invalid_for_mismatched_braces(self) -> None:
115 schema = TaskScheduleSchema().bind(request=self.req)
116 appstruct = {
117 ViewParam.NAME: "test",
118 ViewParam.GROUP_ID: str(self.group.id),
119 ViewParam.EMAIL_SUBJECT: "Subject",
120 ViewParam.EMAIL_TEMPLATE: "{server_url",
121 }
123 cstruct = schema.serialize(appstruct)
124 with self.assertRaises(Invalid) as cm:
125 schema.deserialize(cstruct)
127 self.assertIn("Invalid email template",
128 cm.exception.children[0].messages()[0])
131class TaskScheduleItemSchemaTests(SchemaTestCase):
132 def test_serialize_deserialize(self) -> None:
133 appstruct = {
134 ViewParam.SCHEDULE_ID: 1,
135 ViewParam.TABLE_NAME: "bmi",
136 ViewParam.CLINICIAN_CONFIRMATION: False,
137 ViewParam.DUE_FROM: Duration(days=90),
138 ViewParam.DUE_WITHIN: Duration(days=100)
139 }
140 schema = TaskScheduleItemSchema().bind(request=self.req)
141 self.serialize_deserialize(schema, appstruct)
143 def test_invalid_for_clinician_task_with_no_confirmation(self) -> None:
144 schema = TaskScheduleItemSchema().bind(request=self.req)
145 appstruct = {
146 ViewParam.SCHEDULE_ID: 1,
147 ViewParam.TABLE_NAME: "elixhauserci",
148 ViewParam.CLINICIAN_CONFIRMATION: False,
149 ViewParam.DUE_FROM: Duration(days=90),
150 ViewParam.DUE_WITHIN: Duration(days=100)
151 }
153 cstruct = schema.serialize(appstruct)
154 with self.assertRaises(Invalid) as cm:
155 schema.deserialize(cstruct)
157 self.assertIn("you must tick 'Allow clinician tasks'",
158 cm.exception.messages()[0])
160 def test_valid_for_clinician_task_with_confirmation(self) -> None:
161 schema = TaskScheduleItemSchema().bind(request=mock.Mock())
162 appstruct = {
163 ViewParam.SCHEDULE_ID: 1,
164 ViewParam.TABLE_NAME: "elixhauserci",
165 ViewParam.CLINICIAN_CONFIRMATION: True,
166 ViewParam.DUE_FROM: Duration(days=90),
167 ViewParam.DUE_WITHIN: Duration(days=100)
168 }
170 try:
171 schema.serialize(appstruct)
172 except Invalid:
173 self.fail("Validation failed unexpectedly")
175 def test_invalid_for_zero_due_within(self) -> None:
176 schema = TaskScheduleItemSchema().bind(request=self.req)
177 appstruct = {
178 ViewParam.SCHEDULE_ID: 1,
179 ViewParam.TABLE_NAME: "phq9",
180 ViewParam.CLINICIAN_CONFIRMATION: False,
181 ViewParam.DUE_FROM: Duration(days=90),
182 ViewParam.DUE_WITHIN: Duration(days=0)
183 }
185 cstruct = schema.serialize(appstruct)
186 with self.assertRaises(Invalid) as cm:
187 schema.deserialize(cstruct)
189 self.assertIn("must be more than zero days",
190 cm.exception.messages()[0])
192 def test_invalid_for_negative_due_within(self) -> None:
193 schema = TaskScheduleItemSchema().bind(request=self.req)
194 appstruct = {
195 ViewParam.SCHEDULE_ID: 1,
196 ViewParam.TABLE_NAME: "phq9",
197 ViewParam.CLINICIAN_CONFIRMATION: False,
198 ViewParam.DUE_FROM: Duration(days=90),
199 ViewParam.DUE_WITHIN: Duration(days=-1)
200 }
202 cstruct = schema.serialize(appstruct)
203 with self.assertRaises(Invalid) as cm:
204 schema.deserialize(cstruct)
206 self.assertIn("must be more than zero days",
207 cm.exception.messages()[0])
209 def test_invalid_for_negative_due_from(self) -> None:
210 schema = TaskScheduleItemSchema().bind(request=self.req)
211 appstruct = {
212 ViewParam.SCHEDULE_ID: 1,
213 ViewParam.TABLE_NAME: "phq9",
214 ViewParam.CLINICIAN_CONFIRMATION: False,
215 ViewParam.DUE_FROM: Duration(days=-1),
216 ViewParam.DUE_WITHIN: Duration(days=10)
217 }
219 cstruct = schema.serialize(appstruct)
220 with self.assertRaises(Invalid) as cm:
221 schema.deserialize(cstruct)
223 self.assertIn("must be zero or more days",
224 cm.exception.messages()[0])
227class TaskScheduleItemSchemaIpTests(DemoDatabaseTestCase):
228 def setUp(self) -> None:
229 super().setUp()
231 self.schedule = TaskSchedule()
232 self.schedule.group_id = self.group.id
233 self.dbsession.add(self.schedule)
234 self.dbsession.commit()
236 def create_tasks(self) -> None:
237 # Speed things up a bit
238 pass
240 def test_invalid_for_commercial_mismatch(self) -> None:
241 self.group.ip_use.commercial = True
242 self.dbsession.add(self.group)
243 self.dbsession.commit()
245 schema = TaskScheduleItemSchema().bind(request=self.req)
246 appstruct = {
247 ViewParam.SCHEDULE_ID: self.schedule.id,
248 ViewParam.TABLE_NAME: "mfi20",
249 ViewParam.CLINICIAN_CONFIRMATION: False,
250 ViewParam.DUE_FROM: Duration(days=0),
251 ViewParam.DUE_WITHIN: Duration(days=10)
252 }
254 cstruct = schema.serialize(appstruct)
255 with self.assertRaises(Invalid) as cm:
256 schema.deserialize(cstruct)
258 self.assertIn("prohibits commercial",
259 cm.exception.messages()[0])
261 def test_invalid_for_clinical_mismatch(self) -> None:
262 self.group.ip_use.clinical = True
263 self.dbsession.add(self.group)
264 self.dbsession.commit()
266 schema = TaskScheduleItemSchema().bind(request=self.req)
267 appstruct = {
268 ViewParam.SCHEDULE_ID: self.schedule.id,
269 ViewParam.TABLE_NAME: "mfi20",
270 ViewParam.CLINICIAN_CONFIRMATION: False,
271 ViewParam.DUE_FROM: Duration(days=0),
272 ViewParam.DUE_WITHIN: Duration(days=10),
273 }
275 cstruct = schema.serialize(appstruct)
276 with self.assertRaises(Invalid) as cm:
277 schema.deserialize(cstruct)
279 self.assertIn("prohibits clinical",
280 cm.exception.messages()[0])
282 def test_invalid_for_educational_mismatch(self) -> None:
283 self.group.ip_use.educational = True
284 self.dbsession.add(self.group)
285 self.dbsession.commit()
287 schema = TaskScheduleItemSchema().bind(request=self.req)
288 appstruct = {
289 ViewParam.SCHEDULE_ID: self.schedule.id,
290 ViewParam.TABLE_NAME: "mfi20",
291 ViewParam.CLINICIAN_CONFIRMATION: True,
292 ViewParam.DUE_FROM: Duration(days=0),
293 ViewParam.DUE_WITHIN: Duration(days=10),
294 }
296 cstruct = schema.serialize(appstruct)
298 # No real world example prohibits educational use
299 mock_task_class = mock.Mock(prohibits_educational=True)
300 with mock.patch.object(schema, "_get_task_class",
301 return_value=mock_task_class):
302 with self.assertRaises(Invalid) as cm:
303 schema.deserialize(cstruct)
305 self.assertIn("prohibits educational",
306 cm.exception.messages()[0])
308 def test_invalid_for_research_mismatch(self) -> None:
309 self.group.ip_use.research = True
310 self.dbsession.add(self.group)
311 self.dbsession.commit()
313 schema = TaskScheduleItemSchema().bind(request=self.req)
314 appstruct = {
315 ViewParam.SCHEDULE_ID: self.schedule.id,
316 ViewParam.TABLE_NAME: "moca",
317 ViewParam.CLINICIAN_CONFIRMATION: True,
318 ViewParam.DUE_FROM: Duration(days=0),
319 ViewParam.DUE_WITHIN: Duration(days=10),
320 }
322 cstruct = schema.serialize(appstruct)
323 with self.assertRaises(Invalid) as cm:
324 schema.deserialize(cstruct)
326 self.assertIn("prohibits research",
327 cm.exception.messages()[0])
329 def test_invalid_for_missing_ip_use(self) -> None:
330 self.group.ip_use = None
331 self.dbsession.add(self.group)
332 self.dbsession.commit()
334 schema = TaskScheduleItemSchema().bind(request=self.req)
335 appstruct = {
336 ViewParam.SCHEDULE_ID: self.schedule.id,
337 ViewParam.TABLE_NAME: "moca",
338 ViewParam.CLINICIAN_CONFIRMATION: True,
339 ViewParam.DUE_FROM: Duration(days=0),
340 ViewParam.DUE_WITHIN: Duration(days=10),
341 }
343 cstruct = schema.serialize(appstruct)
344 with self.assertRaises(Invalid) as cm:
345 schema.deserialize(cstruct)
347 self.assertIn(
348 f"The group '{self.group.name}' has no intellectual property "
349 f"settings",
350 cm.exception.messages()[0]
351 )
354class DurationWidgetTests(TestCase):
355 def setUp(self) -> None:
356 super().setUp()
357 self.request = mock.Mock(gettext=lambda t: t)
359 def test_serialize_renders_template_with_values(self) -> None:
360 widget = DurationWidget(self.request)
362 field = mock.Mock()
363 field.renderer = mock.Mock()
365 cstruct = {
366 "months": 1,
367 "weeks": 2,
368 "days": 3,
369 }
371 widget.serialize(field, cstruct, readonly=False)
373 args, kwargs = field.renderer.call_args
375 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/duration.pt")
376 self.assertFalse(kwargs["readonly"])
378 self.assertEqual(kwargs["months"], 1)
379 self.assertEqual(kwargs["weeks"], 2)
380 self.assertEqual(kwargs["days"], 3)
382 self.assertEqual(kwargs["field"], field)
384 def test_serialize_renders_readonly_template_with_values(self) -> None:
385 widget = DurationWidget(self.request)
387 field = mock.Mock()
388 field.renderer = mock.Mock()
390 cstruct = {
391 "months": 1,
392 "weeks": 2,
393 "days": 3,
394 }
396 widget.serialize(field, cstruct, readonly=True)
398 args, kwargs = field.renderer.call_args
400 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/readonly/duration.pt")
401 self.assertTrue(kwargs["readonly"])
403 def test_serialize_renders_readonly_template_if_widget_is_readonly(
404 self) -> None:
405 widget = DurationWidget(self.request, readonly=True)
407 field = mock.Mock()
408 field.renderer = mock.Mock()
410 cstruct = {
411 "months": 1,
412 "weeks": 2,
413 "days": 3,
414 }
416 widget.serialize(field, cstruct)
418 args, kwargs = field.renderer.call_args
420 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/readonly/duration.pt")
422 def test_serialize_with_null_defaults_to_blank_values(self) -> None:
423 widget = DurationWidget(self.request)
425 field = mock.Mock()
426 field.renderer = mock.Mock()
428 widget.serialize(field, null)
430 args, kwargs = field.renderer.call_args
432 self.assertEqual(kwargs["months"], "")
433 self.assertEqual(kwargs["weeks"], "")
434 self.assertEqual(kwargs["days"], "")
436 def test_serialize_none_defaults_to_blank_values(self) -> None:
437 widget = DurationWidget(self.request)
439 field = mock.Mock()
440 field.renderer = mock.Mock()
442 widget.serialize(field, None)
444 args, kwargs = field.renderer.call_args
446 self.assertEqual(kwargs["months"], "")
447 self.assertEqual(kwargs["weeks"], "")
448 self.assertEqual(kwargs["days"], "")
450 def test_deserialize_returns_valid_values(self) -> None:
451 widget = DurationWidget(self.request)
453 pstruct = {
454 "days": 1,
455 "weeks": 2,
456 "months": 3,
457 }
459 # noinspection PyTypeChecker
460 cstruct = widget.deserialize(None, pstruct)
462 self.assertEqual(cstruct["days"], 1)
463 self.assertEqual(cstruct["weeks"], 2)
464 self.assertEqual(cstruct["months"], 3)
466 def test_deserialize_defaults_to_zero_days(self) -> None:
467 widget = DurationWidget(self.request)
469 # noinspection PyTypeChecker
470 cstruct = widget.deserialize(None, {})
472 self.assertEqual(cstruct["days"], 0)
474 def test_deserialize_fails_validation(self) -> None:
475 widget = DurationWidget(self.request)
477 pstruct = {
478 "days": "abc",
479 "weeks": "def",
480 "months": "ghi",
481 }
483 with self.assertRaises(Invalid) as cm:
484 # noinspection PyTypeChecker
485 widget.deserialize(None, pstruct)
487 self.assertIn("Please enter a valid number of days or leave blank",
488 cm.exception.messages())
489 self.assertIn("Please enter a valid number of weeks or leave blank",
490 cm.exception.messages())
491 self.assertIn("Please enter a valid number of months or leave blank",
492 cm.exception.messages())
493 self.assertEqual(cm.exception.value, pstruct)
496class DurationTypeTests(TestCase):
497 def test_deserialize_valid_duration(self) -> None:
498 cstruct = {"days": 45}
500 duration_type = DurationType()
501 duration = duration_type.deserialize(None, cstruct)
502 assert duration is not None # for type checker
504 self.assertEqual(duration.days, 45)
506 def test_deserialize_none_returns_null(self) -> None:
507 duration_type = DurationType()
508 duration = duration_type.deserialize(None, None)
509 self.assertIsNone(duration)
511 def test_deserialize_ignores_invalid_days(self) -> None:
512 duration_type = DurationType()
513 cstruct = {"days": "abc", "months": 1, "weeks": 1}
514 duration = duration_type.deserialize(None, cstruct)
515 assert duration is not None # for type checker
517 self.assertEqual(duration.days, 37)
519 def test_deserialize_ignores_invalid_months(self) -> None:
520 duration_type = DurationType()
521 cstruct = {"days": 1, "months": "abc", "weeks": 1}
522 duration = duration_type.deserialize(None, cstruct)
523 assert duration is not None # for type checker
525 self.assertEqual(duration.days, 8)
527 def test_deserialize_ignores_invalid_weeks(self) -> None:
528 duration_type = DurationType()
529 cstruct = {"days": 1, "months": 1, "weeks": "abc"}
530 duration = duration_type.deserialize(None, cstruct)
531 assert duration is not None # for type checker
533 self.assertEqual(duration.days, 31)
535 def test_serialize_valid_duration(self) -> None:
536 duration = Duration(days=47)
538 duration_type = DurationType()
539 cstruct = duration_type.serialize(None, duration)
541 # For type checker
542 assert cstruct not in (null,)
543 cstruct: Dict[Any, Any]
545 self.assertEqual(cstruct["days"], 3)
546 self.assertEqual(cstruct["months"], 1)
547 self.assertEqual(cstruct["weeks"], 2)
549 def test_serialize_null_returns_null(self) -> None:
550 duration_type = DurationType()
551 cstruct = duration_type.serialize(None, null)
552 self.assertIs(cstruct, null)
555class JsonWidgetTests(TestCase):
556 def setUp(self) -> None:
557 super().setUp()
558 self.request = mock.Mock(gettext=lambda t: t)
560 def test_serialize_renders_template_with_values(self) -> None:
561 widget = JsonWidget(self.request)
563 field = mock.Mock()
564 field.renderer = mock.Mock()
566 cstruct = json.dumps({"a": "1", "b": "2", "c": "3"})
568 widget.serialize(field, cstruct, readonly=False)
570 args, kwargs = field.renderer.call_args
572 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/json.pt")
573 self.assertFalse(kwargs["readonly"])
575 self.assertEqual(kwargs["cstruct"], cstruct)
576 self.assertEqual(kwargs["field"], field)
578 def test_serialize_renders_readonly_template_with_values(self) -> None:
579 widget = JsonWidget(self.request)
581 field = mock.Mock()
582 field.renderer = mock.Mock()
584 cstruct = json.dumps({"a": "1", "b": "2", "c": "3"})
586 widget.serialize(field, cstruct, readonly=True)
588 args, kwargs = field.renderer.call_args
590 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/readonly/json.pt")
592 self.assertEqual(kwargs["cstruct"], cstruct)
593 self.assertEqual(kwargs["field"], field)
594 self.assertTrue(kwargs["readonly"])
596 def test_serialize_renders_readonly_template_if_widget_is_readonly(
597 self) -> None:
598 widget = JsonWidget(self.request, readonly=True)
600 field = mock.Mock()
601 field.renderer = mock.Mock()
603 json_text = json.dumps({"a": "1", "b": "2", "c": "3"})
604 widget.serialize(field, json_text)
606 args, kwargs = field.renderer.call_args
608 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/readonly/json.pt")
610 def test_serialize_with_null_defaults_to_empty_string(self) -> None:
611 widget = JsonWidget(self.request)
613 field = mock.Mock()
614 field.renderer = mock.Mock()
616 widget.serialize(field, null)
618 args, kwargs = field.renderer.call_args
620 self.assertEqual(kwargs["cstruct"], "")
622 def test_deserialize_passes_json(self) -> None:
623 widget = JsonWidget(self.request)
625 pstruct = json.dumps({"a": "1", "b": "2", "c": "3"})
627 # noinspection PyTypeChecker
628 cstruct = widget.deserialize(None, pstruct)
630 self.assertEqual(cstruct, pstruct)
632 def test_deserialize_defaults_to_empty_json_string(self) -> None:
633 widget = JsonWidget(self.request)
635 # noinspection PyTypeChecker
636 cstruct = widget.deserialize(None, "{}")
638 self.assertEqual(cstruct, "{}")
640 def test_deserialize_invalid_json_fails_validation(self) -> None:
641 widget = JsonWidget(self.request)
643 pstruct = "{"
645 with self.assertRaises(Invalid) as cm:
646 # noinspection PyTypeChecker
647 widget.deserialize(None, pstruct)
649 self.assertIn(
650 "Please enter valid JSON",
651 cm.exception.messages()[0]
652 )
654 self.assertEqual(cm.exception.value, "{")
657class JsonTypeTests(TestCase):
658 def test_deserialize_valid_json(self) -> None:
659 original = {"one": 1, "two": 2, "three": 3}
661 json_type = JsonType()
662 json_value = json_type.deserialize(None, json.dumps(original))
663 self.assertEqual(json_value, original)
665 def test_deserialize_null_returns_none(self) -> None:
666 json_type = JsonType()
667 json_value = json_type.deserialize(None, null)
668 self.assertIsNone(json_value)
670 def test_deserialize_none_returns_null(self) -> None:
671 json_type = JsonType()
672 json_value = json_type.deserialize(None, None)
673 self.assertIsNone(json_value)
675 def test_deserialize_invalid_json_returns_none(self) -> None:
676 json_type = JsonType()
677 json_value = json_type.deserialize(None, "{")
678 self.assertIsNone(json_value)
680 def test_serialize_valid_appstruct(self) -> None:
681 original = {"one": 1, "two": 2, "three": 3}
683 json_type = JsonType()
684 json_string = json_type.serialize(None, original)
685 self.assertEqual(json_string, json.dumps(original))
687 def test_serialize_null_returns_null(self) -> None:
688 json_type = JsonType()
689 json_string = json_type.serialize(None, null)
690 self.assertIs(json_string, null)
693class TaskScheduleNodeTests(TestCase):
695 def test_deserialize_not_a_json_object_fails_validation(self) -> None:
696 node = TaskScheduleNode()
697 with self.assertRaises(Invalid) as cm:
698 node.deserialize({})
700 self.assertIn(
701 "Please enter a valid JSON object",
702 cm.exception.messages()[0]
703 )
705 self.assertEqual(cm.exception.value, "[{}]")
708class GroupIpUseWidgetTests(TestCase):
709 def setUp(self) -> None:
710 super().setUp()
711 self.request = mock.Mock(gettext=lambda t: t)
713 def test_serialize_renders_template_with_values(self) -> None:
714 widget = GroupIpUseWidget(self.request)
716 field = mock.Mock()
717 field.renderer = mock.Mock()
719 cstruct = {
720 IpContexts.CLINICAL: False,
721 IpContexts.COMMERCIAL: False,
722 IpContexts.EDUCATIONAL: True,
723 IpContexts.RESEARCH: True,
724 }
726 widget.serialize(field, cstruct, readonly=False)
728 args, kwargs = field.renderer.call_args
730 self.assertEqual(args[0], f"{TEMPLATE_DIR}/deform/group_ip_use.pt")
731 self.assertFalse(kwargs["readonly"])
733 self.assertFalse(kwargs[IpContexts.CLINICAL])
734 self.assertFalse(kwargs[IpContexts.COMMERCIAL])
735 self.assertTrue(kwargs[IpContexts.EDUCATIONAL])
736 self.assertTrue(kwargs[IpContexts.RESEARCH])
737 self.assertEqual(kwargs["field"], field)
739 def test_serialize_renders_readonly_template(self) -> None:
740 widget = GroupIpUseWidget(self.request)
742 field = mock.Mock()
743 field.renderer = mock.Mock()
745 cstruct = {
746 IpContexts.CLINICAL: False,
747 IpContexts.COMMERCIAL: False,
748 IpContexts.EDUCATIONAL: True,
749 IpContexts.RESEARCH: True,
750 }
752 widget.serialize(field, cstruct, readonly=True)
754 args, kwargs = field.renderer.call_args
756 self.assertEqual(args[0],
757 f"{TEMPLATE_DIR}/deform/readonly/group_ip_use.pt")
758 self.assertTrue(kwargs["readonly"])
760 def test_serialize_readonly_widget_renders_readonly_template(self) -> None:
761 widget = GroupIpUseWidget(self.request, readonly=True)
763 field = mock.Mock()
764 field.renderer = mock.Mock()
766 cstruct = {
767 IpContexts.CLINICAL: False,
768 IpContexts.COMMERCIAL: False,
769 IpContexts.EDUCATIONAL: True,
770 IpContexts.RESEARCH: True,
771 }
773 widget.serialize(field, cstruct)
775 args, kwargs = field.renderer.call_args
777 self.assertEqual(args[0],
778 f"{TEMPLATE_DIR}/deform/readonly/group_ip_use.pt")
780 def test_serialize_with_null_defaults_to_false_values(self) -> None:
781 widget = GroupIpUseWidget(self.request)
783 field = mock.Mock()
784 field.renderer = mock.Mock()
786 widget.serialize(field, null)
788 args, kwargs = field.renderer.call_args
790 self.assertFalse(kwargs[IpContexts.CLINICAL])
791 self.assertFalse(kwargs[IpContexts.COMMERCIAL])
792 self.assertFalse(kwargs[IpContexts.EDUCATIONAL])
793 self.assertFalse(kwargs[IpContexts.RESEARCH])
795 def test_serialize_with_none_defaults_to_false_values(self) -> None:
796 widget = GroupIpUseWidget(self.request)
798 field = mock.Mock()
799 field.renderer = mock.Mock()
801 widget.serialize(field, None)
803 args, kwargs = field.renderer.call_args
805 self.assertFalse(kwargs[IpContexts.CLINICAL])
806 self.assertFalse(kwargs[IpContexts.COMMERCIAL])
807 self.assertFalse(kwargs[IpContexts.EDUCATIONAL])
808 self.assertFalse(kwargs[IpContexts.RESEARCH])
810 def test_deserialize_with_null_defaults_to_false_values(self) -> None:
811 widget = GroupIpUseWidget(self.request)
813 field = None # Not used
814 # noinspection PyTypeChecker
815 cstruct = widget.deserialize(field, null)
817 self.assertFalse(cstruct[IpContexts.CLINICAL])
818 self.assertFalse(cstruct[IpContexts.COMMERCIAL])
819 self.assertFalse(cstruct[IpContexts.EDUCATIONAL])
820 self.assertFalse(cstruct[IpContexts.RESEARCH])
822 def test_deserialize_converts_to_bool_values(self) -> None:
823 widget = GroupIpUseWidget(self.request)
825 field = None # Not used
827 # It shouldn't matter what the values are set to so long as the keys
828 # are present. In practice the values will be set to "1"
829 pstruct = {
830 IpContexts.EDUCATIONAL: "1",
831 IpContexts.RESEARCH: "1",
832 }
834 # noinspection PyTypeChecker
835 cstruct = widget.deserialize(field, pstruct)
837 self.assertFalse(cstruct[IpContexts.CLINICAL])
838 self.assertFalse(cstruct[IpContexts.COMMERCIAL])
839 self.assertTrue(cstruct[IpContexts.EDUCATIONAL])
840 self.assertTrue(cstruct[IpContexts.RESEARCH])
843class IpUseTypeTests(TestCase):
844 def test_deserialize_none_returns_none(self) -> None:
845 ip_use_type = IpUseType()
847 node = None # not used
848 self.assertIsNone(ip_use_type.deserialize(node, None), None)
850 def test_deserialize_null_returns_none(self) -> None:
851 ip_use_type = IpUseType()
853 node = None # not used
854 self.assertIsNone(ip_use_type.deserialize(node, null), None)
856 def test_deserialize_returns_ip_use_object(self) -> None:
857 ip_use_type = IpUseType()
859 node = None # not used
861 cstruct = {
862 IpContexts.CLINICAL: False,
863 IpContexts.COMMERCIAL: True,
864 IpContexts.EDUCATIONAL: False,
865 IpContexts.RESEARCH: True,
866 }
867 ip_use = ip_use_type.deserialize(node, cstruct)
869 self.assertFalse(ip_use.clinical)
870 self.assertTrue(ip_use.commercial)
871 self.assertFalse(ip_use.educational)
872 self.assertTrue(ip_use.research)