from unittest.mock import patch import pytest from notifications_utils.template import SubjectMixin, Template class ConcreteImplementation: template_type = None # Can’t instantiate and test templates unless they implement __str__ def __str__(self): pass class ConcreteTemplate(ConcreteImplementation, Template): pass class ConcreteTemplateWithSubject(SubjectMixin, ConcreteTemplate): pass def test_class(): assert ( repr(ConcreteTemplate({"content": "hello ((name))"})) == 'ConcreteTemplate("hello ((name))", {})' ) def test_passes_through_template_attributes(): assert ConcreteTemplate({"content": ""}).name is None assert ( ConcreteTemplate({"content": "", "name": "Two week reminder"}).name == "Two week reminder" ) assert ConcreteTemplate({"content": ""}).id is None assert ConcreteTemplate({"content": "", "id": "1234"}).id == "1234" assert ConcreteTemplate({"content": ""}).template_type is None def test_passes_through_subject(): assert ( ConcreteTemplateWithSubject( {"content": "", "subject": "Your tax is due"} ).subject == "Your tax is due" ) def test_errors_for_missing_template_content(): with pytest.raises(KeyError): ConcreteTemplate({}) @pytest.mark.parametrize("template", [0, 1, 2, True, False, None]) def test_errors_for_invalid_template_types(template): with pytest.raises(TypeError): ConcreteTemplate(template) @pytest.mark.parametrize("values", [[], False]) def test_errors_for_invalid_values(values): with pytest.raises(TypeError): ConcreteTemplate({"content": ""}, values) def test_matches_keys_to_placeholder_names(): template = ConcreteTemplate({"content": "hello ((name))"}) template.values = {"NAME": "Chris"} assert template.values == {"name": "Chris"} template.values = {"NAME": "Chris", "Town": "London"} assert template.values == {"name": "Chris", "Town": "London"} assert template.additional_data == {"Town"} template.values = None assert template.missing_data == ["name"] @pytest.mark.parametrize( ("template_content", "template_subject", "expected"), [ ("the quick brown fox", "jumps", []), ("the quick ((colour)) fox", "jumps", ["colour"]), ("the quick ((colour)) ((animal))", "jumps", ["colour", "animal"]), ("((colour)) ((animal)) ((colour)) ((animal))", "jumps", ["colour", "animal"]), ("the quick brown fox", "((colour))", ["colour"]), ("the quick ((colour)) ", "((animal))", ["animal", "colour"]), ("((colour)) ((animal)) ", "((colour)) ((animal))", ["colour", "animal"]), ("Dear ((name)), ((warning?? This is a warning))", "", ["name", "warning"]), ("((warning? one question mark))", "", ["warning? one question mark"]), ], ) def test_extracting_placeholders(template_content, template_subject, expected): assert ( ConcreteTemplateWithSubject( {"content": template_content, "subject": template_subject} ).placeholders == expected ) def test_random_variable_retrieve(): template = ConcreteTemplate({"content": "content", "created_by": "now"}) assert template.get_raw("created_by") == "now" assert template.get_raw("missing", default="random") == "random" assert template.get_raw("missing") is None def test_compare_template(): with patch( "notifications_utils.template_change.TemplateChange.__init__", return_value=None ) as mocked: old_template = ConcreteTemplate({"content": "faked"}) new_template = ConcreteTemplate({"content": "faked"}) old_template.compare_to(new_template) mocked.assert_called_once_with(old_template, new_template)