Repository: awdeorio/mailmerge
Pull request #51 (2019-11-01 12:19:11)

Strict jinja
Show an error to the user if Jinja template uses a variable that's not provided in the context.


Patch:
diff --git a/mailmerge/api.py b/mailmerge/api.py
index 44f1b24..8a79dfc 100644
--- a/mailmerge/api.py
+++ b/mailmerge/api.py
@@ -317,10 +317,14 @@ def main(sample=False,
         sys.exit(1)
 
     try:
-        # Read template
-        with io.open(template_filename, "r") as template_file:
-            content = template_file.read() + u"\n"
-            template = jinja2.Template(content)
+        # Configure Jinja2 template engine
+        template_env = jinja2.Environment(
+            loader=jinja2.FileSystemLoader(os.path.dirname(template_filename)),
+            undefined=jinja2.StrictUndefined,
+        )
+        template = template_env.get_template(
+            os.path.basename(template_filename),
+        )
 
         # Read CSV file database
         database = []


Test patch:
diff --git a/tests/test_mailmerge.py b/tests/test_mailmerge.py
index 65691e1..3edc32a 100644
--- a/tests/test_mailmerge.py
+++ b/tests/test_mailmerge.py
@@ -2,6 +2,7 @@
 import os
 import email
 import cd
+import pytest
 import markdown
 import mailmerge
 
@@ -44,6 +45,22 @@ def test_stdout(capsys):
     assert ">>> sent message 1 DRY RUN" in stdout
 
 
+@mock.patch('smtplib.SMTP')
+def test_bad_jinja(mock_SMTP):
+    """Bad jinja template should produce an error."""
+    with pytest.raises(SystemExit):
+        mailmerge.api.main(
+            database_filename=os.path.join(TESTDATA, "simple_database.csv"),
+            template_filename=os.path.join(TESTDATA, "bad_template.txt"),
+            config_filename=os.path.join(TESTDATA, "server_open.conf"),
+            dry_run=False,
+        )
+
+    # Verify no emails were sent
+    smtp = mock_SMTP.return_value
+    assert smtp.sendmail.call_count == 0
+
+
 @mock.patch('smtplib.SMTP')
 def test_smtp(mock_SMTP):
     """Verify SMTP library calls."""
@@ -223,8 +240,8 @@ def test_utf8_template(mock_SMTP):
     # Verify content
     # NOTE: to decode a base46-encoded string:
     # print((str(base64.b64decode(payload), "utf-8")))
-    payload = message.get_payload()
-    assert payload == 'RnJvbSB0aGUgVGFnZWxpZWQgb2YgV29sZnJhbSB2b24gRXNjaGVuYmFjaCAoTWlkZGxlIEhpZ2gg\nR2VybWFuKToKClPDrm5lIGtsw6J3ZW4gZHVyaCBkaWUgd29sa2VuIHNpbnQgZ2VzbGFnZW4sCmVy\nIHN0w65nZXQgw7tmIG1pdCBncsO0emVyIGtyYWZ0LAppY2ggc2loIGluIGdyw6J3ZW4gdMOkZ2Vs\nw65jaCBhbHMgZXIgd2lsIHRhZ2VuLApkZW4gdGFjLCBkZXIgaW0gZ2VzZWxsZXNjaGFmdAplcndl\nbmRlbiB3aWwsIGRlbSB3ZXJkZW4gbWFuLApkZW4gaWNoIG1pdCBzb3JnZW4gw65uIHZlcmxpZXou\nCmljaCBicmluZ2UgaW4gaGlubmVuLCBvYiBpY2gga2FuLgpzw65uIHZpbCBtYW5lZ2l1IHR1Z2Vu\ndCBtaWNoeiBsZWlzdGVuIGhpZXouCgpodHRwOi8vd3d3LmNvbHVtYmlhLmVkdS9+ZmRjL3V0Zjgv\nCg==\n'  # noqa: E501 pylint: disable=line-too-long
+    payload = message.get_payload().replace("\n", "")
+    assert payload == 'RnJvbSB0aGUgVGFnZWxpZWQgb2YgV29sZnJhbSB2b24gRXNjaGVuYmFjaCAoTWlkZGxlIEhpZ2ggR2VybWFuKToKClPDrm5lIGtsw6J3ZW4gZHVyaCBkaWUgd29sa2VuIHNpbnQgZ2VzbGFnZW4sCmVyIHN0w65nZXQgw7tmIG1pdCBncsO0emVyIGtyYWZ0LAppY2ggc2loIGluIGdyw6J3ZW4gdMOkZ2Vsw65jaCBhbHMgZXIgd2lsIHRhZ2VuLApkZW4gdGFjLCBkZXIgaW0gZ2VzZWxsZXNjaGFmdAplcndlbmRlbiB3aWwsIGRlbSB3ZXJkZW4gbWFuLApkZW4gaWNoIG1pdCBzb3JnZW4gw65uIHZlcmxpZXouCmljaCBicmluZ2UgaW4gaGlubmVuLCBvYiBpY2gga2FuLgpzw65uIHZpbCBtYW5lZ2l1IHR1Z2VudCBtaWNoeiBsZWlzdGVuIGhpZXouCgpodHRwOi8vd3d3LmNvbHVtYmlhLmVkdS9+ZmRjL3V0Zjgv'  # noqa: E501 pylint: disable=line-too-long
 
 
 @mock.patch('smtplib.SMTP')
@@ -259,4 +276,5 @@ def test_utf8_database(mock_SMTP):
     # NOTE: to decode a base46-encoded string:
     # print((str(base64.b64decode(payload), "utf-8")))
     payload = message.get_payload()
-    assert payload == 'SGksIExhyJ1hbW9uLAoKWW91ciBudW1iZXIgaXMgMTcuCg==\n'
+    payload = message.get_payload().replace("\n", "")
+    assert payload == 'SGksIExhyJ1hbW9uLAoKWW91ciBudW1iZXIgaXMgMTcu'
diff --git a/tests/testdata/bad_template.txt b/tests/testdata/bad_template.txt
new file mode 100644
index 0000000..c22952e
--- /dev/null
+++ b/tests/testdata/bad_template.txt
@@ -0,0 +1,7 @@
+TO: {{error_not_in_database}}
+SUBJECT: Testing mailmerge
+FROM: My Self <myself@mydomain.com>
+
+Hi, {{name}},
+
+Your number is {{number}}.