on master help sign in
[enh] unit tests for wolframalpha
commit4d8996eb4d54a0938ad9dc6ad105de2b850fa033 authora01200356 <a01200356@itesm.mx> dateSun 28 Feb 2016, 00:47 (11 years ago) parent4267b11
5 files changed+393−307
searx/engines/wolframalpha_api.py+27−20 searx/engines/wolframalpha_noapi.py+34−27 searx/settings.yml+2−2 tests/unit/engines/test_wolframalpha_api.py+112−255 tests/unit/engines/test_wolframalpha_noapi.py+218−3
searx/engines/wolframalpha_api.py+27−20
@@ -19,9 +19,10 @@ api_key = '' # defined in settings.yml
1919# xpath variables
2020failure_xpath = '/queryresult[attribute::success="false"]'
2121answer_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'
2323pods_xpath = '//pod'
2424subpods_xpath = './subpod'
25+pod_id_xpath = './@id'
2526pod_title_xpath = './@title'
2627plaintext_xpath = './plaintext'
2728image_xpath = './img'
@@ -30,8 +31,8 @@ img_alt_xpath = './@alt'
3031
3132# pods to display as image in infobox
3233# 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'}
3536
3637
3738# do search-request
@@ -45,15 +46,15 @@ def request(query, params):
4546
4647# replace private user area characters to make text legible
4748def 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
5758
5859 for k, v in pua_chars.iteritems():
5960 text = text.replace(k, v)
@@ -71,30 +72,35 @@ def response(resp):
7172 if search_results.xpath(failure_xpath):
7273 return []
7374
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
7779
7880 pods = search_results.xpath(pods_xpath)
7981 result_chunks = []
8082 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]
8285
8386 subpods = pod.xpath(subpods_xpath)
8487 if not subpods:
8588 continue
8689
90+ # Appends either a text or an image, depending on which one is more suitable
8791 for subpod in subpods:
8892 content = subpod.xpath(plaintext_xpath)[0].text
8993 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})
9394
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
9598 if not infobox_title:
9699 infobox_title = content
97100
101+ content = replace_pua_chars(content)
102+ result_chunks.append({'label': pod_title, 'value': content})
103+
98104 elif image:
99105 result_chunks.append({'label': pod_title,
100106 'image': {'src': image[0].xpath(img_src_xpath)[0],
@@ -103,6 +109,7 @@ def response(resp):
103109 if not result_chunks:
104110 return []
105111
112+ # append infobox
106113 results.append({'infobox': infobox_title,
107114 'attributes': result_chunks,
108115 'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})
searx/engines/wolframalpha_noapi.py+34−27
@@ -48,8 +48,8 @@ img_alt_xpath = './img/@alt'
4848
4949# pods to display as image in infobox
5050# 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',
5353 'Symbol'}
5454
5555
@@ -82,26 +82,35 @@ def request(query, params):
8282# get additional pod
8383# NOTE: this makes an additional requests to server, so the response will take longer and might reach timeout
8484def get_async_pod(url):
85− pod = {'subpods': []}
86−
8785 try:
8886 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]}})
10387 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)
105114
106115 return pod
107116
@@ -119,6 +128,7 @@ def response(resp):
119128 result_chunks = []
120129 infobox_title = None
121130 for pod in resp_json['queryresult']['pods']:
131+ pod_id = pod.get('id', '')
122132 pod_title = pod.get('title', '')
123133
124134 if 'subpods' not in pod:
@@ -127,19 +137,16 @@ def response(resp):
127137 result = get_async_pod(pod['async'])
128138 if result:
129139 pod = result
140+ else:
141+ continue
130142 else:
131143 continue
132144
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']
140147
141148 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:
143150 # append unless it's not an actual answer
144151 if subpod['plaintext'] != '(requires interactivity)':
145152 result_chunks.append({'label': pod_title, 'value': subpod['plaintext']})
searx/settings.yml+2−2
@@ -311,9 +311,9 @@ engines:
311311 # You can use the engine using the official stable API, but you need an API key
312312 # See : http://products.wolframalpha.com/api/
313313 # engine : wolframalpha_api
314− # api_key: '5952JX-X52L3VKWT8' # required!
314+ # api_key: '' # required!
315315 engine : wolframalpha_noapi
316− timeout: 10.0
316+ timeout: 6.0
317317 categories : science
318318
319319#The blekko technology and team have joined IBM Watson! -> https://blekko.com/
tests/unit/engines/test_wolframalpha_api.py+112−255
@@ -1,6 +1,7 @@
11# -*- coding: utf-8 -*-
22from collections import defaultdict
33import mock
4+from requests import Request
45from searx.engines import wolframalpha_api
56from searx.testing import SearxTestCase
67
@@ -9,17 +10,17 @@ class TestWolframAlphaAPIEngine(SearxTestCase):
910
1011 def test_request(self):
1112 query = 'test_query'
12− api_key = 'XXXXXX-XXXXXXXXXX'
1313 dicto = defaultdict(dict)
14− dicto['api_key'] = api_key
1514 params = wolframalpha_api.request(query, dicto)
1615
16+ # TODO: test api_key
1717 self.assertIn('url', params)
18+ self.assertIn('https://api.wolframalpha.com/v2/query?', params['url'])
1819 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'])
2021
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'))
2324
2425 def test_response(self):
2526 self.assertRaises(AttributeError, wolframalpha_api.response, None)
@@ -27,281 +28,137 @@ class TestWolframAlphaAPIEngine(SearxTestCase):
2728 self.assertRaises(AttributeError, wolframalpha_api.response, '')
2829 self.assertRaises(AttributeError, wolframalpha_api.response, '[]')
2930
31+ referer_url = 'referer_url'
32+ request = Request(headers={'Referer': referer_url})
33+
34+ # test failure
3035 xml = '''<?xml version='1.0' encoding='UTF-8'?>
3136 <queryresult success='false' error='false' />
3237 '''
33− # test failure
3438 response = mock.Mock(content=xml)
3539 self.assertEqual(wolframalpha_api.response(response), [])
3640
41+ # test basic case
3742 xml = """<?xml version='1.0' encoding='UTF-8'?>
3843 <queryresult success='true'
3944 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'
5250 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'
15365 numsubpods='1'
15466 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>
17573 </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>
20883 </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>
22385 """
224− # test integral
225− response = mock.Mock(content=xml)
86+ response = mock.Mock(content=xml, request=request)
22687 results = wolframalpha_api.response(response)
22788 self.assertEqual(type(results), list)
22889 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)
232102
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
233109 xml = """<?xml version='1.0' encoding='UTF-8'?>
234110 <queryresult success='true'
235111 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=''
242114 parsetimedout='false'
243− recalculate=''
244− id='MSPa7481f7i06d25h3deh2900004810i3a78d9b4fdc'
115+ id='queryresult_id'
245116 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>
297144 </queryresult>
298145 """
299− # test ecuation with multiple answers
300− response = mock.Mock(content=xml)
146+ response = mock.Mock(content=xml, request=request)
301147 results = wolframalpha_api.response(response)
302148 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'])
tests/unit/engines/test_wolframalpha_noapi.py+218−3
@@ -1,5 +1,7 @@
11# -*- coding: utf-8 -*-
22from collections import defaultdict
3+import mock
4+from requests import Request
35from searx.engines import wolframalpha_noapi
46from searx.testing import SearxTestCase
57
@@ -9,15 +11,228 @@ class TestWolframAlphaNoAPIEngine(SearxTestCase):
911 def test_request(self):
1012 query = 'test_query'
1113 dicto = defaultdict(dict)
12− dicto['pageno'] = 1
1314 params = wolframalpha_noapi.request(query, dicto)
15+
1416 self.assertIn('url', params)
17+ self.assertIn('https://www.wolframalpha.com/input/json.jsp', params['url'])
1518 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'])
1720
1821 def test_response(self):
1922 self.assertRaises(AttributeError, wolframalpha_noapi.response, None)
2023 self.assertRaises(AttributeError, wolframalpha_noapi.response, [])
2124 self.assertRaises(AttributeError, wolframalpha_noapi.response, '')
2225 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'])