1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-11-14 14:12:17 +00:00
supysonic/tests/api/apitestbase.py

95 lines
3.4 KiB
Python
Raw Normal View History

2017-11-01 19:55:35 +00:00
# This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API.
#
# Copyright (C) 2017-2020 Alban 'spl0k' Féron
2017-11-01 19:55:35 +00:00
#
# Distributed under terms of the GNU AGPLv3 license.
import re
from lxml import etree
2017-11-17 22:26:25 +00:00
from ..testbase import TestBase
2017-11-01 19:55:35 +00:00
2019-06-29 15:25:44 +00:00
path_replace_regexp = re.compile(r"/(\w+)")
NS = "http://subsonic.org/restapi"
NSMAP = {"sub": NS}
2017-11-01 19:55:35 +00:00
2017-11-17 22:26:25 +00:00
class ApiTestBase(TestBase):
2017-11-27 21:30:13 +00:00
__with_api__ = True
2017-11-01 19:55:35 +00:00
def setUp(self, apiVersion="1.10.2"):
2020-11-22 15:12:14 +00:00
super().setUp()
self.apiVersion = apiVersion
xsd = etree.parse(
"tests/assets/subsonic-rest-api-{}.xsd".format(self.apiVersion)
)
2017-11-01 19:55:35 +00:00
self.schema = etree.XMLSchema(xsd)
def _find(self, xml, path):
"""
2017-11-08 22:21:52 +00:00
Helper method that insert the namespace in ElementPath 'path'
2017-11-01 19:55:35 +00:00
"""
2019-06-29 15:25:44 +00:00
path = path_replace_regexp.sub(r"/{{{}}}\1".format(NS), path)
2017-11-01 19:55:35 +00:00
return xml.find(path)
2017-11-08 22:21:52 +00:00
def _xpath(self, elem, path):
2017-11-03 22:15:48 +00:00
"""
2017-11-08 22:21:52 +00:00
Helper method that insert a prefix and map the namespace in XPath 'path'
2017-11-03 22:15:48 +00:00
"""
2019-06-29 15:25:44 +00:00
path = path_replace_regexp.sub(r"/sub:\1", path)
return elem.xpath(path, namespaces=NSMAP)
2017-11-03 22:15:48 +00:00
def _make_request(self, endpoint, args={}, tag=None, error=None, skip_post=False):
2017-11-01 19:55:35 +00:00
"""
Makes both a GET and POST requests against the API, assert both get the same response.
If the user isn't provided with the 'u' and 'p' in 'args', the default 'alice' is used.
Validate the response against the Subsonic API XSD and assert it matches the expected tag or error.
:param endpoint: request endpoint, with the '/rest/'prefix nor the '.view' suffix
:param args: dict of parameters. 'u', 'p', 'c' and 'v' are automatically added
:param tag: topmost expected element, right beneath 'subsonic-response'
:param error: if the given 'args' should produce an error at 'endpoint', this is the expected error code
:param skip_post: don't do the POST request
:return: a 2-tuple (resp, child) if no error, where 'resp' is the full response object, 'child' a
'lxml.etree.Element' mathching 'tag' (if any). If there's an error (when expected), only returns
the response object
"""
if not isinstance(args, dict):
raise TypeError("'args', expecting a dict, got " + type(args).__name__)
2019-12-23 15:23:57 +00:00
if tag and not isinstance(tag, str):
2017-11-01 19:55:35 +00:00
raise TypeError("'tag', expecting a str, got " + type(tag).__name__)
args.update({"c": "tests", "v": self.apiVersion})
2019-06-29 15:25:44 +00:00
if "u" not in args:
args.update({"u": "alice", "p": "Alic3"})
2017-11-01 19:55:35 +00:00
2019-06-29 15:25:44 +00:00
uri = "/rest/{}.view".format(endpoint)
rg = self.client.get(uri, query_string=args)
2017-11-01 19:55:35 +00:00
if not skip_post:
2019-06-29 15:25:44 +00:00
rp = self.client.post(uri, data=args)
2017-11-01 19:55:35 +00:00
self.assertEqual(rg.data, rp.data)
xml = etree.fromstring(rg.data)
self.schema.assert_(xml)
2017-11-01 19:55:35 +00:00
2019-06-29 15:25:44 +00:00
if xml.get("status") == "ok":
2017-11-01 19:55:35 +00:00
self.assertIsNone(error)
if tag:
2019-06-29 15:25:44 +00:00
self.assertEqual(xml[0].tag, "{{{}}}{}".format(NS, tag))
2017-11-01 19:55:35 +00:00
return rg, xml[0]
else:
self.assertEqual(len(xml), 0)
return rg, None
else:
self.assertIsNone(tag)
2019-06-29 15:25:44 +00:00
self.assertEqual(xml[0].tag, "{{{}}}error".format(NS))
self.assertEqual(xml[0].get("code"), str(error))
2017-11-01 19:55:35 +00:00
return rg