소스 검색

[enh] unit tests for wolframalpha

a01200356 9 년 전
부모
커밋
4d8996eb4d
5개의 변경된 파일393개의 추가작업 그리고 307개의 파일을 삭제
  1. 27
    20
      searx/engines/wolframalpha_api.py
  2. 34
    27
      searx/engines/wolframalpha_noapi.py
  3. 2
    2
      searx/settings.yml
  4. 112
    255
      tests/unit/engines/test_wolframalpha_api.py
  5. 218
    3
      tests/unit/engines/test_wolframalpha_noapi.py

+ 27
- 20
searx/engines/wolframalpha_api.py 파일 보기

@@ -19,9 +19,10 @@ api_key = ''  # defined in settings.yml
19 19
 # xpath variables
20 20
 failure_xpath = '/queryresult[attribute::success="false"]'
21 21
 answer_xpath = '//pod[attribute::primary="true"]/subpod/plaintext'
22
-input_xpath = '//pod[starts-with(attribute::title, "Input")]/subpod/plaintext'
22
+input_xpath = '//pod[starts-with(attribute::id, "Input")]/subpod/plaintext'
23 23
 pods_xpath = '//pod'
24 24
 subpods_xpath = './subpod'
25
+pod_id_xpath = './@id'
25 26
 pod_title_xpath = './@title'
26 27
 plaintext_xpath = './plaintext'
27 28
 image_xpath = './img'
@@ -30,8 +31,8 @@ img_alt_xpath = './@alt'
30 31
 
31 32
 # pods to display as image in infobox
32 33
 # this pods do return a plaintext, but they look better and are more useful as images
33
-image_pods = {'Visual representation',
34
-              'Manipulatives illustration'}
34
+image_pods = {'VisualRepresentation',
35
+              'Illustration'}
35 36
 
36 37
 
37 38
 # do search-request
@@ -45,15 +46,15 @@ def request(query, params):
45 46
 
46 47
 # replace private user area characters to make text legible
47 48
 def replace_pua_chars(text):
48
-    pua_chars = {u'\uf522': u'\u2192',
49
-                 u'\uf7b1': u'\u2115',
50
-                 u'\uf7b4': u'\u211a',
51
-                 u'\uf7b5': u'\u211d',
52
-                 u'\uf7bd': u'\u2124',
53
-                 u'\uf74c': 'd',
54
-                 u'\uf74d': u'\u212f',
55
-                 u'\uf74e': 'i',
56
-                 u'\uf7d9': '='}
49
+    pua_chars = {u'\uf522': u'\u2192',  # rigth arrow
50
+                 u'\uf7b1': u'\u2115',  # set of natural numbers
51
+                 u'\uf7b4': u'\u211a',  # set of rational numbers
52
+                 u'\uf7b5': u'\u211d',  # set of real numbers
53
+                 u'\uf7bd': u'\u2124',  # set of integer numbers
54
+                 u'\uf74c': 'd',        # differential
55
+                 u'\uf74d': u'\u212f',  # euler's number
56
+                 u'\uf74e': 'i',        # imaginary number
57
+                 u'\uf7d9': '='}        # equals sign
57 58
 
58 59
     for k, v in pua_chars.iteritems():
59 60
         text = text.replace(k, v)
@@ -71,30 +72,35 @@ def response(resp):
71 72
     if search_results.xpath(failure_xpath):
72 73
         return []
73 74
 
74
-    infobox_title = search_results.xpath(input_xpath)
75
-    if infobox_title:
76
-        infobox_title = replace_pua_chars(infobox_title[0].text)
75
+    try:
76
+        infobox_title = search_results.xpath(input_xpath)[0].text
77
+    except:
78
+        infobox_title = None
77 79
 
78 80
     pods = search_results.xpath(pods_xpath)
79 81
     result_chunks = []
80 82
     for pod in pods:
81
-        pod_title = replace_pua_chars(pod.xpath(pod_title_xpath)[0])
83
+        pod_id = pod.xpath(pod_id_xpath)[0]
84
+        pod_title = pod.xpath(pod_title_xpath)[0]
82 85
 
83 86
         subpods = pod.xpath(subpods_xpath)
84 87
         if not subpods:
85 88
             continue
86 89
 
90
+        # Appends either a text or an image, depending on which one is more suitable
87 91
         for subpod in subpods:
88 92
             content = subpod.xpath(plaintext_xpath)[0].text
89 93
             image = subpod.xpath(image_xpath)
90
-            if content and pod_title not in image_pods:
91
-                content = replace_pua_chars(content)
92
-                result_chunks.append({'label': pod_title, 'value': content})
93 94
 
