Repository: certbot/certbot

Issue:
DvsniPerformTest errors
Interestingly, Travis is green, but locally I get the following errors:

```
======================================================================
FAIL: test_mod_config (letsencrypt.client.tests.apache.dvsni_test.DvsniPerformTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jakub/dev/letsencrypt/lets-encrypt-preview/letsencrypt/client/tests/apache/dvsni_test.py", line 156, in test_mod_config
    self.assertEqual(len(vhs), 2)
AssertionError: 0 != 2
-------------------- >> begin captured logging << --------------------
root: DEBUG: Creating backup of /tmp/tmpXgEs3Stemp/debian_apache_2_4/two_vhost_80/apache2/apache2.conf
--------------------- >> end captured logging << ---------------------

======================================================================
FAIL: test_perform0 (letsencrypt.client.tests.apache.dvsni_test.DvsniPerformTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jakub/dev/letsencrypt/lets-encrypt-preview/letsencrypt/client/tests/apache/dvsni_test.py", line 63, in test_perform0
    self.assertTrue(resp is None)
AssertionError: False is not true
```

It seems like #286 broke it.



Discussion:

Resolving patch:
diff --git a/letsencrypt/client/apache/dvsni.py b/letsencrypt/client/apache/dvsni.py
--- a/letsencrypt/client/apache/dvsni.py
+++ b/letsencrypt/client/apache/dvsni.py
@@ -26,6 +26,23 @@ class ApacheDvsni(object):
     :param str challenge_conf: location of the challenge config file
 
     """
+
+    VHOST_TEMPLATE = """\
+<VirtualHost {vhost}>
+    ServerName {server_name}
+    UseCanonicalName on
+    SSLStrictSNIVHostCheck on
+
+    LimitRequestBody 1048576
+
+    Include {ssl_options_conf_path}
+    SSLCertificateFile {cert_path}
+    SSLCertificateKeyFile {key_path}
+
+    DocumentRoot {document_root}
+</VirtualHost>
+
+"""
     def __init__(self, configurator):
         self.configurator = configurator
         self.achalls = []
@@ -160,19 +177,16 @@ def _get_config_text(self, achall, ip_addrs):
         ips = " ".join(str(i) for i in ip_addrs)
         document_root = os.path.join(
             self.configurator.config.config_dir, "dvsni_page/")
-        return ("<VirtualHost " + ips + ">{0}"
-                "ServerName " + achall.nonce_domain + "{0}"
-                "UseCanonicalName on{0}"
-                "SSLStrictSNIVHostCheck on{0}"
-                "{0}"
-                "LimitRequestBody 1048576{0}"
-                "{0}"
-                "Include " + self.configurator.parser.loc["ssl_options"] + "{0}"
-                "SSLCertificateFile " + self.get_cert_file(achall) + "{0}"
-                "SSLCertificateKeyFile " + achall.key.file + "{0}"
-                "{0}"
-                "DocumentRoot " + document_root + "{0}"
-                "</VirtualHost>{0}{0}".format(os.linesep))
+        # TODO: Python docs is not clear how mutliline string literal
+        # newlines are parsed on different platforms. At least on
+        # Linux (Debian sid), when source file uses CRLF, Python still
+        # parses it as '\n'... c.f.:
+        # https://docs.python.org/2.7/reference/lexical_analysis.html
+        return self.VHOST_TEMPLATE.format(
+            vhost=ips, server_name=achall.nonce_domain,
+            ssl_options_conf_path=self.configurator.parser.loc["ssl_options"],
+            cert_path=self.get_cert_file(achall), key_path=achall.key.file,
+            document_root=document_root).replace('\n', os.linesep)
 
     def get_cert_file(self, achall):
         """Returns standardized name for challenge certificate.


Test patch:
diff --git a/letsencrypt/client/tests/apache/dvsni_test.py b/letsencrypt/client/tests/apache/dvsni_test.py
--- a/letsencrypt/client/tests/apache/dvsni_test.py
+++ b/letsencrypt/client/tests/apache/dvsni_test.py
@@ -60,7 +60,7 @@ def tearDown(self):
 
     def test_perform0(self):
         resp = self.sni.perform()
-        self.assertTrue(resp is None)
+        self.assertTrue(len(resp) == 0)
 
     def test_setup_challenge_cert(self):
         # This is a helper function that can be used for handling