#!/usr/bin/env python # coding: utf-8 # # This file is part of Supysonic. # Supysonic is a Python implementation of the Subsonic server API. # # Copyright (C) 2017-2018 Alban 'spl0k' FĂ©ron # # Distributed under terms of the GNU AGPLv3 license. import unittest import flask.json from xml.etree import ElementTree from supysonic.api.formatters import JSONFormatter, JSONPFormatter, XMLFormatter from supysonic.py23 import strtype from ..testbase import TestBase class UnwrapperMixin(object): def make_response(self, elem, data): with self.request_context(): rv = super(UnwrapperMixin, self).make_response(elem, data) return rv.get_data(as_text = True) @staticmethod def create_from(cls): class Unwrapper(UnwrapperMixin, cls): pass return Unwrapper class ResponseHelperJsonTestCase(TestBase, UnwrapperMixin.create_from(JSONFormatter)): def make_response(self, elem, data): rv = super(ResponseHelperJsonTestCase, self).make_response(elem, data) return flask.json.loads(rv) def process_and_extract(self, d): return self.make_response('tag', d)['subsonic-response']['tag'] def test_basic(self): empty = self.empty self.assertEqual(len(empty), 1) self.assertIn('subsonic-response', empty) self.assertIsInstance(empty['subsonic-response'], dict) resp = empty['subsonic-response'] self.assertEqual(len(resp), 2) self.assertIn('status', resp) self.assertIn('version', resp) self.assertEqual(resp['status'], 'ok') resp = self.error(0, 'message')['subsonic-response'] self.assertEqual(resp['status'], 'failed') some_dict = { 'intValue': 2, 'someString': 'Hello world!' } resp = self.process_and_extract(some_dict) self.assertIn('intValue', resp) self.assertIn('someString', resp) def test_lists(self): resp = self.process_and_extract({ 'someList': [ 2, 4, 8, 16 ], 'emptyList': [] }) self.assertIn('someList', resp) self.assertNotIn('emptyList', resp) self.assertListEqual(resp['someList'], [ 2, 4, 8, 16 ]) def test_dicts(self): resp = self.process_and_extract({ 'dict': { 's': 'Blah', 'i': 20 }, 'empty': {} }) self.assertIn('dict', resp) self.assertIn('empty', resp) self.assertDictEqual(resp['dict'], { 's': 'Blah', 'i': 20 }) self.assertDictEqual(resp['empty'], {}) def test_nesting(self): resp = self.process_and_extract({ 'dict': { 'value': 'hey look! a string', 'list': [ 1, 2, 3 ], 'emptyList': [], 'subdict': { 'a': 'A' } }, 'list': [ { 'b': 'B' }, { 'c': 'C' }, [ 4, 5, 6 ], 'final string' ] }) self.assertEqual(len(resp), 2) self.assertIn('dict', resp) self.assertIn('list', resp) d = resp['dict'] l = resp['list'] self.assertIn('value', d) self.assertIn('list', d) self.assertNotIn('emptyList', d) self.assertIn('subdict', d) self.assertIsInstance(d['value'], strtype) self.assertIsInstance(d['list'], list) self.assertIsInstance(d['subdict'], dict) self.assertEqual(l, [ { 'b': 'B' }, { 'c': 'C' }, [ 4, 5, 6 ], 'final string' ]) class ResponseHelperJsonpTestCase(TestBase, UnwrapperMixin.create_from(JSONPFormatter)): def test_basic(self): self._JSONPFormatter__callback = 'callback' # hacky result = self.empty self.assertTrue(result.startswith('callback({')) self.assertTrue(result.endswith('})')) json = flask.json.loads(result[9:-1]) self.assertIn('subsonic-response', json) class ResponseHelperXMLTestCase(TestBase, UnwrapperMixin.create_from(XMLFormatter)): def make_response(self, elem, data): xml = super(ResponseHelperXMLTestCase, self).make_response(elem, data) xml = xml.replace('xmlns="http://subsonic.org/restapi"', '') root = ElementTree.fromstring(xml) return root def process_and_extract(self, d): rv = self.make_response('tag', d) return rv.find('tag') def assertAttributesMatchDict(self, elem, d): d = { k: str(v) for k, v in d.items() } self.assertDictEqual(elem.attrib, d) def test_root(self): xml = super(ResponseHelperXMLTestCase, self).make_response('tag', {}) self.assertIn('')) def test_basic(self): empty = self.empty self.assertIsNotNone(empty.find('.[@version]')) self.assertIsNotNone(empty.find(".[@status='ok']")) resp = self.error(0, 'message') self.assertIsNotNone(resp.find(".[@status='failed']")) some_dict = { 'intValue': 2, 'someString': 'Hello world!' } resp = self.process_and_extract(some_dict) self.assertIsNotNone(resp.find('.[@intValue]')) self.assertIsNotNone(resp.find('.[@someString]')) def test_lists(self): resp = self.process_and_extract({ 'someList': [ 2, 4, 8, 16 ], 'emptyList': [] }) elems = resp.findall('./someList') self.assertEqual(len(elems), 4) self.assertIsNone(resp.find('./emptyList')) for e, i in zip(elems, [ 2, 4, 8, 16 ]): self.assertEqual(int(e.text), i) def test_dicts(self): resp = self.process_and_extract({ 'dict': { 's': 'Blah', 'i': 20 }, 'empty': {} }) d = resp.find('./dict') self.assertIsNotNone(d) self.assertIsNotNone(resp.find('./empty')) self.assertAttributesMatchDict(d, { 's': 'Blah', 'i': 20 }) def test_nesting(self): resp = self.process_and_extract({ 'dict': { 'somevalue': 'hey look! a string', 'list': [ 1, 2, 3 ], 'emptyList': [], 'subdict': { 'a': 'A' } }, 'list': [ { 'b': 'B' }, { 'c': 'C' }, 'final string' ] }) self.assertEqual(len(resp), 4) # 'dict' and 3 'list's d = resp.find('./dict') lists = resp.findall('./list') self.assertIsNotNone(d) self.assertAttributesMatchDict(d, { 'somevalue': 'hey look! a string' }) self.assertEqual(len(d.findall('./list')), 3) self.assertEqual(len(d.findall('./emptyList')), 0) self.assertIsNotNone(d.find('./subdict')) self.assertEqual(len(lists), 3) self.assertAttributesMatchDict(lists[0], { 'b': 'B' }) self.assertAttributesMatchDict(lists[1], { 'c': 'C' }) self.assertEqual(lists[2].text, 'final string') def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ResponseHelperJsonTestCase)) suite.addTest(unittest.makeSuite(ResponseHelperJsonpTestCase)) suite.addTest(unittest.makeSuite(ResponseHelperXMLTestCase)) return suite if __name__ == '__main__': unittest.main()