94
-                # if there's no input pod, infobox_title is content of first pod
95
+            if content and pod_id not in image_pods:
96
+
97
+                # if no input pod was found, title is first plaintext pod
95 98
                 if not infobox_title:
96 99
                     infobox_title = content
97 100
 
101
+                content = replace_pua_chars(content)
102
+                result_chunks.append({'label': pod_title, 'value': content})
103
+
98 104
             elif image:
99 105
                 result_chunks.append({'label': pod_title,
100 106
                                       'image': {'src': image[0].xpath(img_src_xpath)[0],
@@ -103,6 +109,7 @@ def response(resp):
103 109
     if not result_chunks:
104 110
         return []
105 111
 
112
+    # append infobox
106 113
     results.append({'infobox': infobox_title,
107 114
                     'attributes': result_chunks,
108 115
                     'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})

+ 34
- 27
searx/engines/wolframalpha_noapi.py 파일 보기

@@ -48,8 +48,8 @@ img_alt_xpath = './img/@alt'
48 48
 
49 49
 # pods to display as image in infobox
50 50
 # this pods do return a plaintext, but they look better and are more useful as images
51
-image_pods = {'Visual representation',
52
-              'Manipulatives illustration',
51
+image_pods = {'VisualRepresentation',
52
+              'Illustration',
53 53
               'Symbol'}
54 54
 
55 55
 
@@ -82,26 +82,35 @@ def request(query, params):
82 82
 # get additional pod
83 83
 # NOTE: this makes an additional requests to server, so the response will take longer and might reach timeout
84 84
 def get_async_pod(url):
85
-    pod = {'subpods': []}
86
-
87 85
     try:
88 86
         resp = http_get(url, timeout=2.0)
89
-
90
-        resp_pod = XML(resp.content)
91
-        if resp_pod.xpath(success_xpath):
92
-
93
-            for subpod in resp_pod:
94
-                plaintext = subpod.xpath(plaintext_xpath)[0].text
95
-                if plaintext:
96
-                    pod['subpods'].append({'title': subpod.xpath(title_xpath)[0],
97
-                                           'plaintext': plaintext})
98
-                elif subpod.xpath(image_xpath):
99
-                    pod['subpods'].append({'title': subpod.xpath(title_xpath)[0],
100
-                                           'plaintext': '',
101
-                                           'img': {'src': subpod.xpath(img_src_xpath)[0],
102
-                                                   'alt': subpod.xpath(img_alt_xpath)[0]}})
103 87
     except:
104
-        pass
88
+        return None
89
+
90
+    if resp:
91
+        return parse_async_pod(resp)
92
+
93
+
94
+def parse_async_pod(resp):
95
+    pod = {'subpods': []}
96
+
97
+    resp_pod = XML(resp.content)
98
+
99
+    if resp_pod.xpath(success_xpath):
100
+        for subpod in resp_pod:
101
+            new_subpod = {'title': subpod.xpath(title_xpath)[0]}
102
+
103
+            plaintext = subpod.xpath(plaintext_xpath)[0].text
104
+            if plaintext:
105
+                new_subpod['plaintext'] = plaintext
106
+            else:
107
+                new_subpod['plaintext'] = ''
108
+
109
+            if subpod.xpath(image_xpath):
110
+                new_subpod['img'] = {'src': subpod.xpath(img_src_xpath)[0],
111
+                                     'alt': subpod.xpath(img_alt_xpath)[0]}
112
+
113
+            pod['subpods'].append(new_subpod)
105 114
 
106 115
     return pod
107 116
 
@@ -119,6 +128,7 @@ def response(resp):
119 128
     result_chunks = []
120 129
     infobox_title = None
121 130
     for pod in resp_json['queryresult']['pods']:
131
+        pod_id = pod.get('id', '')
122 132
         pod_title = pod.get('title', '')
123 133
 
124 134
         if 'subpods' not in pod:
@@ -127,19 +137,16 @@ def response(resp):
127 137
                 result = get_async_pod(pod['async'])
128 138
                 if result:
129 139
                     pod = result
140
+                else:
141
+                    continue
130 142
             else:
131 143
                 continue
132 144
 
133
-        # infobox title is input or text content on first pod
134
-        if pod_title.startswith('Input') or not infobox_title:
135
-            try:
136
-                infobox_title = pod['subpods'][0]['plaintext']
137
-            except:
138
-                infobox_title = ''
139
-                pass
145
+        if pod_id == 'Input' or not infobox_title:
146
+            infobox_title = pod['subpods'][0]['plaintext']
140 147
 
141 148
         for subpod in pod['subpods']:
142
-            if subpod['plaintext'] != '' and pod_title not in image_pods:
149
+            if subpod['plaintext'] != '' and pod_id not in image_pods:
143 150
                 # append unless it's not an actual answer
144 151
                 if subpod['plaintext'] != '(requires interactivity)':
145 152
                     result_chunks.append({'label': pod_title, 'value': subpod['plaintext']})

+ 2
- 2
searx/settings.yml 파일 보기

@@ -311,9 +311,9 @@ engines:
311 311
     # You can use the engine using the official stable API, but you need an API key
312 312
     # See : http://products.wolframalpha.com/api/
313 313
     # engine : wolframalpha_api
314
-    # api_key: '5952JX-X52L3VKWT8' # required!
314
+    # api_key: '' # required!
315 315
     engine : wolframalpha_noapi
316
-    timeout: 10.0
316
+    timeout: 6.0
317 317
     categories : science
318 318
 
319 319
 #The blekko technology and team have joined IBM Watson! -> https://blekko.com/

+ 112
- 255
tests/unit/engines/test_wolframalpha_api.py 파일 보기

@@ -1,6 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from collections import defaultdict
3 3
 import mock
4
+from requests import Request
4 5
 from searx.engines import wolframalpha_api
5 6
 from searx.testing import SearxTestCase
6 7
 
@@ -9,17 +10,17 @@ class TestWolframAlphaAPIEngine(SearxTestCase):
9 10
 
10 11
     def test_request(self):
11 12
         query = 'test_query'
12
-        api_key = 'XXXXXX-XXXXXXXXXX'
13 13
         dicto = defaultdict(dict)
14
-        dicto['api_key'] = api_key
15 14
         params = wolframalpha_api.request(query, dicto)
16 15
 
16
+        # TODO: test api_key
17 17
         self.assertIn('url', params)
18
+        self.assertIn('https://api.wolframalpha.com/v2/query?', params['url'])
18 19
         self.assertIn(query, params['url'])
19
-        self.assertIn('wolframalpha.com', params['url'])
20
+        self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer'])
20 21
 
21
-        self.assertIn('api_key', params)
22
-        self.assertIn(api_key, params['api_key'])
22
+    def test_replace_pua_chars(self):
23
+        self.assertEqual('i', wolframalpha_api.replace_pua_chars(u'\uf74e'))
23 24
 
24 25
     def test_response(self):
25 26
         self.assertRaises(AttributeError, wolframalpha_api.response, None)
@@ -27,281 +28,137 @@ class TestWolframAlphaAPIEngine(SearxTestCase):
27 28
         self.assertRaises(AttributeError, wolframalpha_api.response, '')
28 29
         self.assertRaises(AttributeError, wolframalpha_api.response, '[]')
29 30
 
31
+        referer_url = 'referer_url'
32
+        request = Request(headers={'Referer': referer_url})
33
+
34
+        # test failure
30 35
         xml = '''<?xml version='1.0' encoding='UTF-8'?>
31 36
         <queryresult success='false' error='false' />
32 37
         '''
33
-        # test failure
34 38
         response = mock.Mock(content=xml)
35 39
         self.assertEqual(wolframalpha_api.response(response), [])
36 40
 
41
+        # test basic case
37 42
         xml = """<?xml version='1.0' encoding='UTF-8'?>
38 43
         <queryresult success='true'
39 44
             error='false'
40
-            numpods='6'
41
-            datatypes=''
42
-            timedout=''
43
-            timedoutpods=''
44
-            timing='0.684'
45
-            parsetiming='0.138'
46
-            parsetimedout='false'
47
-            recalculate=''
48
-            id='MSPa416020a7966dachc463600000f9c66cc21444cfg'
49
-            host='http://www3.wolframalpha.com'
50
-            server='6'
51
-            related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?...'
45
+            numpods='3'
46
+            datatypes='Math'
47
+            id='queryresult_id'
48
+            host='http://www4c.wolframalpha.com'
49
+            related='related_url'
52 50
             version='2.6'>
53
-         <pod title='Input'
54
-             scanner='Identity'
55
-             id='Input'
56
-             position='100'
57
-             error='false'
58
-             numsubpods='1'>
59
-          <subpod title=''>
60
-           <plaintext>sqrt(-1)</plaintext>
61
-          </subpod>
62
-         </pod>
63
-         <pod title='Result'
64
-             scanner='Simplification'
65
-             id='Result'
66
-             position='200'
67
-             error='false'
68
-             numsubpods='1'
69
-             primary='true'>
70
-          <subpod title=''>
71
-           <plaintext></plaintext>
72
-          </subpod>
73
-          <states count='1'>
74
-           <state name='Step-by-step solution'
75
-               input='Result__Step-by-step solution' />
76
-          </states>
77
-         </pod>
78
-         <pod title='Polar coordinates'
79
-             scanner='Numeric'
80
-             id='PolarCoordinates'
81
-             position='300'
82
-             error='false'
83
-             numsubpods='1'>
84
-          <subpod title=''>
85
-           <plaintext>r1 (radius), θ90° (angle)</plaintext>
86
-          </subpod>
87
-         </pod>
88
-         <pod title='Position in the complex plane'
89
-             scanner='Numeric'
90
-             id='PositionInTheComplexPlane'
91
-             position='400'
92
-             error='false'
93
-             numsubpods='1'>
94
-          <subpod title=''>
95
-           <plaintext></plaintext>
96
-          </subpod>
97
-         </pod>
98
-         <pod title='All 2nd roots of -1'
99
-             scanner='RootsOfUnity'
100
-             id=''
101
-             position='500'
102
-             error='false'
103
-             numsubpods='2'>
104
-          <subpod title=''>
105
-           <plaintext>  (principal root)</plaintext>
106
-          </subpod>
107
-          <subpod title=''>
108
-           <plaintext>-</plaintext>
109
-          </subpod>
110
-         </pod>
111
-         <pod title='Plot of all roots in the complex plane'
112
-             scanner='RootsOfUnity'
113
-             id='PlotOfAllRootsInTheComplexPlane'
114
-             position='600'
115
-             error='false'
116
-             numsubpods='1'>
117
-          <subpod title=''>
118
-           <plaintext></plaintext>
119
-          </subpod>
120
-         </pod>
121
-        </queryresult>
122
-        """
123
-        # test private user area char in response
124
-        response = mock.Mock(content=xml)
125
-        results = wolframalpha_api.response(response)
126
-        self.assertEqual(type(results), list)
127
-        self.assertEqual(len(results), 2)
128
-        self.assertIn('i', results[0]['answer'])
129
-        self.assertIn('sqrt(-1) - Wolfram|Alpha', results[1]['title'])
130
-        self.assertEquals('http://www.wolframalpha.com/input/?i=sqrt%28-1%29', results[1]['url'])
131
-
132
-        xml = """<?xml version='1.0' encoding='UTF-8'?>
133
-            <queryresult success='true'
134
-                error='false'
135
-                numpods='2'
136
-                datatypes=''
137
-                timedout=''
138
-                timedoutpods=''
139
-                timing='1.286'
140
-                parsetiming='0.255'
141
-                parsetimedout='false'
142
-                recalculate=''
143
-                id='MSPa195222ad740ede5214h30000480ca61h003d3gd6'
144
-                host='http://www3.wolframalpha.com'
145
-                server='20'
146
-                related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?id=...'
147
-                version='2.6'>
148
-             <pod title='Indefinite integral'
149
-                 scanner='Integral'
150
-                 id='IndefiniteIntegral'
151
-                 position='100'
152
-                 error='false'
51
+            <pod title='Input'
52
+                 scanner='Identity'
53
+                 id='Input'
54
+                 numsubpods='1'>
55
+                  <subpod title=''>
56
+                       <img src='input_img_src.gif'
57
+                           alt='input_img_alt'
58
+                           title='input_img_title' />
59
+                       <plaintext>input_plaintext</plaintext>
60
+                  </subpod>
61
+             </pod>
62
+             <pod title='Result'
63
+                 scanner='Simplification'
64
+                 id='Result'
153 65
                  numsubpods='1'
154 66
                  primary='true'>
155
-              <subpod title=''>
156
-               <plaintext>∫1/xxlog(x)+constant</plaintext>
157
-              </subpod>
158
-              <states count='1'>
159
-               <state name='Step-by-step solution'
160
-                   input='IndefiniteIntegral__Step-by-step solution' />
161
-              </states>
162
-              <infos count='1'>
163
-               <info text='log(x) is the natural logarithm'>
164
-                <link url='http://reference.wolfram.com/mathematica/ref/Log.html'
165
-                    text='Documentation'
166
-                    title='Mathematica' />
167
-                <link url='http://functions.wolfram.com/ElementaryFunctions/Log'
168
-                    text='Properties'
169
-                    title='Wolfram Functions Site' />
170
-                <link url='http://mathworld.wolfram.com/NaturalLogarithm.html'
171
-                    text='Definition'
172
-                    title='MathWorld' />
173
-               </info>
174
-              </infos>
67
+                  <subpod title=''>
68
+                       <img src='result_img_src.gif'
69
+                           alt='result_img_alt'
70
+                           title='result_img_title' />
71
+                       <plaintext>result_plaintext</plaintext>
72
+                  </subpod>
175 73
              </pod>
176
-             <pod title='Plots of the integral'
177
-                 scanner='Integral'
178
-                 id='Plot'
179
-                 position='200'
180
-                 error='false'
181
-                 numsubpods='2'>
182
-              <subpod title=''>
183
-               <plaintext></plaintext>
184
-               <states count='1'>
185
-                <statelist count='2'
186
-                    value='Complex-valued plot'
187
-                    delimiters=''>
188
-                 <state name='Complex-valued plot'
189
-                     input='Plot__1_Complex-valued plot' />
190
-                 <state name='Real-valued plot'
191
-                     input='Plot__1_Real-valued plot' />
192
-                </statelist>
193
-               </states>
194
-              </subpod>
195
-              <subpod title=''>
196
-               <plaintext></plaintext>
197
-               <states count='1'>
198
-                <statelist count='2'
199
-                    value='Complex-valued plot'
200
-                    delimiters=''>
201
-                 <state name='Complex-valued plot'
202
-                     input='Plot__2_Complex-valued plot' />
203
-                 <state name='Real-valued plot'
204
-                     input='Plot__2_Real-valued plot' />
205
-                </statelist>
206
-               </states>
207
-              </subpod>
74
+             <pod title='Manipulatives illustration'
75
+                 scanner='Arithmetic'
76
+                 id='Illustration'
77
+                 numsubpods='1'>
78
+                  <subpod title=''>
79
+                       <img src='illustration_img_src.gif'
80
+                           alt='illustration_img_alt' />
81
+                       <plaintext>illustration_plaintext</plaintext>
82
+                  </subpod>
208 83
              </pod>
209
-             <assumptions count='1'>
210
-              <assumption type='Clash'
211
-                  word='integral'
212
-                  template='Assuming &quot;${word}&quot; is ${desc1}. Use as ${desc2} instead'
213
-                  count='2'>
214
-               <value name='IntegralsWord'
215
-                   desc='an integral'
216
-                   input='*C.integral-_*IntegralsWord-' />
217
-               <value name='MathematicalFunctionIdentityPropertyClass'
218
-                   desc='a function property'
219
-                   input='*C.integral-_*MathematicalFunctionIdentityPropertyClass-' />
220
-              </assumption>
221
-             </assumptions>
222
-            </queryresult>
84
+        </queryresult>
223 85
         """
224
-        # test integral
225
-        response = mock.Mock(content=xml)
86
+        response = mock.Mock(content=xml, request=request)
226 87
         results = wolframalpha_api.response(response)
227 88
         self.assertEqual(type(results), list)
228 89
         self.assertEqual(len(results), 2)
229
-        self.assertIn('log(x)+c', results[0]['answer'])
230
-        self.assertIn('∫1/xx - Wolfram|Alpha'.decode('utf-8'), results[1]['title'])
231
-        self.assertEquals('http://www.wolframalpha.com/input/?i=%E2%88%AB1%2Fx%EF%9D%8Cx', results[1]['url'])
90
+        self.assertIn('input_plaintext', results[0]['infobox'])
91
+
92
+        self.assertEqual(len(results[0]['attributes']), 3)
93
+        self.assertIn('Input', results[0]['attributes'][0]['label'])
94
+        self.assertIn('input_plaintext', results[0]['attributes'][0]['value'])
95
+        self.assertIn('Result', results[0]['attributes'][1]['label'])
96
+        self.assertIn('result_plaintext', results[0]['attributes'][1]['value'])
97
+        self.assertIn('Manipulatives illustration', results[0]['attributes'][2]['label'])
98
+        self.assertIn('illustration_img_src.gif', results[0]['attributes'][2]['image']['src'])
99
+        self.assertIn('illustration_img_alt', results[0]['attributes'][2]['image']['alt'])
100
+
101
+        self.assertEqual(len(results[0]['urls']), 1)
232 102
 
103
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
104
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
105
+        self.assertEqual(referer_url, results[1]['url'])
106
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])
107
+
108
+        # test calc
233 109
         xml = """<?xml version='1.0' encoding='UTF-8'?>
234 110
         <queryresult success='true'
235 111
             error='false'
236
-            numpods='4'
237
-            datatypes='Solve'
238
-            timedout=''
239
-            timedoutpods=''
240
-            timing='0.79'
241
-            parsetiming='0.338'
112
+            numpods='2'
113
+            datatypes=''
242 114
             parsetimedout='false'
243
-            recalculate=''
244
-            id='MSPa7481f7i06d25h3deh2900004810i3a78d9b4fdc'
115
+            id='queryresult_id'
245 116
             host='http://www5b.wolframalpha.com'
246
-            server='23'
247
-            related='http://www5b.wolframalpha.com/api/v2/relatedQueries.jsp?id=...'
248
-            version='2.6'>
249
-         <pod title='Input interpretation'
250
-             scanner='Identity'
251
-             id='Input'
252
-             position='100'
253
-             error='false'
254
-             numsubpods='1'>
255
-          <subpod title=''>
256
-           <plaintext>solve x^2+x0</plaintext>
257
-          </subpod>
258
-         </pod>
259
-         <pod title='Results'
260
-             scanner='Solve'
261
-             id='Result'
262
-             position='200'
263
-             error='false'
264
-             numsubpods='2'
265
-             primary='true'>
266
-          <subpod title=''>
267
-           <plaintext>x-1</plaintext>
268
-          </subpod>
269
-          <subpod title=''>
270
-           <plaintext>x0</plaintext>
271
-          </subpod>
272
-          <states count='1'>
273
-           <state name='Step-by-step solution'
274
-               input='Result__Step-by-step solution' />
275
-          </states>
276
-         </pod>
277
-         <pod title='Root plot'
278
-             scanner='Solve'
279
-             id='RootPlot'
280
-             position='300'
281
-             error='false'
282
-             numsubpods='1'>
283
-          <subpod title=''>
284
-           <plaintext></plaintext>
285
-          </subpod>
286
-         </pod>
287
-         <pod title='Number line'
288
-             scanner='Solve'
289
-             id='NumberLine'
290
-             position='400'
291
-             error='false'
292
-             numsubpods='1'>
293
-          <subpod title=''>
294
-           <plaintext></plaintext>
295
-          </subpod>
296
-         </pod>
117
+            related='related_url'
118
+            version='2.6' >
119
+            <pod title='Indefinite integral'
120
+                scanner='Integral'
121
+                id='IndefiniteIntegral'
122
+                error='false'
123
+                numsubpods='1'
124
+                primary='true'>
125
+                <subpod title=''>
126
+                    <img src='integral_image.gif'
127
+                        alt='integral_img_alt'
128
+                        title='integral_img_title' />
129
+                    <plaintext>integral_plaintext</plaintext>
130
+                </subpod>
131
+            </pod>
132
+            <pod title='Plot'
133
+                scanner='Plotter'
134
+                id='Plot'
135
+                error='false'
136
+                numsubpods='1'>
137
+                <subpod title=''>
138
+                    <img src='plot.gif'
139
+                        alt='plot_alt'
140
+                        title='' />
141
+                    <plaintext></plaintext>
142
+                </subpod>
143
+            </pod>
297 144
         </queryresult>
298 145
         """
299
-        # test ecuation with multiple answers
300
-        response = mock.Mock(content=xml)
146
+        response = mock.Mock(content=xml, request=request)
301 147
         results = wolframalpha_api.response(response)
302 148
         self.assertEqual(type(results), list)
303
-        self.assertEqual(len(results), 3)
304
-        self.assertIn('x=-1', results[0]['answer'])
305
-        self.assertIn('x=0', results[1]['answer'])
306
-        self.assertIn('solve x^2+x0 - Wolfram|Alpha'.decode('utf-8'), results[2]['title'])
307
-        self.assertEquals('http://www.wolframalpha.com/input/?i=solve+x%5E2%2Bx%EF%9F%990', results[2]['url'])
149
+        self.assertEqual(len(results), 2)
150
+        self.assertIn('integral_plaintext', results[0]['infobox'])
151
+
152
+        self.assertEqual(len(results[0]['attributes']), 2)
153
+        self.assertIn('Indefinite integral', results[0]['attributes'][0]['label'])
154
+        self.assertIn('integral_plaintext', results[0]['attributes'][0]['value'])
155
+        self.assertIn('Plot', results[0]['attributes'][1]['label'])
156
+        self.assertIn('plot.gif', results[0]['attributes'][1]['image']['src'])
157
+        self.assertIn('plot_alt', results[0]['attributes'][1]['image']['alt'])
158
+
159
+        self.assertEqual(len(results[0]['urls']), 1)
160
+
161
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
162
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
163
+        self.assertEqual(referer_url, results[1]['url'])
164
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])

+ 218
- 3
tests/unit/engines/test_wolframalpha_noapi.py 파일 보기

@@ -1,5 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from collections import defaultdict
3
+import mock
4
+from requests import Request
3 5
 from searx.engines import wolframalpha_noapi
4 6
 from searx.testing import SearxTestCase
5 7
 
@@ -9,15 +11,228 @@ class TestWolframAlphaNoAPIEngine(SearxTestCase):
9 11
     def test_request(self):
10 12
         query = 'test_query'
11 13
         dicto = defaultdict(dict)
12
-        dicto['pageno'] = 1
13 14
         params = wolframalpha_noapi.request(query, dicto)
15
+
14 16
         self.assertIn('url', params)
17
+        self.assertIn('https://www.wolframalpha.com/input/json.jsp', params['url'])
15 18
         self.assertIn(query, params['url'])
16
-        self.assertIn('wolframalpha.com', params['url'])
19
+        self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer'])
17 20
 
18 21
     def test_response(self):
19 22
         self.assertRaises(AttributeError, wolframalpha_noapi.response, None)
20 23
         self.assertRaises(AttributeError, wolframalpha_noapi.response, [])
21 24
         self.assertRaises(AttributeError, wolframalpha_noapi.response, '')
22 25
         self.assertRaises(AttributeError, wolframalpha_noapi.response, '[]')
23
-        # TODO
26
+
27
+        referer_url = 'referer_url'
28
+        request = Request(headers={'Referer': referer_url})
29
+
30
+        # test failure
31
+        json = '''
32
+        {"queryresult" : {
33
+            "success" : false,
34
+            "error" : false,
35
+            "numpods" : 0,
36
+            "id" : "",
37
+            "host" : "https:\/\/www5a.wolframalpha.com",
38
+            "didyoumeans" : {}
39
+        }}
40
+        '''
41
+        response = mock.Mock(text=json, request=request)
42
+        self.assertEqual(wolframalpha_noapi.response(response), [])
43
+
44
+        # test basic case
45
+        json = '''
46
+        {"queryresult" : {
47
+            "success" : true,
48
+            "error" : false,
49
+            "numpods" : 6,
50
+            "datatypes" : "Math",
51
+            "id" : "queryresult_id",
52
+            "host" : "https:\/\/www5b.wolframalpha.com",
53
+            "related" : "related_url",
54
+            "version" : "2.6",
55
+            "pods" : [
56
+                {
57
+                    "title" : "Input",
58
+                    "scanners" : [
59
+                        "Identity"
60
+                    ],
61
+                    "id" : "Input",
62
+                    "error" : false,
63
+                    "numsubpods" : 1,
64
+                    "subpods" : [
65
+                        {
66
+                            "title" : "",
67
+                            "img" : {
68
+                                "src" : "input_img_src.gif",
69
+                                "alt" : "input_img_alt",
70
+                                "title" : "input_img_title"
71
+                            },
72
+                            "plaintext" : "input_plaintext",
73
+                            "minput" : "input_minput"
74
+                        }
75
+                    ]
76
+                },
77
+                {
78
+                    "title" : "Result",
79
+                    "scanners" : [
80
+                        "Simplification"
81
+                    ],
82
+                    "id" : "Result",
83
+                    "error" : false,
84
+                    "numsubpods" : 1,
85
+                    "primary" : true,
86
+                    "subpods" : [
87
+                        {
88
+                            "title" : "",
89
+                            "img" : {
90
+                                "src" : "result_img_src.gif",
91
+                                "alt" : "result_img_alt",
92
+                                "title" : "result_img_title"
93
+                            },
94
+                            "plaintext" : "result_plaintext",
95
+                            "moutput" : "result_moutput"
96
+                        }
97
+                    ]
98
+                },
99
+                {
100
+                    "title" : "Manipulatives illustration",
101
+                    "scanners" : [
102
+                        "Arithmetic"
103
+                    ],
104
+                    "id" : "Illustration",
105
+                    "error" : false,
106
+                    "numsubpods" : 1,
107
+                    "subpods" : [
108
+                        {
109
+                            "title" : "",
110
+                            "CDFcontent" : "Resizeable",
111
+                            "img" : {
112
+                                "src" : "illustration_img_src.gif",
113
+                                "alt" : "illustration_img_alt",
114
+                                "title" : "illustration_img_title"
115
+                            },
116
+                            "plaintext" : "illustration_img_plaintext"
117
+                        }
118
+                    ]
119
+                }
120
+            ]
121
+        }}
122
+        '''
123
+        response = mock.Mock(text=json, request=request)
124
+        results = wolframalpha_noapi.response(response)
125
+        self.assertEqual(type(results), list)
126
+        self.assertEqual(len(results), 2)
127
+        self.assertIn('input_plaintext', results[0]['infobox'])
128
+
129
+        self.assertEqual(len(results[0]['attributes']), 3)
130
+        self.assertIn('Input', results[0]['attributes'][0]['label'])
131
+        self.assertIn('input_plaintext', results[0]['attributes'][0]['value'])
132
+        self.assertIn('Result', results[0]['attributes'][1]['label'])
133
+        self.assertIn('result_plaintext', results[0]['attributes'][1]['value'])
134
+        self.assertIn('Manipulatives illustration', results[0]['attributes'][2]['label'])
135
+        self.assertIn('illustration_img_src.gif', results[0]['attributes'][2]['image']['src'])
136
+        self.assertIn('illustration_img_alt', results[0]['attributes'][2]['image']['alt'])
137
+
138
+        self.assertEqual(len(results[0]['urls']), 1)
139
+
140
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
141
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
142
+        self.assertEqual(referer_url, results[1]['url'])
143
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])
144
+
145
+        # test calc
146
+        json = """
147
+        {"queryresult" : {
148
+            "success" : true,
149
+            "error" : false,
150
+            "numpods" : 2,
151
+            "datatypes" : "",
152
+            "id" : "queryresult_id",
153
+            "host" : "https:\/\/www4b.wolframalpha.com",
154
+            "related" : "related_url",
155
+            "version" : "2.6",
156
+            "pods" : [
157
+                {
158
+                    "title" : "Indefinite integral",
159
+                    "scanners" : [
160
+                        "Integral"
161
+                    ],
162
+                    "id" : "IndefiniteIntegral",
163
+                    "error" : false,
164
+                    "numsubpods" : 1,
165
+                    "primary" : true,
166
+                    "subpods" : [
167
+                        {
168
+                            "title" : "",
169
+                            "img" : {
170
+                                "src" : "integral_img_src.gif",
171
+                                "alt" : "integral_img_alt",
172
+                                "title" : "integral_img_title"
173
+                            },
174
+                            "plaintext" : "integral_plaintext",
175
+                            "minput" : "integral_minput",
176
+                            "moutput" : "integral_moutput"
177
+                        }
178
+                    ]
179
+                },
180
+                {
181
+                    "title" : "Plot of the integral",
182
+                    "scanners" : [
183
+                        "Integral"
184
+                    ],
185
+                    "id" : "Plot",
186
+                    "error" : false,
187
+                    "numsubpods" : 0,
188
+                    "async" : "invalid_async_url"
189
+                }
190
+            ]
191
+        }}
192
+        """
193
+        response = mock.Mock(text=json, request=request)
194
+        results = wolframalpha_noapi.response(response)
195
+        self.assertEqual(type(results), list)
196
+        self.assertEqual(len(results), 2)
197
+        self.assertIn('integral_plaintext', results[0]['infobox'])
198
+
199
+        self.assertEqual(len(results[0]['attributes']), 1)
200
+        self.assertIn('Indefinite integral', results[0]['attributes'][0]['label'])
201
+        self.assertIn('integral_plaintext', results[0]['attributes'][0]['value'])
202
+
203
+        self.assertEqual(len(results[0]['urls']), 1)
204
+
205
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
206
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
207
+        self.assertEqual(referer_url, results[1]['url'])
208
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])
209
+
210
+    def test_parse_async_pod(self):
211
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, None)
212
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, [])
213
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, '')
214
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, '[]')
215
+
216
+        # test plot
217
+        xml = '''<?xml version='1.0' encoding='UTF-8'?>
218
+        <pod title='Plot'
219
+            scanner='Plot'
220
+            id='Plot'
221
+            error='false'
222
+            numsubpods='1'>
223
+            <subpod title=''>
224
+                <img src='plot_img_src.gif'
225
+                    alt='plot_img_alt'
226
+                    title='plot_img_title' />
227
+                <plaintext>plot_plaintext</plaintext>
228
+                <minput>plot_minput</minput>
229
+            </subpod>
230
+        </pod>
231
+        '''
232
+        response = mock.Mock(content=xml)
233
+        pod = wolframalpha_noapi.parse_async_pod(response)
234
+        self.assertEqual(len(pod['subpods']), 1)
235
+        self.assertEqual('', pod['subpods'][0]['title'])
236
+        self.assertEqual('plot_plaintext', pod['subpods'][0]['plaintext'])
237
+        self.assertEqual('plot_img_src.gif', pod['subpods'][0]['img']['src'])
238
+        self.assertEqual('plot_img_alt', pod['subpods'][0]['img']['alt'])