Repository: cdent/gabbi

Issue:
Support regex in response_strings
Many of the other test handlers support regex in the value, but `response_strings` does not.

This should probably fixed, but it has the potential to cause errors in existing tests so we may want to consider it as a version 3 to signal the change.

Discussion:

Patch:
diff --git a/docs/source/format.rst b/docs/source/format.rst
index 1217255..847c93c 100644
--- a/docs/source/format.rst
+++ b/docs/source/format.rst
@@ -181,6 +181,11 @@ Response Expectations
    ``response_strings``            A list of string fragments expected
                                    to be present in the response body.
 
+                                   If the value is wrapped in ``/.../``
+                                   the response body will be searched
+                                   for the value as a regular
+                                   expression.
+
    ``response_json_paths``         A dictionary of JSONPath rules paired
                                    with expected matches. Using this
                                    rule requires that the content being
diff --git a/gabbi/handlers/base.py b/gabbi/handlers/base.py
index 1bbafe0..980eebb 100644
--- a/gabbi/handlers/base.py
+++ b/gabbi/handlers/base.py
@@ -66,6 +66,15 @@ class ResponseHandler:
         """
         pass
 
+    def is_regex(self, value):
+        """Check if the value is formatted to looks like a regular expression.
+
+        Meaning it starts and ends with "/".
+        """
+        return (
+            value.startswith('/') and value.endswith('/') and len(value) > 1
+        )
+
     def _register(self):
         """Register this handler on the provided test class."""
         self.response_handler = None
diff --git a/gabbi/handlers/core.py b/gabbi/handlers/core.py
index 97a1d87..cc80b1d 100644
--- a/gabbi/handlers/core.py
+++ b/gabbi/handlers/core.py
@@ -22,8 +22,18 @@ class StringResponseHandler(base.ResponseHandler):
     test_key_value = []
 
     def action(self, test, expected, value=None):
-        expected = test.replace_template(expected)
-        test.assert_in_or_print_output(expected, test.output)
+        is_regex = self.is_regex(expected)
+        expected = test.replace_template(expected, escape_regex=is_regex)
+
+        if is_regex:
+            # Trim off /
+            expected = expected[1:-1]
+            test.assertRegex(
+                test.output, expected,
+                'Expect resonse body %s to match /%s/' %
+                (test.output, expected))
+        else:
+            test.assert_in_or_print_output(expected, test.output)
 
 
 class ForbiddenHeadersResponseHandler(base.ResponseHandler):
@@ -56,9 +66,7 @@ class HeadersResponseHandler(base.ResponseHandler):
         response = test.response
 
         header_value = str(value)
-        is_regex = (header_value.startswith('/') and
-                    header_value.endswith('/') and
-                    len(header_value) > 1)
+        is_regex = self.is_regex(header_value)
         header_value = test.replace_template(header_value,
                                              escape_regex=is_regex)
 
diff --git a/gabbi/handlers/jsonhandler.py b/gabbi/handlers/jsonhandler.py
index 34dc7ae..2ee49d5 100644
--- a/gabbi/handlers/jsonhandler.py
+++ b/gabbi/handlers/jsonhandler.py
@@ -118,10 +118,7 @@ class JSONHandler(base.ContentHandler):
                                          'match %s' % (rhs_path, value))
 
         # If expected is a string, check to see if it is a regex.
-        is_regex = (isinstance(value, str) and
-                    value.startswith('/') and
-                    value.endswith('/') and
-                    len(value) > 1)
+        is_regex = isinstance(value, str) and self.is_regex(value)
         expected = (rhs_match or
                     test.replace_template(value, escape_regex=is_regex))
         match = lhs_match


Test patch:
diff --git a/gabbi/tests/gabbits_intercept/regex.yaml b/gabbi/tests/gabbits_intercept/regex.yaml
index 9a0c055..1c01c63 100644
--- a/gabbi/tests/gabbits_intercept/regex.yaml
+++ b/gabbi/tests/gabbits_intercept/regex.yaml
@@ -1,4 +1,4 @@
-# Confirm regex handling in response and json path headers
+# Confirm regex handling in response headers, strings and json path handlers
 tests:
 - name: regex header test
   url: /cow?alpha=1
@@ -19,3 +19,30 @@ tests:
     $.alpha: /ow$/
     $.beta: /(?!cow).*/
     $.gamma: /\d+/
+
+- name: regex string test json
+  PUT: /cow
+  request_headers:
+    content-type: application/json
+  data:
+    alpha: cow
+    beta: pig
+    gamma: 1
+  response_strings:
+    - '/"alpha": "cow",/'
+
+- name: regex string test multiline
+  GET: /presenter
+  response_strings:
+    - '/Hello World/'
+    - '/dolor sit/'
+
+- name: regex string test splat
+  GET: /presenter
+  response_strings:
+    - '/dolor.*amet/'
+
+- name: regex string test mix
+  GET: /presenter
+  response_strings:
+    - '/[Hh]el{2}o [Ww]orld/'