From 4d8996eb4d54a0938ad9dc6ad105de2b850fa033 Mon Sep 17 00:00:00 2001 From: a01200356 Date: Sun, 28 Feb 2016 00:47:36 -0600 Subject: [PATCH] [enh] unit tests for wolframalpha --- searx/engines/wolframalpha_api.py | 47 ++- searx/engines/wolframalpha_noapi.py | 61 +-- searx/settings.yml | 4 +- tests/unit/engines/test_wolframalpha_api.py | 367 ++++++------------ tests/unit/engines/test_wolframalpha_noapi.py | 221 ++++++++++- 5 files changed, 393 insertions(+), 307 deletions(-) diff --git a/searx/engines/wolframalpha_api.py b/searx/engines/wolframalpha_api.py index ad80fb0f..9a13d742 100644 --- a/searx/engines/wolframalpha_api.py +++ b/searx/engines/wolframalpha_api.py @@ -19,9 +19,10 @@ api_key = '' # defined in settings.yml # xpath variables failure_xpath = '/queryresult[attribute::success="false"]' answer_xpath = '//pod[attribute::primary="true"]/subpod/plaintext' -input_xpath = '//pod[starts-with(attribute::title, "Input")]/subpod/plaintext' +input_xpath = '//pod[starts-with(attribute::id, "Input")]/subpod/plaintext' pods_xpath = '//pod' subpods_xpath = './subpod' +pod_id_xpath = './@id' pod_title_xpath = './@title' plaintext_xpath = './plaintext' image_xpath = './img' @@ -30,8 +31,8 @@ img_alt_xpath = './@alt' # pods to display as image in infobox # this pods do return a plaintext, but they look better and are more useful as images -image_pods = {'Visual representation', - 'Manipulatives illustration'} +image_pods = {'VisualRepresentation', + 'Illustration'} # do search-request @@ -45,15 +46,15 @@ def request(query, params): # replace private user area characters to make text legible def replace_pua_chars(text): - pua_chars = {u'\uf522': u'\u2192', - u'\uf7b1': u'\u2115', - u'\uf7b4': u'\u211a', - u'\uf7b5': u'\u211d', - u'\uf7bd': u'\u2124', - u'\uf74c': 'd', - u'\uf74d': u'\u212f', - u'\uf74e': 'i', - u'\uf7d9': '='} + pua_chars = {u'\uf522': u'\u2192', # rigth arrow + u'\uf7b1': u'\u2115', # set of natural numbers + u'\uf7b4': u'\u211a', # set of rational numbers + u'\uf7b5': u'\u211d', # set of real numbers + u'\uf7bd': u'\u2124', # set of integer numbers + u'\uf74c': 'd', # differential + u'\uf74d': u'\u212f', # euler's number + u'\uf74e': 'i', # imaginary number + u'\uf7d9': '='} # equals sign for k, v in pua_chars.iteritems(): text = text.replace(k, v) @@ -71,30 +72,35 @@ def response(resp): if search_results.xpath(failure_xpath): return [] - infobox_title = search_results.xpath(input_xpath) - if infobox_title: - infobox_title = replace_pua_chars(infobox_title[0].text) + try: + infobox_title = search_results.xpath(input_xpath)[0].text + except: + infobox_title = None pods = search_results.xpath(pods_xpath) result_chunks = [] for pod in pods: - pod_title = replace_pua_chars(pod.xpath(pod_title_xpath)[0]) + pod_id = pod.xpath(pod_id_xpath)[0] + pod_title = pod.xpath(pod_title_xpath)[0] subpods = pod.xpath(subpods_xpath) if not subpods: continue + # Appends either a text or an image, depending on which one is more suitable for subpod in subpods: content = subpod.xpath(plaintext_xpath)[0].text image = subpod.xpath(image_xpath) - if content and pod_title not in image_pods: - content = replace_pua_chars(content) - result_chunks.append({'label': pod_title, 'value': content}) - # if there's no input pod, infobox_title is content of first pod + if content and pod_id not in image_pods: + + # if no input pod was found, title is first plaintext pod if not infobox_title: infobox_title = content + content = replace_pua_chars(content) + result_chunks.append({'label': pod_title, 'value': content}) + elif image: result_chunks.append({'label': pod_title, 'image': {'src': image[0].xpath(img_src_xpath)[0], @@ -103,6 +109,7 @@ def response(resp): if not result_chunks: return [] + # append infobox results.append({'infobox': infobox_title, 'attributes': result_chunks, 'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]}) diff --git a/searx/engines/wolframalpha_noapi.py b/searx/engines/wolframalpha_noapi.py index a5fbdeae..d10716a2 100644 --- a/searx/engines/wolframalpha_noapi.py +++ b/searx/engines/wolframalpha_noapi.py @@ -48,8 +48,8 @@ img_alt_xpath = './img/@alt' # pods to display as image in infobox # this pods do return a plaintext, but they look better and are more useful as images -image_pods = {'Visual representation', - 'Manipulatives illustration', +image_pods = {'VisualRepresentation', + 'Illustration', 'Symbol'} @@ -82,26 +82,35 @@ def request(query, params): # get additional pod # NOTE: this makes an additional requests to server, so the response will take longer and might reach timeout def get_async_pod(url): - pod = {'subpods': []} - try: resp = http_get(url, timeout=2.0) - - resp_pod = XML(resp.content) - if resp_pod.xpath(success_xpath): - - for subpod in resp_pod: - plaintext = subpod.xpath(plaintext_xpath)[0].text - if plaintext: - pod['subpods'].append({'title': subpod.xpath(title_xpath)[0], - 'plaintext': plaintext}) - elif subpod.xpath(image_xpath): - pod['subpods'].append({'title': subpod.xpath(title_xpath)[0], - 'plaintext': '', - 'img': {'src': subpod.xpath(img_src_xpath)[0], - 'alt': subpod.xpath(img_alt_xpath)[0]}}) except: - pass + return None + + if resp: + return parse_async_pod(resp) + + +def parse_async_pod(resp): + pod = {'subpods': []} + + resp_pod = XML(resp.content) + + if resp_pod.xpath(success_xpath): + for subpod in resp_pod: + new_subpod = {'title': subpod.xpath(title_xpath)[0]} + + plaintext = subpod.xpath(plaintext_xpath)[0].text + if plaintext: + new_subpod['plaintext'] = plaintext + else: + new_subpod['plaintext'] = '' + + if subpod.xpath(image_xpath): + new_subpod['img'] = {'src': subpod.xpath(img_src_xpath)[0], + 'alt': subpod.xpath(img_alt_xpath)[0]} + + pod['subpods'].append(new_subpod) return pod @@ -119,6 +128,7 @@ def response(resp): result_chunks = [] infobox_title = None for pod in resp_json['queryresult']['pods']: + pod_id = pod.get('id', '') pod_title = pod.get('title', '') if 'subpods' not in pod: @@ -127,19 +137,16 @@ def response(resp): result = get_async_pod(pod['async']) if result: pod = result + else: + continue else: continue - # infobox title is input or text content on first pod - if pod_title.startswith('Input') or not infobox_title: - try: - infobox_title = pod['subpods'][0]['plaintext'] - except: - infobox_title = '' - pass + if pod_id == 'Input' or not infobox_title: + infobox_title = pod['subpods'][0]['plaintext'] for subpod in pod['subpods']: - if subpod['plaintext'] != '' and pod_title not in image_pods: + if subpod['plaintext'] != '' and pod_id not in image_pods: # append unless it's not an actual answer if subpod['plaintext'] != '(requires interactivity)': result_chunks.append({'label': pod_title, 'value': subpod['plaintext']}) diff --git a/searx/settings.yml b/searx/settings.yml index 6e4316b0..7a5937ed 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -311,9 +311,9 @@ engines: # You can use the engine using the official stable API, but you need an API key # See : http://products.wolframalpha.com/api/ # engine : wolframalpha_api - # api_key: '5952JX-X52L3VKWT8' # required! + # api_key: '' # required! engine : wolframalpha_noapi - timeout: 10.0 + timeout: 6.0 categories : science #The blekko technology and team have joined IBM Watson! -> https://blekko.com/ diff --git a/tests/unit/engines/test_wolframalpha_api.py b/tests/unit/engines/test_wolframalpha_api.py index c8077579..9dbe4d86 100644 --- a/tests/unit/engines/test_wolframalpha_api.py +++ b/tests/unit/engines/test_wolframalpha_api.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from collections import defaultdict import mock +from requests import Request from searx.engines import wolframalpha_api from searx.testing import SearxTestCase @@ -9,17 +10,17 @@ class TestWolframAlphaAPIEngine(SearxTestCase): def test_request(self): query = 'test_query' - api_key = 'XXXXXX-XXXXXXXXXX' dicto = defaultdict(dict) - dicto['api_key'] = api_key params = wolframalpha_api.request(query, dicto) + # TODO: test api_key self.assertIn('url', params) + self.assertIn('https://api.wolframalpha.com/v2/query?', params['url']) self.assertIn(query, params['url']) - self.assertIn('wolframalpha.com', params['url']) + self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer']) - self.assertIn('api_key', params) - self.assertIn(api_key, params['api_key']) + def test_replace_pua_chars(self): + self.assertEqual('i', wolframalpha_api.replace_pua_chars(u'\uf74e')) def test_response(self): self.assertRaises(AttributeError, wolframalpha_api.response, None) @@ -27,281 +28,137 @@ class TestWolframAlphaAPIEngine(SearxTestCase): self.assertRaises(AttributeError, wolframalpha_api.response, '') self.assertRaises(AttributeError, wolframalpha_api.response, '[]') + referer_url = 'referer_url' + request = Request(headers={'Referer': referer_url}) + + # test failure xml = ''' ''' - # test failure response = mock.Mock(content=xml) self.assertEqual(wolframalpha_api.response(response), []) + # test basic case xml = """ - - - sqrt(-1)</plaintext> - </subpod> - </pod> - <pod title='Result' - scanner='Simplification' - id='Result' - position='200' - error='false' - numsubpods='1' - primary='true'> - <subpod title=''> - <plaintext></plaintext> - </subpod> - <states count='1'> - <state name='Step-by-step solution' - input='Result__Step-by-step solution' /> - </states> - </pod> - <pod title='Polar coordinates' - scanner='Numeric' - id='PolarCoordinates' - position='300' - error='false' - numsubpods='1'> - <subpod title=''> - <plaintext>r1 (radius), θ90° (angle)</plaintext> - </subpod> - </pod> - <pod title='Position in the complex plane' - scanner='Numeric' - id='PositionInTheComplexPlane' - position='400' - error='false' - numsubpods='1'> - <subpod title=''> - <plaintext></plaintext> - </subpod> - </pod> - <pod title='All 2nd roots of -1' - scanner='RootsOfUnity' - id='' - position='500' - error='false' - numsubpods='2'> - <subpod title=''> - <plaintext> (principal root)</plaintext> - </subpod> - <subpod title=''> - <plaintext>-</plaintext> - </subpod> - </pod> - <pod title='Plot of all roots in the complex plane' - scanner='RootsOfUnity' - id='PlotOfAllRootsInTheComplexPlane' - position='600' - error='false' - numsubpods='1'> - <subpod title=''> - <plaintext></plaintext> - </subpod> - </pod> - </queryresult> - """ - # test private user area char in response - response = mock.Mock(content=xml) - results = wolframalpha_api.response(response) - self.assertEqual(type(results), list) - self.assertEqual(len(results), 2) - self.assertIn('i', results[0]['answer']) - self.assertIn('sqrt(-1) - Wolfram|Alpha', results[1]['title']) - self.assertEquals('http://www.wolframalpha.com/input/?i=sqrt%28-1%29', results[1]['url']) - - xml = """<?xml version='1.0' encoding='UTF-8'?> - <queryresult success='true' - error='false' - numpods='2' - datatypes='' - timedout='' - timedoutpods='' - timing='1.286' - parsetiming='0.255' - parsetimedout='false' - recalculate='' - id='MSPa195222ad740ede5214h30000480ca61h003d3gd6' - host='http://www3.wolframalpha.com' - server='20' - related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?id=...' - version='2.6'> - <pod title='Indefinite integral' - scanner='Integral' - id='IndefiniteIntegral' - position='100' - error='false' + <pod title='Input' + scanner='Identity' + id='Input' + numsubpods='1'> + <subpod title=''> + <img src='input_img_src.gif' + alt='input_img_alt' + title='input_img_title' /> + <plaintext>input_plaintext</plaintext> + </subpod> + </pod> + <pod title='Result' + scanner='Simplification' + id='Result' numsubpods='1' primary='true'> - <subpod title=''> - <plaintext>∫1/xxlog(x)+constant</plaintext> - </subpod> - <states count='1'> - <state name='Step-by-step solution' - input='IndefiniteIntegral__Step-by-step solution' /> - </states> - <infos count='1'> - <info text='log(x) is the natural logarithm'> - <link url='http://reference.wolfram.com/mathematica/ref/Log.html' - text='Documentation' - title='Mathematica' /> - <link url='http://functions.wolfram.com/ElementaryFunctions/Log' - text='Properties' - title='Wolfram Functions Site' /> - <link url='http://mathworld.wolfram.com/NaturalLogarithm.html' - text='Definition' - title='MathWorld' /> - </info> - </infos> + <subpod title=''> + <img src='result_img_src.gif' + alt='result_img_alt' + title='result_img_title' /> + <plaintext>result_plaintext</plaintext> + </subpod> </pod> - <pod title='Plots of the integral' - scanner='Integral' - id='Plot' - position='200' - error='false' - numsubpods='2'> - <subpod title=''> - <plaintext></plaintext> - <states count='1'> - <statelist count='2' - value='Complex-valued plot' - delimiters=''> - <state name='Complex-valued plot' - input='Plot__1_Complex-valued plot' /> - <state name='Real-valued plot' - input='Plot__1_Real-valued plot' /> - </statelist> - </states> - </subpod> - <subpod title=''> - <plaintext></plaintext> - <states count='1'> - <statelist count='2' - value='Complex-valued plot' - delimiters=''> - <state name='Complex-valued plot' - input='Plot__2_Complex-valued plot' /> - <state name='Real-valued plot' - input='Plot__2_Real-valued plot' /> - </statelist> - </states> - </subpod> + <pod title='Manipulatives illustration' + scanner='Arithmetic' + id='Illustration' + numsubpods='1'> + <subpod title=''> + <img src='illustration_img_src.gif' + alt='illustration_img_alt' /> + <plaintext>illustration_plaintext</plaintext> + </subpod> </pod> - <assumptions count='1'> - <assumption type='Clash' - word='integral' - template='Assuming &quot;${word}&quot; is ${desc1}. Use as ${desc2} instead' - count='2'> - <value name='IntegralsWord' - desc='an integral' - input='*C.integral-_*IntegralsWord-' /> - <value name='MathematicalFunctionIdentityPropertyClass' - desc='a function property' - input='*C.integral-_*MathematicalFunctionIdentityPropertyClass-' /> - </assumption> - </assumptions> - </queryresult> + </queryresult> """ - # test integral - response = mock.Mock(content=xml) + response = mock.Mock(content=xml, request=request) results = wolframalpha_api.response(response) self.assertEqual(type(results), list) self.assertEqual(len(results), 2) - self.assertIn('log(x)+c', results[0]['answer']) - self.assertIn('∫1/xx - Wolfram|Alpha'.decode('utf-8'), results[1]['title']) - self.assertEquals('http://www.wolframalpha.com/input/?i=%E2%88%AB1%2Fx%EF%9D%8Cx', results[1]['url']) + self.assertIn('input_plaintext', results[0]['infobox']) + self.assertEqual(len(results[0]['attributes']), 3) + self.assertIn('Input', results[0]['attributes'][0]['label']) + self.assertIn('input_plaintext', results[0]['attributes'][0]['value']) + self.assertIn('Result', results[0]['attributes'][1]['label']) + self.assertIn('result_plaintext', results[0]['attributes'][1]['value']) + self.assertIn('Manipulatives illustration', results[0]['attributes'][2]['label']) + self.assertIn('illustration_img_src.gif', results[0]['attributes'][2]['image']['src']) + self.assertIn('illustration_img_alt', results[0]['attributes'][2]['image']['alt']) + + self.assertEqual(len(results[0]['urls']), 1) + + self.assertEqual(referer_url, results[0]['urls'][0]['url']) + self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) + self.assertEqual(referer_url, results[1]['url']) + self.assertEqual('Wolfram|Alpha', results[1]['title']) + + # test calc xml = """<?xml version='1.0' encoding='UTF-8'?> <queryresult success='true' error='false' - numpods='4' - datatypes='Solve' - timedout='' - timedoutpods='' - timing='0.79' - parsetiming='0.338' + numpods='2' + datatypes='' parsetimedout='false' - recalculate='' - id='MSPa7481f7i06d25h3deh2900004810i3a78d9b4fdc' + id='queryresult_id' host='http://www5b.wolframalpha.com' - server='23' - related='http://www5b.wolframalpha.com/api/v2/relatedQueries.jsp?id=...' - version='2.6'> - <pod title='Input interpretation' - scanner='Identity' - id='Input' - position='100' - error='false' - numsubpods='1'> - <subpod title=''> - <plaintext>solve x^2+x0</plaintext> - </subpod> - </pod> - <pod title='Results' - scanner='Solve' - id='Result' - position='200' - error='false' - numsubpods='2' - primary='true'> - <subpod title=''> - <plaintext>x-1</plaintext> - </subpod> - <subpod title=''> - <plaintext>x0</plaintext> - </subpod> - <states count='1'> - <state name='Step-by-step solution' - input='Result__Step-by-step solution' /> - </states> - </pod> - <pod title='Root plot' - scanner='Solve' - id='RootPlot' - position='300' - error='false' - numsubpods='1'> - <subpod title=''> - <plaintext></plaintext> - </subpod> - </pod> - <pod title='Number line' - scanner='Solve' - id='NumberLine' - position='400' - error='false' - numsubpods='1'> - <subpod title=''> - <plaintext></plaintext> - </subpod> - </pod> + related='related_url' + version='2.6' > + <pod title='Indefinite integral' + scanner='Integral' + id='IndefiniteIntegral' + error='false' + numsubpods='1' + primary='true'> + <subpod title=''> + <img src='integral_image.gif' + alt='integral_img_alt' + title='integral_img_title' /> + <plaintext>integral_plaintext</plaintext> + </subpod> + </pod> + <pod title='Plot' + scanner='Plotter' + id='Plot' + error='false' + numsubpods='1'> + <subpod title=''> + <img src='plot.gif' + alt='plot_alt' + title='' /> + <plaintext></plaintext> + </subpod> + </pod> </queryresult> """ - # test ecuation with multiple answers - response = mock.Mock(content=xml) + response = mock.Mock(content=xml, request=request) results = wolframalpha_api.response(response) self.assertEqual(type(results), list) - self.assertEqual(len(results), 3) - self.assertIn('x=-1', results[0]['answer']) - self.assertIn('x=0', results[1]['answer']) - self.assertIn('solve x^2+x0 - Wolfram|Alpha'.decode('utf-8'), results[2]['title']) - self.assertEquals('http://www.wolframalpha.com/input/?i=solve+x%5E2%2Bx%EF%9F%990', results[2]['url']) + self.assertEqual(len(results), 2) + self.assertIn('integral_plaintext', results[0]['infobox']) + + self.assertEqual(len(results[0]['attributes']), 2) + self.assertIn('Indefinite integral', results[0]['attributes'][0]['label']) + self.assertIn('integral_plaintext', results[0]['attributes'][0]['value']) + self.assertIn('Plot', results[0]['attributes'][1]['label']) + self.assertIn('plot.gif', results[0]['attributes'][1]['image']['src']) + self.assertIn('plot_alt', results[0]['attributes'][1]['image']['alt']) + + self.assertEqual(len(results[0]['urls']), 1) + + self.assertEqual(referer_url, results[0]['urls'][0]['url']) + self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) + self.assertEqual(referer_url, results[1]['url']) + self.assertEqual('Wolfram|Alpha', results[1]['title']) diff --git a/tests/unit/engines/test_wolframalpha_noapi.py b/tests/unit/engines/test_wolframalpha_noapi.py index 37f3a905..1129dc8b 100644 --- a/tests/unit/engines/test_wolframalpha_noapi.py +++ b/tests/unit/engines/test_wolframalpha_noapi.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- from collections import defaultdict +import mock +from requests import Request from searx.engines import wolframalpha_noapi from searx.testing import SearxTestCase @@ -9,15 +11,228 @@ class TestWolframAlphaNoAPIEngine(SearxTestCase): def test_request(self): query = 'test_query' dicto = defaultdict(dict) - dicto['pageno'] = 1 params = wolframalpha_noapi.request(query, dicto) + self.assertIn('url', params) + self.assertIn('https://www.wolframalpha.com/input/json.jsp', params['url']) self.assertIn(query, params['url']) - self.assertIn('wolframalpha.com', params['url']) + self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer']) def test_response(self): self.assertRaises(AttributeError, wolframalpha_noapi.response, None) self.assertRaises(AttributeError, wolframalpha_noapi.response, []) self.assertRaises(AttributeError, wolframalpha_noapi.response, '') self.assertRaises(AttributeError, wolframalpha_noapi.response, '[]') - # TODO + + referer_url = 'referer_url' + request = Request(headers={'Referer': referer_url}) + + # test failure + json = ''' + {"queryresult" : { + "success" : false, + "error" : false, + "numpods" : 0, + "id" : "", + "host" : "https:\/\/www5a.wolframalpha.com", + "didyoumeans" : {} + }} + ''' + response = mock.Mock(text=json, request=request) + self.assertEqual(wolframalpha_noapi.response(response), []) + + # test basic case + json = ''' + {"queryresult" : { + "success" : true, + "error" : false, + "numpods" : 6, + "datatypes" : "Math", + "id" : "queryresult_id", + "host" : "https:\/\/www5b.wolframalpha.com", + "related" : "related_url", + "version" : "2.6", + "pods" : [ + { + "title" : "Input", + "scanners" : [ + "Identity" + ], + "id" : "Input", + "error" : false, + "numsubpods" : 1, + "subpods" : [ + { + "title" : "", + "img" : { + "src" : "input_img_src.gif", + "alt" : "input_img_alt", + "title" : "input_img_title" + }, + "plaintext" : "input_plaintext", + "minput" : "input_minput" + } + ] + }, + { + "title" : "Result", + "scanners" : [ + "Simplification" + ], + "id" : "Result", + "error" : false, + "numsubpods" : 1, + "primary" : true, + "subpods" : [ + { + "title" : "", + "img" : { + "src" : "result_img_src.gif", + "alt" : "result_img_alt", + "title" : "result_img_title" + }, + "plaintext" : "result_plaintext", + "moutput" : "result_moutput" + } + ] + }, + { + "title" : "Manipulatives illustration", + "scanners" : [ + "Arithmetic" + ], + "id" : "Illustration", + "error" : false, + "numsubpods" : 1, + "subpods" : [ + { + "title" : "", + "CDFcontent" : "Resizeable", + "img" : { + "src" : "illustration_img_src.gif", + "alt" : "illustration_img_alt", + "title" : "illustration_img_title" + }, + "plaintext" : "illustration_img_plaintext" + } + ] + } + ] + }} + ''' + response = mock.Mock(text=json, request=request) + results = wolframalpha_noapi.response(response) + self.assertEqual(type(results), list) + self.assertEqual(len(results), 2) + self.assertIn('input_plaintext', results[0]['infobox']) + + self.assertEqual(len(results[0]['attributes']), 3) + self.assertIn('Input', results[0]['attributes'][0]['label']) + self.assertIn('input_plaintext', results[0]['attributes'][0]['value']) + self.assertIn('Result', results[0]['attributes'][1]['label']) + self.assertIn('result_plaintext', results[0]['attributes'][1]['value']) + self.assertIn('Manipulatives illustration', results[0]['attributes'][2]['label']) + self.assertIn('illustration_img_src.gif', results[0]['attributes'][2]['image']['src']) + self.assertIn('illustration_img_alt', results[0]['attributes'][2]['image']['alt']) + + self.assertEqual(len(results[0]['urls']), 1) + + self.assertEqual(referer_url, results[0]['urls'][0]['url']) + self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) + self.assertEqual(referer_url, results[1]['url']) + self.assertEqual('Wolfram|Alpha', results[1]['title']) + + # test calc + json = """ + {"queryresult" : { + "success" : true, + "error" : false, + "numpods" : 2, + "datatypes" : "", + "id" : "queryresult_id", + "host" : "https:\/\/www4b.wolframalpha.com", + "related" : "related_url", + "version" : "2.6", + "pods" : [ + { + "title" : "Indefinite integral", + "scanners" : [ + "Integral" + ], + "id" : "IndefiniteIntegral", + "error" : false, + "numsubpods" : 1, + "primary" : true, + "subpods" : [ + { + "title" : "", + "img" : { + "src" : "integral_img_src.gif", + "alt" : "integral_img_alt", + "title" : "integral_img_title" + }, + "plaintext" : "integral_plaintext", + "minput" : "integral_minput", + "moutput" : "integral_moutput" + } + ] + }, + { + "title" : "Plot of the integral", + "scanners" : [ + "Integral" + ], + "id" : "Plot", + "error" : false, + "numsubpods" : 0, + "async" : "invalid_async_url" + } + ] + }} + """ + response = mock.Mock(text=json, request=request) + results = wolframalpha_noapi.response(response) + self.assertEqual(type(results), list) + self.assertEqual(len(results), 2) + self.assertIn('integral_plaintext', results[0]['infobox']) + + self.assertEqual(len(results[0]['attributes']), 1) + self.assertIn('Indefinite integral', results[0]['attributes'][0]['label']) + self.assertIn('integral_plaintext', results[0]['attributes'][0]['value']) + + self.assertEqual(len(results[0]['urls']), 1) + + self.assertEqual(referer_url, results[0]['urls'][0]['url']) + self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) + self.assertEqual(referer_url, results[1]['url']) + self.assertEqual('Wolfram|Alpha', results[1]['title']) + + def test_parse_async_pod(self): + self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, None) + self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, []) + self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, '') + self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, '[]') + + # test plot + xml = '''<?xml version='1.0' encoding='UTF-8'?> + <pod title='Plot' + scanner='Plot' + id='Plot' + error='false' + numsubpods='1'> + <subpod title=''> + <img src='plot_img_src.gif' + alt='plot_img_alt' + title='plot_img_title' /> + <plaintext>plot_plaintext</plaintext> + <minput>plot_minput</minput> + </subpod> + </pod> + ''' + response = mock.Mock(content=xml) + pod = wolframalpha_noapi.parse_async_pod(response) + self.assertEqual(len(pod['subpods']), 1) + self.assertEqual('', pod['subpods'][0]['title']) + self.assertEqual('plot_plaintext', pod['subpods'][0]['plaintext']) + self.assertEqual('plot_img_src.gif', pod['subpods'][0]['img']['src']) + self.assertEqual('plot_img_alt', pod['subpods'][0]['img']['alt'])