diff --git a/tests/__init__.py b/tests/__init__.py index e976340..fa56d18 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -11,9 +11,7 @@ import unittest -import base, managers, api - -from .test_frontend import FrontendTestCase +import base, managers, api, frontend def suite(): suite = unittest.TestSuite() @@ -21,7 +19,6 @@ def suite(): suite.addTest(managers.suite()) suite.addTest(base.suite()) suite.addTest(api.suite()) - - suite.addTest(unittest.makeSuite(FrontendTestCase)) + suite.addTest(frontend.suite()) return suite diff --git a/tests/api/apitestbase.py b/tests/api/apitestbase.py index e8b846d..9a45b19 100644 --- a/tests/api/apitestbase.py +++ b/tests/api/apitestbase.py @@ -8,44 +8,27 @@ # # Distributed under terms of the GNU AGPLv3 license. -import unittest - import re -import sys from lxml import etree - from supysonic.managers.user import UserManager -from .appmock import AppMock +from ..testbase import TestBase path_replace_regexp = re.compile(r'/(\w+)') NS = 'http://subsonic.org/restapi' NSMAP = { 'sub': NS } -class ApiTestBase(unittest.TestCase): +class ApiTestBase(TestBase): + __module_to_test__ = 'supysonic.api' + def setUp(self): - app_mock = AppMock() - self.app = app_mock.app - self.store = app_mock.store - self.client = self.app.test_client() - - sys.modules['supysonic.web'] = app_mock - import supysonic.api - - UserManager.add(self.store, 'alice', 'Alic3', 'test@example.com', True) - UserManager.add(self.store, 'bob', 'B0b', 'bob@example.com', False) + super(ApiTestBase, self).setUp() xsd = etree.parse('tests/assets/subsonic-rest-api-1.8.0.xsd') self.schema = etree.XMLSchema(xsd) - def tearDown(self): - self.store.close() - to_unload = [ m for m in sys.modules if m.startswith('supysonic') ] - for m in to_unload: - del sys.modules[m] - def _find(self, xml, path): """ Helper method that insert the namespace in ElementPath 'path' diff --git a/tests/api/test_api_setup.py b/tests/api/test_api_setup.py index 17686c9..10354cc 100644 --- a/tests/api/test_api_setup.py +++ b/tests/api/test_api_setup.py @@ -12,35 +12,14 @@ import base64 import binascii -import io import simplejson -import sys -import unittest -from flask import request from xml.etree import ElementTree -from supysonic.managers.user import UserManager +from ..testbase import TestBase -from .appmock import AppMock - -class ApiSetupTestCase(unittest.TestCase): - def setUp(self): - app_mock = AppMock() - self.app = app_mock.app - self.store = app_mock.store - self.client = self.app.test_client() - - sys.modules['supysonic.web'] = app_mock - import supysonic.api - - UserManager.add(self.store, 'alice', 'Alic3', 'test@example.com', True) - - def tearDown(self): - self.store.close() - to_unload = [ m for m in sys.modules if m.startswith('supysonic') ] - for m in to_unload: - del sys.modules[m] +class ApiSetupTestCase(TestBase): + __module_to_test__ = 'supysonic.api' def __basic_auth_get(self, username, password): hashed = base64.b64encode('{}:{}'.format(username, password)) diff --git a/tests/api/test_response_helper.py b/tests/api/test_response_helper.py index 8f5bb19..8f7048f 100644 --- a/tests/api/test_response_helper.py +++ b/tests/api/test_response_helper.py @@ -14,7 +14,7 @@ import unittest, sys import simplejson from xml.etree import ElementTree -from .appmock import AppMock +from ..appmock import AppMock class ResponseHelperBaseCase(unittest.TestCase): def setUp(self): diff --git a/tests/api/appmock.py b/tests/appmock.py similarity index 85% rename from tests/api/appmock.py rename to tests/appmock.py index a13c2c7..6567b20 100644 --- a/tests/api/appmock.py +++ b/tests/appmock.py @@ -14,8 +14,9 @@ from supysonic.db import get_store class AppMock(object): def __init__(self, with_store = True): - self.app = Flask(__name__) + self.app = Flask(__name__, template_folder = '../supysonic/templates') self.app.testing = True + self.app.secret_key = 'Testing secret' if with_store: self.store = get_store('sqlite:') diff --git a/tests/frontend/__init__.py b/tests/frontend/__init__.py new file mode 100644 index 0000000..df7328c --- /dev/null +++ b/tests/frontend/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# This file is part of Supysonic. +# Supysonic is a Python implementation of the Subsonic server API. +# +# Copyright (C) 2017 Alban 'spl0k' Féron +# +# Distributed under terms of the GNU AGPLv3 license. + +import unittest + +from .test_login import LoginTestCase + +def suite(): + suite = unittest.TestSuite() + + suite.addTest(unittest.makeSuite(LoginTestCase)) + + return suite + diff --git a/tests/frontend/test_login.py b/tests/frontend/test_login.py new file mode 100644 index 0000000..03a0d7f --- /dev/null +++ b/tests/frontend/test_login.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# This file is part of Supysonic. +# Supysonic is a Python implementation of the Subsonic server API. +# +# Copyright (C) 2013-2017 Alban 'spl0k' Féron +# 2017 Óscar García Amor +# +# Distributed under terms of the GNU AGPLv3 license. + +import uuid + +from supysonic.db import User + +from ..testbase import TestBase + +class LoginTestCase(TestBase): + __module_to_test__ = 'supysonic.frontend' + + def test_unauthorized_request(self): + # Unauthorized request + rv = self.client.get('/', follow_redirects=True) + self.assertIn('Please login', rv.data) + + def test_login_with_bad_data(self): + # Login with not blank user or password + rv = self.client.post('/user/login', data=dict(name='', password=''), follow_redirects=True) + self.assertIn('Missing user name', rv.data) + self.assertIn('Missing password', rv.data) + # Login with not valid user or password + rv = self.client.post('/user/login', data=dict(user='nonexistent', password='nonexistent'), follow_redirects=True) + self.assertIn('No such user', rv.data) + rv = self.client.post('/user/login', data=dict(user='alice', password='badpassword'), follow_redirects=True) + self.assertIn('Wrong password', rv.data) + + def test_login_admin(self): + # Login with a valid admin user + rv = self.client.post('/user/login', data=dict(user='alice', password='Alic3'), follow_redirects=True) + self.assertIn('Logged in', rv.data) + self.assertIn('Users', rv.data) + self.assertIn('Folders', rv.data) + + def test_login_non_admin(self): + # Login with a valid non-admin user + rv = self.client.post('/user/login', data=dict(user='bob', password='B0b'), follow_redirects=True) + self.assertIn('Logged in', rv.data) + # Non-admin user cannot acces to users and folders + self.assertNotIn('Users', rv.data) + self.assertNotIn('Folders', rv.data) + + def test_root_with_valid_session(self): + # Root with valid session + with self.client.session_transaction() as sess: + sess['userid'] = self.store.find(User, User.name == 'alice').one().id + sess['username'] = 'alice' + rv = self.client.get('/', follow_redirects=True) + self.assertIn('alice', rv.data) + self.assertIn('Log out', rv.data) + self.assertIn('There\'s nothing much to see here.', rv.data) + + def test_root_with_non_valid_session(self): + # Root with a no-valid session + with self.client.session_transaction() as sess: + sess['userid'] = uuid.uuid4() + sess['username'] = 'alice' + rv = self.client.get('/', follow_redirects=True) + self.assertIn('Please login', rv.data) + # Root with a no-valid user + with self.client.session_transaction() as sess: + sess['userid'] = self.store.find(User, User.name == 'alice').one().id + sess['username'] = 'nonexistent' + rv = self.client.get('/', follow_redirects=True) + self.assertIn('Please login', rv.data) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_frontend.py b/tests/test_frontend.py deleted file mode 100644 index f7d8d26..0000000 --- a/tests/test_frontend.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# vim:fenc=utf-8 -# -# This file is part of Supysonic. -# Supysonic is a Python implementation of the Subsonic server API. -# -# Copyright (C) 2013-2017 Alban 'spl0k' Féron -# 2017 Óscar García Amor -# -# Distributed under terms of the GNU AGPLv3 license. - -from supysonic import db -from supysonic.managers.user import UserManager - -import sys -import unittest -import uuid - -# Create an empty sqlite database in memory -store = db.get_store("sqlite:") -# Read schema from file -with open('schema/sqlite.sql') as sql: - schema = sql.read() -# Create tables on memory database -for command in schema.split(';'): - store.execute(command) -# Create some users -UserManager.add(store, 'alice', 'alice', 'test@example.com', True) -UserManager.add(store, 'bob', 'bob', 'bob@example.com', False) -UserManager.add(store, 'charlie', 'charlie', 'charlie@example.com', False) - -# Create a mockup of web -from flask import Flask -app = Flask(__name__, template_folder='../supysonic/templates') -class web(): - app = app - store = store -sys.modules['supysonic.web'] = web() - -# Import module and set app in test mode -import supysonic.frontend -app.secret_key = 'test-suite' - -class FrontendTestCase(unittest.TestCase): - def setUp(self): - self.app = app.test_client() - - def test_unauthorized_request(self): - # Unauthorized request - rv = self.app.get('/', follow_redirects=True) - self.assertIn('Please login', rv.data) - - def test_login_with_bad_data(self): - # Login with not blank user or password - rv = self.app.post('/user/login', data=dict(name='', password=''), follow_redirects=True) - self.assertIn('Missing user name', rv.data) - self.assertIn('Missing password', rv.data) - # Login with not valid user or password - rv = self.app.post('/user/login', data=dict(user='nonexistent', password='nonexistent'), follow_redirects=True) - self.assertIn('No such user', rv.data) - rv = self.app.post('/user/login', data=dict(user='alice', password='badpassword'), follow_redirects=True) - self.assertIn('Wrong password', rv.data) - - def test_login_admin(self): - # Login with a valid admin user - rv = self.app.post('/user/login', data=dict(user='alice', password='alice'), follow_redirects=True) - self.assertIn('Logged in', rv.data) - self.assertIn('Users', rv.data) - self.assertIn('Folders', rv.data) - - def test_login_non_admin(self): - # Login with a valid non-admin user - rv = self.app.post('/user/login', data=dict(user='bob', password='bob'), follow_redirects=True) - self.assertIn('Logged in', rv.data) - # Non-admin user cannot acces to users and folders - self.assertNotIn('Users', rv.data) - self.assertNotIn('Folders', rv.data) - - def test_root_with_valid_session(self): - # Root with valid session - with self.app.session_transaction() as sess: - sess['userid'] = store.find(db.User, db.User.name == 'alice').one().id - sess['username'] = 'alice' - rv = self.app.get('/', follow_redirects=True) - self.assertIn('alice', rv.data) - self.assertIn('Log out', rv.data) - self.assertIn('There\'s nothing much to see here.', rv.data) - - def test_root_with_non_valid_session(self): - # Root with a no-valid session - with self.app.session_transaction() as sess: - sess['userid'] = uuid.uuid4() - sess['username'] = 'alice' - rv = self.app.get('/', follow_redirects=True) - self.assertIn('Please login', rv.data) - # Root with a no-valid user - with self.app.session_transaction() as sess: - sess['userid'] = store.find(db.User, db.User.name == 'alice').one().id - sess['username'] = 'nonexistent' - rv = self.app.get('/', follow_redirects=True) - self.assertIn('Please login', rv.data) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/testbase.py b/tests/testbase.py new file mode 100644 index 0000000..35ec190 --- /dev/null +++ b/tests/testbase.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# This file is part of Supysonic. +# Supysonic is a Python implementation of the Subsonic server API. +# +# Copyright (C) 2017 Alban 'spl0k' Féron +# +# Distributed under terms of the GNU AGPLv3 license. + +import importlib +import unittest +import sys + +from supysonic.managers.user import UserManager + +from .appmock import AppMock + +class TestBase(unittest.TestCase): + def setUp(self): + app_mock = AppMock() + self.app = app_mock.app + self.store = app_mock.store + self.client = self.app.test_client() + + sys.modules['supysonic.web'] = app_mock + importlib.import_module(self.__module_to_test__) + + UserManager.add(self.store, 'alice', 'Alic3', 'test@example.com', True) + UserManager.add(self.store, 'bob', 'B0b', 'bob@example.com', False) + + def tearDown(self): + self.store.close() + to_unload = [ m for m in sys.modules if m.startswith('supysonic') ] + for m in to_unload: + del sys.modules[m] +