Repository: spritelink/nipap
Issue: Response on smart parser error incorrect

The response sent when the smart parser encounters an error does not follow the same format as for a successful query, which leads to parser error messages not being displayed:

```
$ ./nipap --show-interpretation a l 'foo "'
Searching for prefixes in any VRF...
Query interpretation:
No addresses matching 'foo "' found.
```

Here I'd expect the error message to be displayed.


Changed files:
nipap/nipap/backend.py, tests/nipaptest.py

diff --git a/nipap/nipap/backend.py b/nipap/nipap/backend.py
index 5e5793e6..6aa8b83c 100644
--- a/nipap/nipap/backend.py
+++ b/nipap/nipap/backend.py
@@ -1664,13 +1664,17 @@ class Nipap:
             query = self._parse_vrf_query(query_str)
         except NipapValueError as exc:
             return {
-                'interpretation': [
-                    {
+                'interpretation': {
+                    'operator': None,
+                    'val1': None,
+                    'val2': None,
+                    'interpretation': {
                         'string': query_str,
-                        'interpretation': exc,
-                        'attribute': 'text'
+                        'interpretation': 'unclosed quote',
+                        'attribute': 'text',
+                        'operator': None
                     }
-                ],
+                },
                 'search_options': search_options,
                 'result': []
             }
@@ -2282,16 +2286,20 @@ class Nipap:
             query = self._parse_pool_query(query_str)
         except NipapValueError:
             return {
-                    'interpretation': [
-                        {
-                            'string': query_str,
-                            'interpretation': 'unclosed quote',
-                            'attribute': 'text'
-                        }
-                    ],
-                    'search_options': search_options,
-                    'result': []
-                }
+                'interpretation': {
+                    'operator': None,
+                    'val1': None,
+                    'val2': None,
+                    'interpretation': {
+                        'string': query_str,
+                        'interpretation': 'unclosed quote',
+                        'attribute': 'text',
+                        'operator': None
+                    }
+                },
+                'search_options': search_options,
+                'result': []
+            }
 
         if extra_query is not None:
             query = {
@@ -3626,13 +3634,17 @@ class Nipap:
             query = self._parse_prefix_query(query_str)
         except NipapValueError:
             return {
-                'interpretation': [
-                    {
+                'interpretation': {
+                    'operator': None,
+                    'val1': None,
+                    'val2': None,
+                    'interpretation': {
                         'string': query_str,
                         'interpretation': 'unclosed quote',
-                        'attribute': 'text'
+                        'attribute': 'text',
+                        'operator': None
                     }
-                ],
+                },
                 'search_options': search_options,
                 'result': []
             }
@@ -4098,13 +4110,17 @@ class Nipap:
             query = self._parse_asn_query(query_str)
         except NipapValueError:
             return {
-                    'interpretation': [
-                        {
+                    'interpretation': {
+                        'operator': None,
+                        'val1': None,
+                        'val2': None,
+                        'interpretation': {
                             'string': query_str,
                             'interpretation': 'unclosed quote',
-                            'attribute': 'text'
+                            'attribute': 'text',
+                            'operator': None
                         }
-                    ],
+                    },
                     'search_options': search_options,
                     'result': []
                 }
diff --git a/tests/nipaptest.py b/tests/nipaptest.py
index e4ca8c15..ab5ba75a 100755
--- a/tests/nipaptest.py
+++ b/tests/nipaptest.py
@@ -1540,6 +1540,40 @@ class TestAddressListing(unittest.TestCase):
 
 
 
+    def test_invalid_search_string(self):
+        """ Test error handling of prefix smart search
+        """
+
+        expected = {
+            'interpretation': {
+                'operator': None,
+                'val1': None,
+                'val2': None,
+                'interpretation': {
+                    'interpretation': None,
+                    'string': None,
+                    'attribute': 'text',
+                    'operator': None
+                },
+            },
+            'result': [],
+            'search_options': {}
+        }
+
+        # unclosed single quote
+        result = Prefix.smart_search('foo \\'')
+        expected['interpretation']['interpretation']['interpretation'] = 'unclosed quote'
+        expected['interpretation']['interpretation']['string'] = 'foo \\''
+        self.assertEquals(expected, result)
+
+        # unclosed double quote
+        result = Prefix.smart_search('"')
+        expected['interpretation']['interpretation']['interpretation'] = 'unclosed quote'
+        expected['interpretation']['interpretation']['string'] = '"'
+        self.assertEquals(expected, result)
+
+
+
 class TestPrefixLastModified(unittest.TestCase):
     """ Test updates of the last modified value
     """