2017-11-01 19:55:35 +00:00
|
|
|
# This file is part of Supysonic.
|
|
|
|
# Supysonic is a Python implementation of the Subsonic server API.
|
|
|
|
#
|
2020-11-09 11:03:05 +00:00
|
|
|
# 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
|
|
|
|
from supysonic.managers.user import UserManager
|
|
|
|
|
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
|
|
|
|
2017-11-17 22:26:25 +00:00
|
|
|
def setUp(self):
|
|
|
|
super(ApiTestBase, self).setUp()
|
2017-11-01 19:55:35 +00:00
|
|
|
|
2020-11-08 17:00:36 +00:00
|
|
|
xsd = etree.parse("tests/assets/subsonic-rest-api-1.10.2.xsd")
|
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
|
|
|
|
2020-11-09 11:03:05 +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__)
|
|
|
|
|
2019-06-29 15:25:44 +00:00
|
|
|
args.update({"c": "tests", "v": "1.9.0"})
|
|
|
|
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)
|
2020-11-09 11:03:05 +00:00
|
|
|
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
|