summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrr- <rr-@sakuya.pl>2016-04-15 14:36:00 +0200
committerrr- <rr-@sakuya.pl>2016-04-15 18:00:06 +0200
commit07ea920def9f158682a1ecdac5e5d5692faa81d6 (patch)
tree8ae991a304c9aa58648706b8b417bbf0b222468b
parent7f8c2cfa007705c374fc25d330b680246feb709d (diff)
server/tests: switch to pytest
-rw-r--r--.gitignore2
-rw-r--r--server/requirements.txt3
-rw-r--r--server/szurubooru/tests/__init__.py0
-rw-r--r--server/szurubooru/tests/api/__init__.py0
-rw-r--r--server/szurubooru/tests/api/test_password_reset.py78
-rw-r--r--server/szurubooru/tests/api/test_password_reset_api.py68
-rw-r--r--server/szurubooru/tests/api/test_user_api.py366
-rw-r--r--server/szurubooru/tests/api/test_user_creating.py112
-rw-r--r--server/szurubooru/tests/api/test_user_deletion.py65
-rw-r--r--server/szurubooru/tests/api/test_user_retrieval.py116
-rw-r--r--server/szurubooru/tests/api/test_user_updating.py237
-rw-r--r--server/szurubooru/tests/api/util.py42
-rw-r--r--server/szurubooru/tests/conftest.py61
-rw-r--r--server/szurubooru/tests/database_test_case.py11
-rw-r--r--server/szurubooru/tests/search/__init__.py0
-rw-r--r--server/szurubooru/tests/search/test_user_search_config.py340
-rw-r--r--server/szurubooru/tests/util/test_misc.py52
-rwxr-xr-xserver/test7
18 files changed, 859 insertions, 701 deletions
diff --git a/.gitignore b/.gitignore
index e2548a3..a683cef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
config.yaml
*/*_modules/
+.coverage
+.cache
diff --git a/server/requirements.txt b/server/requirements.txt
index 5c0d15a..c59cf69 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -3,4 +3,5 @@ pyyaml>=3.11
falcon>=0.3.0
psycopg2>=2.6.1
SQLAlchemy>=1.0.12
-green>=2.4.0
+pytest>=2.9.1
+pytest-cov>=2.2.1
diff --git a/server/szurubooru/tests/__init__.py b/server/szurubooru/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/server/szurubooru/tests/__init__.py
+++ /dev/null
diff --git a/server/szurubooru/tests/api/__init__.py b/server/szurubooru/tests/api/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/server/szurubooru/tests/api/__init__.py
+++ /dev/null
diff --git a/server/szurubooru/tests/api/test_password_reset.py b/server/szurubooru/tests/api/test_password_reset.py
new file mode 100644
index 0000000..b29590f
--- /dev/null
+++ b/server/szurubooru/tests/api/test_password_reset.py
@@ -0,0 +1,78 @@
+from datetime import datetime
+from unittest import mock
+import pytest
+from szurubooru import api, db, errors
+from szurubooru.util import auth, mailer
+
+def mock_user(name, rank, email):
+ user = db.User()
+ user.name = name
+ user.password = 'dummy'
+ user.password_salt = 'dummy'
+ user.password_hash = 'dummy'
+ user.email = email
+ user.rank = rank
+ user.creation_time = datetime(1997, 1, 1)
+ user.avatar_style = db.User.AVATAR_GRAVATAR
+ return user
+
+@pytest.fixture
+def password_reset_api(config_injector):
+ config_injector({
+ 'secret': 'x',
+ 'base_url': 'http://example.com/',
+ 'name': 'Test instance',
+ })
+ return api.PasswordResetApi()
+
+def test_reset_non_existing(password_reset_api, context_factory):
+ with pytest.raises(errors.NotFoundError):
+ password_reset_api.get(context_factory(), 'u1')
+
+def test_reset_without_email(password_reset_api, session, context_factory):
+ user = mock_user('u1', 'regular_user', None)
+ session.add(user)
+ with pytest.raises(errors.ValidationError):
+ password_reset_api.get(context_factory(), 'u1')
+
+def test_reset_sending_email(password_reset_api, session, context_factory):
+ user = mock_user('u1', 'regular_user', 'user@example.com')
+ session.add(user)
+ for getter in ['u1', 'user@example.com']:
+ mailer.send_mail = mock.MagicMock()
+ assert password_reset_api.get(context_factory(), getter) == {}
+ mailer.send_mail.assert_called_once_with(
+ 'noreply@Test instance',
+ 'user@example.com',
+ 'Password reset for Test instance',
+ 'You (or someone else) requested to reset your password ' +
+ 'on Test instance.\nIf you wish to proceed, click this l' +
+ 'ink: http://example.com/password-reset/u1:4ac0be176fb36' +
+ '4f13ee6b634c43220e2\nOtherwise, please ignore this email.')
+
+def test_confirmation_non_existing(password_reset_api, context_factory):
+ with pytest.raises(errors.NotFoundError):
+ password_reset_api.post(context_factory(), 'u1')
+
+def test_confirmation_no_token(password_reset_api, context_factory, session):
+ user = mock_user('u1', 'regular_user', 'user@example.com')
+ session.add(user)
+ with pytest.raises(errors.ValidationError):
+ password_reset_api.post(context_factory(request={}), 'u1')
+
+def test_confirmation_bad_token(password_reset_api, context_factory, session):
+ user = mock_user('u1', 'regular_user', 'user@example.com')
+ session.add(user)
+ with pytest.raises(errors.ValidationError):
+ password_reset_api.post(
+ context_factory(request={'token': 'bad'}), 'u1')
+
+def test_confirmation_good_token(password_reset_api, context_factory, session):
+ user = mock_user('u1', 'regular_user', 'user@example.com')
+ old_hash = user.password_hash
+ session.add(user)
+ context = context_factory(
+ request={'token': '4ac0be176fb364f13ee6b634c43220e2'})
+ result = password_reset_api.post(context, 'u1')
+ assert user.password_hash != old_hash
+ assert auth.is_valid_password(user, result['password']) is True
diff --git a/server/szurubooru/tests/api/test_password_reset_api.py b/server/szurubooru/tests/api/test_password_reset_api.py
deleted file mode 100644
index 93742b9..0000000
--- a/server/szurubooru/tests/api/test_password_reset_api.py
+++ /dev/null
@@ -1,68 +0,0 @@
-from unittest import mock
-from szurubooru import api, errors
-from szurubooru.util import auth, mailer
-from szurubooru.tests.database_test_case import DatabaseTestCase
-from szurubooru.tests.api import util
-
-class TestPasswordReset(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- util.mock_config({
- 'secret': 'x',
- 'base_url': 'http://example.com/',
- 'name': 'Test instance',
- })
- util.mock_context(self)
- self.api = api.PasswordResetApi()
-
- def test_reset_non_existing(self):
- self.assertRaises(errors.NotFoundError, self.api.get, self.context, 'u1')
-
- def test_reset_without_email(self):
- user = util.mock_user('u1', 'regular_user')
- user.email = None
- self.session.add(user)
- self.assertRaises(errors.ValidationError, self.api.get, self.context, 'u1')
-
- def test_reset_sending_email(self):
- user = util.mock_user('u1', 'regular_user')
- user.email = 'user@example.com'
- self.session.add(user)
- for getter in ['u1', 'user@example.com']:
- mailer.send_mail = mock.MagicMock()
- self.assertEqual({}, self.api.get(self.context, getter))
- mailer.send_mail.assert_called_once_with(
- 'noreply@Test instance',
- 'user@example.com',
- 'Password reset for Test instance',
- 'You (or someone else) requested to reset your password ' +
- 'on Test instance.\nIf you wish to proceed, click this l' +
- 'ink: http://example.com/password-reset/u1:4ac0be176fb36' +
- '4f13ee6b634c43220e2\nOtherwise, please ignore this email.')
-
- def test_confirmation_non_existing(self):
- self.assertRaises(errors.NotFoundError, self.api.post, self.context, 'u1')
-
- def test_confirmation_no_token(self):
- user = util.mock_user('u1', 'regular_user')
- user.email = 'user@example.com'
- self.session.add(user)
- self.context.request = {}
- self.assertRaises(errors.ValidationError, self.api.post, self.context, 'u1')
-
- def test_confirmation_bad_token(self):
- user = util.mock_user('u1', 'regular_user')
- user.email = 'user@example.com'
- self.session.add(user)
- self.context.request = {'token': 'bad'}
- self.assertRaises(errors.ValidationError, self.api.post, self.context, 'u1')
-
- def test_confirmation_good_token(self):
- user = util.mock_user('u1', 'regular_user')
- user.email = 'user@example.com'
- old_hash = user.password_hash
- self.session.add(user)
- self.context.request = {'token': '4ac0be176fb364f13ee6b634c43220e2'}
- result = self.api.post(self.context, 'u1')
- self.assertNotEqual(user.password_hash, old_hash)
- self.assertTrue(auth.is_valid_password(user, result['password']))
diff --git a/server/szurubooru/tests/api/test_user_api.py b/server/szurubooru/tests/api/test_user_api.py
deleted file mode 100644
index a2d9be2..0000000
--- a/server/szurubooru/tests/api/test_user_api.py
+++ /dev/null
@@ -1,366 +0,0 @@
-import tempfile
-from datetime import datetime
-from szurubooru import api, db, errors
-from szurubooru.util import auth
-from szurubooru.tests.database_test_case import DatabaseTestCase
-from szurubooru.tests.api import util
-
-class TestRetrievingUsers(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- util.mock_config({
- 'privileges': {
- 'users:list': 'regular_user',
- },
- 'thumbnails': {'avatar_width': 200},
- 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
- 'rank_names': {},
- })
- util.mock_context(self)
-
- def test_retrieving_multiple(self):
- user1 = util.mock_user('u1', 'mod')
- user2 = util.mock_user('u2', 'mod')
- self.session.add_all([user1, user2])
- util.mock_params(self.context, {'query': '', 'page': 1})
- self.context.user.rank = 'regular_user'
- api_ = api.UserListApi()
- result = api_.get(self.context)
- self.assertEqual(result['query'], '')
- self.assertEqual(result['page'], 1)
- self.assertEqual(result['pageSize'], 100)
- self.assertEqual(result['total'], 2)
- self.assertEqual([u['name'] for u in result['users']], ['u1', 'u2'])
-
- def test_retrieving_multiple_without_privileges(self):
- self.context.user.rank = 'anonymous'
- util.mock_params(self.context, {'query': '', 'page': 1})
- api_ = api.UserListApi()
- self.assertRaises(errors.AuthError, api_.get, self.context)
-
- def test_retrieving_non_existing(self):
- self.context.user.rank = 'regular_user'
- util.mock_params(self.context, {'query': 'asd', 'page': 1})
- api_ = api.UserListApi()
- result = api_.get(self.context)
- self.assertEqual(result['query'], 'asd')
- self.assertEqual(result['page'], 1)
- self.assertEqual(result['pageSize'], 100)
- self.assertEqual(result['total'], 0)
- self.assertEqual([u['name'] for u in result['users']], [])
-
-class TestRetrievingUser(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- util.mock_config({
- 'privileges': {
- 'users:view': 'regular_user',
- },
- 'thumbnails': {'avatar_width': 200},
- 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
- 'rank_names': {},
- })
- util.mock_context(self)
-
- def test_retrieving_single(self):
- user = util.mock_user('u1', 'regular_user')
- self.session.add(user)
- self.context.user.rank = 'regular_user'
- util.mock_params(self.context, {'query': '', 'page': 1})
- api_ = api.UserDetailApi()
- result = api_.get(self.context, 'u1')
- self.assertEqual(result['user']['id'], user.user_id)
- self.assertEqual(result['user']['name'], 'u1')
- self.assertEqual(result['user']['rank'], 'regular_user')
- self.assertEqual(result['user']['creationTime'], datetime(1997, 1, 1))
- self.assertEqual(result['user']['lastLoginTime'], None)
- self.assertEqual(result['user']['avatarStyle'], 'gravatar')
-
- def test_retrieving_non_existing(self):
- self.context.user.rank = 'regular_user'
- util.mock_params(self.context, {'query': '', 'page': 1})
- api_ = api.UserDetailApi()
- self.assertRaises(errors.NotFoundError, api_.get, self.context, '-')
-
- def test_retrieving_single_without_privileges(self):
- self.context.user.rank = 'anonymous'
- util.mock_params(self.context, {'query': '', 'page': 1})
- api_ = api.UserDetailApi()
- self.assertRaises(errors.AuthError, api_.get, self.context, '-')
-
-class TestDeletingUser(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- util.mock_config({
- 'privileges': {
- 'users:delete:self': 'regular_user',
- 'users:delete:any': 'mod',
- },
- 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
- 'rank_names': {},
- })
- util.mock_context(self)
-
- def test_removing_oneself(self):
- user1 = util.mock_user('u1', 'regular_user')
- user2 = util.mock_user('u2', 'regular_user')
- self.session.add_all([user1, user2])
- self.session.commit()
- self.context.user.user_id = user1.user_id
- self.context.user.rank = 'regular_user'
- api_ = api.UserDetailApi()
- self.assertRaises(errors.AuthError, api_.delete, self.context, 'u2')
- api_.delete(self.context, 'u1')
- self.assertEqual(self.session.query(db.User).filter_by(name='u1').all(), [])
-
- def test_removing_someone_else(self):
- user1 = util.mock_user('u1', 'regular_user')
- user2 = util.mock_user('u2', 'regular_user')
- self.session.add_all([user1, user2])
- self.session.commit()
- self.context.user.rank = 'mod'
- api_ = api.UserDetailApi()
- api_.delete(self.context, 'u1')
- api_.delete(self.context, 'u2')
- self.assertEqual(self.session.query(db.User).filter_by(name='u1').all(), [])
- self.assertEqual(self.session.query(db.User).filter_by(name='u2').all(), [])
-
- def test_removing_non_existing(self):
- self.context.user.rank = 'regular_user'
- api_ = api.UserDetailApi()
- self.assertRaises(errors.NotFoundError, api_.delete, self.context, 'bad')
-
-class TestCreatingUser(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- util.mock_config({
- 'secret': '',
- 'user_name_regex': '.{3,}',
- 'password_regex': '.{3,}',
- 'default_rank': 'regular_user',
- 'thumbnails': {'avatar_width': 200},
- 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
- 'rank_names': {},
- 'privileges': {
- 'users:create': 'anonymous',
- },
- })
- self.api = api.UserListApi()
- util.mock_context(self)
- self.context.user.rank = 'anonymous'
-
- def test_first_user_becomes_admin(self):
- self.context.request = {
- 'name': 'chewie',
- 'email': 'asd@asd.asd',
- 'password': 'oks',
- }
- self.api.post(self.context)
- created_user = self.session.query(db.User).filter_by(name='chewie').one()
- self.assertEqual(created_user.name, 'chewie')
- self.assertEqual(created_user.email, 'asd@asd.asd')
- self.assertEqual(created_user.rank, 'admin')
- self.assertTrue(auth.is_valid_password(created_user, 'oks'))
- self.assertFalse(auth.is_valid_password(created_user, 'invalid'))
-
- def test_subsequent_users_are_created_normally(self):
- self.context.request = {
- 'name': 'chewie',
- 'email': 'asd@asd.asd',
- 'password': 'oks',
- }
- self.api.post(self.context)
- self.context.request['name'] = 'chewie2'
- self.api.post(self.context)
- created_user = self.session.query(db.User).filter_by(name='chewie2').one()
- self.assertEqual(created_user.name, 'chewie2')
- self.assertEqual(created_user.email, 'asd@asd.asd')
- self.assertEqual(created_user.rank, 'regular_user')
- self.assertTrue(auth.is_valid_password(created_user, 'oks'))
- self.assertFalse(auth.is_valid_password(created_user, 'invalid'))
-
- def test_creating_user_that_already_exists(self):
- self.context.request = {
- 'name': 'chewie',
- 'email': 'asd@asd.asd',
- 'password': 'oks',
- }
- self.api.post(self.context)
- self.assertRaises(errors.IntegrityError, self.api.post, self.context)
-
- def test_creating_user_that_already_exists_insensitive(self):
- self.context.request = {
- 'name': 'chewie',
- 'email': 'asd@asd.asd',
- 'password': 'oks',
- }
- self.api.post(self.context)
- self.context.name = 'chewie'
- self.assertRaises(errors.IntegrityError, self.api.post, self.context)
-
- def test_missing_field(self):
- for key in ['name', 'email', 'password']:
- self.context.request = {
- 'name': 'chewie',
- 'email': 'asd@asd.asd',
- 'password': 'oks',
- }
- del self.context.request[key]
- self.assertRaises(errors.ValidationError, self.api.post, self.context)
-
-class TestUpdatingUser(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- util.mock_config({
- 'secret': '',
- 'user_name_regex': '.{3,}',
- 'password_regex': '.{3,}',
- 'data_dir': tempfile.gettempdir(),
- 'data_url': 'http://example.com/data/',
- 'thumbnails': {'avatar_width': 200, 'avatar_height': 200},
- 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
- 'rank_names': {},
- 'privileges': {
- 'users:edit:self:name': 'regular_user',
- 'users:edit:self:pass': 'regular_user',
- 'users:edit:self:email': 'regular_user',
- 'users:edit:self:rank': 'mod',
- 'users:edit:self:avatar': 'mod',
- 'users:edit:any:name': 'mod',
- 'users:edit:any:pass': 'mod',
- 'users:edit:any:email': 'mod',
- 'users:edit:any:rank': 'admin',
- 'users:edit:any:avatar': 'admin',
- },
- })
- util.mock_context(self)
- self.api = api.UserDetailApi()
-
- def test_update_changing_nothing(self):
- admin_user = util.mock_user('u1', 'admin')
- self.session.add(admin_user)
- self.context.user = admin_user
- self.api.put(self.context, 'u1')
- admin_user = self.session.query(db.User).filter_by(name='u1').one()
- self.assertEqual(admin_user.name, 'u1')
- self.assertEqual(admin_user.email, 'dummy')
- self.assertEqual(admin_user.rank, 'admin')
-
- def test_updating_non_existing_user(self):
- admin_user = util.mock_user('u1', 'admin')
- self.session.add(admin_user)
- self.context.user = admin_user
- self.assertRaises(errors.NotFoundError, self.api.put, self.context, 'u2')
-
- def test_admin_updating_everything_for_themselves(self):
- admin_user = util.mock_user('u1', 'admin')
- self.session.add(admin_user)
- self.context.user = admin_user
- self.context.request = {
- 'name': 'chewie',
- 'email': 'asd@asd.asd',
- 'password': 'oks',
- 'rank': 'mod',
- 'avatarStyle': 'gravatar',
- }
- self.api.put(self.context, 'u1')
- admin_user = self.session.query(db.User).filter_by(name='chewie').one()
- self.assertEqual(admin_user.name, 'chewie')
- self.assertEqual(admin_user.email, 'asd@asd.asd')
- self.assertEqual(admin_user.rank, 'mod')
- self.assertEqual(admin_user.avatar_style, admin_user.AVATAR_GRAVATAR)
- self.assertTrue(auth.is_valid_password(admin_user, 'oks'))
- self.assertFalse(auth.is_valid_password(admin_user, 'invalid'))
-
- def test_removing_email(self):
- admin_user = util.mock_user('u1', 'admin')
- self.session.add(admin_user)
- self.context.user = admin_user
- self.context.request = {'email': ''}
- self.api.put(self.context, 'u1')
- admin_user = self.session.query(db.User).filter_by(name='u1').one()
- self.assertEqual(admin_user.email, None)
-
- def test_invalid_inputs(self):
- admin_user = util.mock_user('u1', 'admin')
- self.session.add(admin_user)
- self.context.user = admin_user
- self.context.request = {'name': '.'}
- self.assertRaises(
- errors.ValidationError, self.api.put, self.context, 'u1')
- self.context.request = {'password': '.'}
- self.assertRaises(
- errors.ValidationError, self.api.put, self.context, 'u1')
- self.context.request = {'rank': '.'}
- self.assertRaises(
- errors.ValidationError, self.api.put, self.context, 'u1')
- self.context.request = {'email': '.'}
- self.assertRaises(
- errors.ValidationError, self.api.put, self.context, 'u1')
- self.context.request = {'avatarStyle': 'manual'}
- self.assertRaises(
- errors.ValidationError, self.api.put, self.context, 'u1')
-
- def test_user_trying_to_update_someone_else(self):
- user1 = util.mock_user('u1', 'regular_user')
- user2 = util.mock_user('u2', 'regular_user')
- self.session.add_all([user1, user2])
- self.context.user = user1
- for request in [
- {'name': 'whatever'},
- {'email': 'whatever'},
- {'rank': 'whatever'},
- {'password': 'whatever'}]:
- self.context.request = request
- self.assertRaises(
- errors.AuthError, self.api.put, self.context, user2.name)
-
- def test_user_trying_to_become_someone_else(self):
- user1 = util.mock_user('me', 'regular_user')
- user2 = util.mock_user('her', 'regular_user')
- self.session.add_all([user1, user2])
- self.context.user = user1
- self.context.request = {'name': 'her'}
- self.assertRaises(
- errors.IntegrityError, self.api.put, self.context, 'me')
- self.session.rollback()
-
- def test_user_trying_to_become_someone_else_insensitive(self):
- user1 = util.mock_user('me', 'regular_user')
- user2 = util.mock_user('her', 'regular_user')
- self.session.add_all([user1, user2])
- self.context.user = user1
- self.context.request = {'name': 'HER'}
- self.assertRaises(
- errors.IntegrityError, self.api.put, self.context, 'me')
- self.session.rollback()
-
- def test_mods_trying_to_become_admin(self):
- user1 = util.mock_user('u1', 'mod')
- user2 = util.mock_user('u2', 'mod')
- self.session.add_all([user1, user2])
- self.context.user = user1
- self.context.request = {'rank': 'admin'}
- self.assertRaises(
- errors.AuthError, self.api.put, self.context, user1.name)
- self.assertRaises(
- errors.AuthError, self.api.put, self.context, user2.name)
-
- def test_uploading_avatar(self):
- user = util.mock_user('u1', 'mod')
- self.session.add(user)
- self.context.user = user
- self.context.request = {
- 'avatarStyle': 'manual',
- }
- empty_pixel = \
- b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00' \
- b'\xff\xff\xff\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00' \
- b'\x01\x00\x01\x00\x00\x02\x02\x4c\x01\x00\x3b'
- self.context.files['avatar'] = empty_pixel
- response = self.api.put(self.context, 'u1')
- user = self.session.query(db.User).filter_by(name='u1').one()
- self.assertEqual(user.avatar_style, user.AVATAR_MANUAL)
- self.assertEqual(
- response['user']['avatarUrl'],
- 'http://example.com/data/avatars/u1.jpg')
diff --git a/server/szurubooru/tests/api/test_user_creating.py b/server/szurubooru/tests/api/test_user_creating.py
new file mode 100644
index 0000000..b9d1204
--- /dev/null
+++ b/server/szurubooru/tests/api/test_user_creating.py
@@ -0,0 +1,112 @@
+import pytest
+from datetime import datetime
+from szurubooru import api, db, errors
+from szurubooru.util import auth
+
+@pytest.fixture
+def user_list_api():
+ return api.UserListApi()
+
+def test_creating_users(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_list_api):
+ config_injector({
+ 'secret': '',
+ 'user_name_regex': '.{3,}',
+ 'password_regex': '.{3,}',
+ 'default_rank': 'regular_user',
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ 'privileges': {'users:create': 'anonymous'},
+ })
+
+ user_list_api.post(
+ context_factory(
+ request={
+ 'name': 'chewie1',
+ 'email': 'asd@asd.asd',
+ 'password': 'oks',
+ },
+ user=user_factory(rank='regular_user')))
+ user_list_api.post(
+ context_factory(
+ request={
+ 'name': 'chewie2',
+ 'email': 'asd@asd.asd',
+ 'password': 'sok',
+ },
+ user=user_factory(rank='regular_user')))
+
+ first_user = session.query(db.User).filter_by(name='chewie1').one()
+ other_user = session.query(db.User).filter_by(name='chewie2').one()
+ assert first_user.name == 'chewie1'
+ assert first_user.email == 'asd@asd.asd'
+ assert first_user.rank == 'admin'
+ assert auth.is_valid_password(first_user, 'oks') is True
+ assert auth.is_valid_password(first_user, 'invalid') is False
+ assert other_user.name == 'chewie2'
+ assert other_user.email == 'asd@asd.asd'
+ assert other_user.rank == 'regular_user'
+ assert auth.is_valid_password(other_user, 'sok') is True
+ assert auth.is_valid_password(other_user, 'invalid') is False
+
+def test_creating_user_that_already_exists(
+ config_injector, context_factory, user_factory, user_list_api):
+ config_injector({
+ 'secret': '',
+ 'user_name_regex': '.{3,}',
+ 'password_regex': '.{3,}',
+ 'default_rank': 'regular_user',
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ 'privileges': {'users:create': 'anonymous'},
+ })
+ user_list_api.post(
+ context_factory(
+ request={
+ 'name': 'chewie',
+ 'email': 'asd@asd.asd',
+ 'password': 'oks',
+ },
+ user=user_factory(rank='regular_user')))
+ with pytest.raises(errors.IntegrityError):
+ user_list_api.post(
+ context_factory(
+ request={
+ 'name': 'chewie',
+ 'email': 'asd@asd.asd',
+ 'password': 'oks',
+ },
+ user=user_factory(rank='regular_user')))
+ with pytest.raises(errors.IntegrityError):
+ user_list_api.post(
+ context_factory(
+ request={
+ 'name': 'CHEWIE',
+ 'email': 'asd@asd.asd',
+ 'password': 'oks',
+ },
+ user=user_factory(rank='regular_user')))
+
+@pytest.mark.parametrize('field', ['name', 'email', 'password'])
+def test_missing_field(
+ config_injector, context_factory, user_factory, user_list_api, field):
+ config_injector({
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'privileges': {'users:create': 'anonymous'},
+ })
+ request = {
+ 'name': 'chewie',
+ 'email': 'asd@asd.asd',
+ 'password': 'oks',
+ }
+ del request[field]
+ with pytest.raises(errors.ValidationError):
+ user_list_api.post(
+ context_factory(
+ request=request, user=user_factory(rank='regular_user')))
diff --git a/server/szurubooru/tests/api/test_user_deletion.py b/server/szurubooru/tests/api/test_user_deletion.py
new file mode 100644
index 0000000..d09d6e5
--- /dev/null
+++ b/server/szurubooru/tests/api/test_user_deletion.py
@@ -0,0 +1,65 @@
+import pytest
+from datetime import datetime
+from szurubooru import api, db, errors
+
+@pytest.fixture
+def user_detail_api():
+ return api.UserDetailApi()
+
+def test_removing_oneself(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'privileges': {
+ 'users:delete:self': 'regular_user',
+ 'users:delete:any': 'mod',
+ },
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ user1 = user_factory(name='u1', rank='regular_user')
+ user2 = user_factory(name='u2', rank='regular_user')
+ session.add_all([user1, user2])
+ session.commit()
+ with pytest.raises(errors.AuthError):
+ user_detail_api.delete(context_factory(user=user1), 'u2')
+ user_detail_api.delete(context_factory(user=user1), 'u1')
+ assert [u.name for u in session.query(db.User).all()] == ['u2']
+
+def test_removing_someone_else(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'privileges': {
+ 'users:delete:self': 'regular_user',
+ 'users:delete:any': 'mod',
+ },
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ user1 = user_factory(name='u1', rank='regular_user')
+ user2 = user_factory(name='u2', rank='regular_user')
+ mod_user = user_factory(rank='mod')
+ session.add_all([user1, user2])
+ session.commit()
+ user_detail_api.delete(context_factory(user=mod_user), 'u1')
+ user_detail_api.delete(context_factory(user=mod_user), 'u2')
+ assert session.query(db.User).all() == []
+
+def test_removing_non_existing(
+ context_factory, config_injector, user_factory, user_detail_api):
+ config_injector({
+ 'privileges': {
+ 'users:delete:self': 'regular_user',
+ 'users:delete:any': 'mod',
+ },
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ with pytest.raises(errors.NotFoundError):
+ user_detail_api.delete(
+ context_factory(user=user_factory(rank='regular_user')), 'bad')
+
diff --git a/server/szurubooru/tests/api/test_user_retrieval.py b/server/szurubooru/tests/api/test_user_retrieval.py
new file mode 100644
index 0000000..6b017dd
--- /dev/null
+++ b/server/szurubooru/tests/api/test_user_retrieval.py
@@ -0,0 +1,116 @@
+import pytest
+from datetime import datetime
+from szurubooru import api, db, errors
+
+@pytest.fixture
+def user_list_api():
+ return api.UserListApi()
+
+@pytest.fixture
+def user_detail_api():
+ return api.UserDetailApi()
+
+def test_retrieving_multiple(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_list_api):
+ config_injector({
+ 'privileges': {'users:list': 'regular_user'},
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ })
+ user1 = user_factory(name='u1', rank='mod')
+ user2 = user_factory(name='u2', rank='mod')
+ session.add_all([user1, user2])
+ result = user_list_api.get(
+ context_factory(
+ params={'query': '', 'page': 1},
+ user=user_factory(rank='regular_user')))
+ assert result['query'] == ''
+ assert result['page'] == 1
+ assert result['pageSize'] == 100
+ assert result['total'] == 2
+ assert [u['name'] for u in result['users']] == ['u1', 'u2']
+
+def test_retrieving_multiple_without_privileges(
+ context_factory, config_injector, user_factory, user_list_api):
+ config_injector({
+ 'privileges': {'users:list': 'regular_user'},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ with pytest.raises(errors.AuthError):
+ user_list_api.get(
+ context_factory(
+ params={'query': '', 'page': 1},
+ user=user_factory(rank='anonymous')))
+
+def test_retrieving_multiple_with_privileges(
+ context_factory, config_injector, user_factory, user_list_api):
+ config_injector({
+ 'privileges': {'users:list': 'regular_user'},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ result = user_list_api.get(
+ context_factory(
+ params={'query': 'asd', 'page': 1},
+ user=user_factory(rank='regular_user')))
+ assert result['query'] == 'asd'
+ assert result['page'] == 1
+ assert result['pageSize'] == 100
+ assert result['total'] == 0
+ assert [u['name'] for u in result['users']] == []
+
+def test_retrieving_single(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'privileges': {'users:view': 'regular_user'},
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ })
+ user = user_factory(name='u1', rank='regular_user')
+ session.add(user)
+ result = user_detail_api.get(
+ context_factory(
+ params={'query': '', 'page': 1},
+ user=user_factory(rank='regular_user')),
+ 'u1')
+ assert result['user']['id'] == user.user_id
+ assert result['user']['name'] == 'u1'
+ assert result['user']['rank'] == 'regular_user'
+ assert result['user']['creationTime'] == datetime(1997, 1, 1)
+ assert result['user']['lastLoginTime'] == None
+ assert result['user']['avatarStyle'] == 'gravatar'
+
+def test_retrieving_non_existing(
+ context_factory, config_injector, user_factory, user_detail_api):
+ config_injector({
+ 'privileges': {'users:view': 'regular_user'},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ with pytest.raises(errors.NotFoundError):
+ user_detail_api.get(
+ context_factory(
+ params={'query': '', 'page': 1},
+ user=user_factory(rank='regular_user')),
+ '-')
+
+def test_retrieving_single_without_privileges(
+ context_factory, config_injector, user_factory, user_detail_api):
+ config_injector({
+ 'privileges': {'users:view': 'regular_user'},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ with pytest.raises(errors.AuthError):
+ user_detail_api.get(
+ context_factory(
+ params={'query': '', 'page': 1},
+ user=user_factory(rank='anonymous')),
+ '-')
diff --git a/server/szurubooru/tests/api/test_user_updating.py b/server/szurubooru/tests/api/test_user_updating.py
new file mode 100644
index 0000000..7920583
--- /dev/null
+++ b/server/szurubooru/tests/api/test_user_updating.py
@@ -0,0 +1,237 @@
+import pytest
+from szurubooru import api, db, errors
+from szurubooru.util import auth
+
+@pytest.fixture
+def user_detail_api():
+ return api.UserDetailApi()
+
+def test_updating_user(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'secret': '',
+ 'user_name_regex': '.{3,}',
+ 'password_regex': '.{3,}',
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ 'privileges': {
+ 'users:edit:self:name': 'regular_user',
+ 'users:edit:self:pass': 'regular_user',
+ 'users:edit:self:email': 'regular_user',
+ 'users:edit:self:rank': 'mod',
+ 'users:edit:self:avatar': 'mod',
+ },
+ })
+ user = user_factory(name='u1', rank='admin')
+ session.add(user)
+ user_detail_api.put(
+ context_factory(
+ request={
+ 'name': 'chewie',
+ 'email': 'asd@asd.asd',
+ 'password': 'oks',
+ 'rank': 'mod',
+ 'avatarStyle': 'gravatar',
+ },
+ user=user),
+ 'u1')
+ user = session.query(db.User).filter_by(name='chewie').one()
+ assert user.name == 'chewie'
+ assert user.email == 'asd@asd.asd'
+ assert user.rank == 'mod'
+ assert user.avatar_style == user.AVATAR_GRAVATAR
+ assert auth.is_valid_password(user, 'oks') is True
+ assert auth.is_valid_password(user, 'invalid') is False
+
+def test_update_changing_nothing(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ })
+ user = user_factory(name='u1', rank='admin')
+ session.add(user)
+ user_detail_api.put(context_factory(user=user), 'u1')
+ user = session.query(db.User).filter_by(name='u1').one()
+ assert user.name == 'u1'
+ assert user.email == 'dummy'
+ assert user.rank == 'admin'
+
+def test_updating_non_existing_user(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ })
+ user = user_factory(name='u1', rank='admin')
+ session.add(user)
+ with pytest.raises(errors.NotFoundError):
+ user_detail_api.put(context_factory(user=user), 'u2')
+
+def test_removing_email(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'thumbnails': {'avatar_width': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ 'privileges': {'users:edit:self:email': 'regular_user'},
+ })
+ user = user_factory(name='u1', rank='admin')
+ session.add(user)
+ user_detail_api.put(
+ context_factory(request={'email': ''}, user=user), 'u1')
+ assert session.query(db.User).filter_by(name='u1').one().email is None
+
+@pytest.mark.parametrize('request', [
+ {'name': '.'},
+ {'password': '.'},
+ {'rank': '.'},
+ {'email': '.'},
+ {'avatarStyle': 'manual'},
+])
+def test_invalid_inputs(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api,
+ request):
+ config_injector({
+ 'user_name_regex': '.{3,}',
+ 'password_regex': '.{3,}',
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'privileges': {
+ 'users:edit:self:name': 'regular_user',
+ 'users:edit:self:pass': 'regular_user',
+ 'users:edit:self:email': 'regular_user',
+ 'users:edit:self:rank': 'mod',
+ 'users:edit:self:avatar': 'mod',
+ },
+ })
+ user = user_factory(name='u1', rank='admin')
+ session.add(user)
+ with pytest.raises(errors.ValidationError):
+ user_detail_api.put(context_factory(request=request, user=user), 'u1')
+
+@pytest.mark.parametrize('request', [
+ {'name': 'whatever'},
+ {'email': 'whatever'},
+ {'rank': 'whatever'},
+ {'password': 'whatever'},
+ {'avatarStyle': 'whatever'},
+])
+def test_user_trying_to_update_someone_else(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api,
+ request):
+ config_injector({
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'privileges': {
+ 'users:edit:any:name': 'mod',
+ 'users:edit:any:pass': 'mod',
+ 'users:edit:any:email': 'mod',
+ 'users:edit:any:rank': 'admin',
+ 'users:edit:any:avatar': 'admin',
+ },
+ })
+ user1 = user_factory(name='u1', rank='regular_user')
+ user2 = user_factory(name='u2', rank='regular_user')
+ session.add_all([user1, user2])
+ with pytest.raises(errors.AuthError):
+ user_detail_api.put(
+ context_factory(request=request, user=user1), user2.name)
+
+def test_user_trying_to_become_someone_else(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'privileges': {'users:edit:self:name': 'regular_user'},
+ })
+ user1 = user_factory(name='me', rank='regular_user')
+ user2 = user_factory(name='her', rank='regular_user')
+ session.add_all([user1, user2])
+ with pytest.raises(errors.IntegrityError):
+ user_detail_api.put(
+ context_factory(request={'name': 'her'}, user=user1),
+ 'me')
+ with pytest.raises(errors.IntegrityError):
+ user_detail_api.put(
+ context_factory(request={'name': 'HER'}, user=user1), 'me')
+
+def test_mods_trying_to_become_admin(
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'privileges': {
+ 'users:edit:self:rank': 'mod',
+ 'users:edit:any:rank': 'admin',
+ },
+ })
+ user1 = user_factory(name='u1', rank='mod')
+ user2 = user_factory(name='u2', rank='mod')
+ session.add_all([user1, user2])
+ context = context_factory(request={'rank': 'admin'}, user=user1)
+ with pytest.raises(errors.AuthError):
+ user_detail_api.put(context, user1.name)
+ with pytest.raises(errors.AuthError):
+ user_detail_api.put(context, user2.name)
+
+def test_uploading_avatar(
+ tmpdir,
+ session,
+ config_injector,
+ context_factory,
+ user_factory,
+ user_detail_api):
+ config_injector({
+ 'data_dir': str(tmpdir.mkdir('data')),
+ 'data_url': 'http://example.com/data/',
+ 'thumbnails': {'avatar_width': 200, 'avatar_height': 200},
+ 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
+ 'rank_names': {},
+ 'privileges': {'users:edit:self:avatar': 'mod'},
+ })
+ user = user_factory(name='u1', rank='mod')
+ session.add(user)
+ empty_pixel = \
+ b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00' \
+ b'\xff\xff\xff\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00' \
+ b'\x01\x00\x01\x00\x00\x02\x02\x4c\x01\x00\x3b'
+ response = user_detail_api.put(
+ context_factory(
+ request={'avatarStyle': 'manual'},
+ files={'avatar': empty_pixel},
+ user=user),
+ 'u1')
+ user = session.query(db.User).filter_by(name='u1').one()
+ assert user.avatar_style == user.AVATAR_MANUAL
+ assert response['user']['avatarUrl'] == \
+ 'http://example.com/data/avatars/u1.jpg'
diff --git a/server/szurubooru/tests/api/util.py b/server/szurubooru/tests/api/util.py
deleted file mode 100644
index 3621b39..0000000
--- a/server/szurubooru/tests/api/util.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from datetime import datetime
-from szurubooru import config, db
-from szurubooru.util import misc
-
-def mock_config(config_mock):
- config.config = config_mock
-
-def mock_user(name, rank='admin'):
- user = db.User()
- user.name = name
- user.password = 'dummy'
- user.password_salt = 'dummy'
- user.password_hash = 'dummy'
- user.email = 'dummy'
- user.rank = rank
- user.creation_time = datetime(1997, 1, 1)
- user.avatar_style = db.User.AVATAR_GRAVATAR
- return user
-
-def mock_context(parent):
- context = misc.dotdict()
- context.session = parent.session
- context.request = {}
- context.files = {}
- context.user = db.User()
- parent.context = context
-
-def mock_params(context, params):
- def get_param_as_string(key, default=None, required=False):
- if key not in params:
- if required:
- raise RuntimeError('Param is missing!')
- return default
- return params[key]
- def get_param_as_int(key, default=None, required=False):
- if key not in params:
- if required:
- raise RuntimeError('Param is missing!')
- return default
- return int(params[key])
- context.get_param_as_string = get_param_as_string
- context.get_param_as_int = get_param_as_int
diff --git a/server/szurubooru/tests/conftest.py b/server/szurubooru/tests/conftest.py
new file mode 100644
index 0000000..7e1851c
--- /dev/null
+++ b/server/szurubooru/tests/conftest.py
@@ -0,0 +1,61 @@
+from datetime import datetime
+import pytest
+import sqlalchemy
+from szurubooru import db, config
+from szurubooru.util import misc
+
+@pytest.fixture
+def session():
+ engine = sqlalchemy.create_engine('sqlite:///:memory:')
+ session_maker = sqlalchemy.orm.sessionmaker(bind=engine)
+ session_instance = sqlalchemy.orm.scoped_session(session_maker)
+ db.Base.query = session_instance.query_property()
+ db.Base.metadata.create_all(bind=engine)
+ return session_instance
+
+@pytest.fixture
+def context_factory(session):
+ def factory(request=None, params=None, files=None, user=None):
+ params = params or {}
+ def get_param_as_string(key, default=None, required=False):
+ if key not in params:
+ if required:
+ raise RuntimeError('Param is missing!')
+ return default
+ return params[key]
+ def get_param_as_int(key, default=None, required=False):
+ if key not in params:
+ if required:
+ raise RuntimeError('Param is missing!')
+ return default
+ return int(params[key])
+ context = misc.dotdict()
+ context.session = session
+ context.request = request or {}
+ context.files = files or {}
+ context.user = user or db.User()
+ context.get_param_as_string = get_param_as_string
+ context.get_param_as_int = get_param_as_int
+ return context
+ return factory
+
+@pytest.fixture
+def config_injector():
+ def injector(new_config_content):
+ config.config = new_config_content
+ return injector
+
+@pytest.fixture
+def user_factory():
+ def factory(name='dummy', rank='regular_user'):
+ user = db.User()
+ user.name = name
+ user.password = 'dummy'
+ user.password_salt = 'dummy'
+ user.password_hash = 'dummy'
+ user.email = 'dummy'
+ user.rank = rank
+ user.creation_time = datetime(1997, 1, 1)
+ user.avatar_style = db.User.AVATAR_GRAVATAR
+ return user
+ return factory
diff --git a/server/szurubooru/tests/database_test_case.py b/server/szurubooru/tests/database_test_case.py
deleted file mode 100644
index e6336d9..0000000
--- a/server/szurubooru/tests/database_test_case.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import unittest
-import sqlalchemy
-from szurubooru import db
-
-class DatabaseTestCase(unittest.TestCase):
- def setUp(self):
- engine = sqlalchemy.create_engine('sqlite:///:memory:')
- session_maker = sqlalchemy.orm.sessionmaker(bind=engine)
- self.session = sqlalchemy.orm.scoped_session(session_maker)
- db.Base.query = self.session.query_property()
- db.Base.metadata.create_all(bind=engine)
diff --git a/server/szurubooru/tests/search/__init__.py b/server/szurubooru/tests/search/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/server/szurubooru/tests/search/__init__.py
+++ /dev/null
diff --git a/server/szurubooru/tests/search/test_user_search_config.py b/server/szurubooru/tests/search/test_user_search_config.py
index c3a9ce8..07c9d00 100644
--- a/server/szurubooru/tests/search/test_user_search_config.py
+++ b/server/szurubooru/tests/search/test_user_search_config.py
@@ -1,192 +1,170 @@
from datetime import datetime
-from szurubooru import errors, search
-from szurubooru.tests.database_test_case import DatabaseTestCase
-from szurubooru.tests.api import util
+import pytest
+from szurubooru import db, errors, search
-class TestUserSearchExecutor(DatabaseTestCase):
- def setUp(self):
- super().setUp()
- self.search_config = search.UserSearchConfig()
- self.executor = search.SearchExecutor(self.search_config)
+def mock_user(name):
+ user = db.User()
+ user.name = name
+ user.password = 'dummy'
+ user.password_salt = 'dummy'
+ user.password_hash = 'dummy'
+ user.email = 'dummy'
+ user.rank = 'dummy'
+ user.creation_time = datetime(1997, 1, 1)
+ user.avatar_style = db.User.AVATAR_GRAVATAR
+ return user
- def _test(self, query, page, page_size, expected_count, expected_user_names):
- count, users = self.executor.execute(self.session, query, page, page_size)
- self.assertEqual(count, expected_count)
- self.assertEqual([u.name for u in users], expected_user_names)
+@pytest.fixture
+def executor(session):
+ search_config = search.UserSearchConfig()
+ return search.SearchExecutor(search_config)
- def _test_raises(self, query, page, page_size):
- self.assertRaises(
- errors.SearchError,
- self.executor.execute,
- self.session,
- query,
- page,
- page_size)
+@pytest.fixture
+def verify_unpaged(session, executor):
+ def verify(input, expected_user_names):
+ actual_count, actual_users = executor.execute(
+ session, input, page=1, page_size=100)
+ actual_user_names = [u.name for u in actual_users]
+ assert actual_count == len(expected_user_names)
+ assert actual_user_names == expected_user_names
+ return verify
- def test_filter_by_creation_time(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2])
- for alias in ['creation-time', 'creation-date']:
- self._test('%s:2014' % alias, 1, 100, 1, ['u1'])
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize('input,expected_user_names', [
+ ('creation-time:2014', ['u1', 'u2']),
+ ('creation-date:2014', ['u1', 'u2']),
+ ('-creation-time:2014', ['u3']),
+ ('-creation-date:2014', ['u3']),
+ ('creation-time:2014..2014-06', ['u1', 'u2']),
+ ('creation-time:2014-06..2015-01-01', ['u2', 'u3']),
+ ('creation-time:2014-06..', ['u2', 'u3']),
+ ('creation-time:..2014-06', ['u1', 'u2']),
+ ('-creation-time:2014..2014-06', ['u3']),
+ ('-creation-time:2014-06..2015-01-01', ['u1']),
+ ('creation-date:2014..2014-06', ['u1', 'u2']),
+ ('creation-date:2014-06..2015-01-01', ['u2', 'u3']),
+ ('creation-date:2014-06..', ['u2', 'u3']),
+ ('creation-date:..2014-06', ['u1', 'u2']),
+ ('-creation-date:2014..2014-06', ['u3']),
+ ('-creation-date:2014-06..2015-01-01', ['u1']),
+ ('creation-time:2014-01,2015', ['u1', 'u3']),
+ ('creation-date:2014-01,2015', ['u1', 'u3']),
+ ('-creation-time:2014-01,2015', ['u2']),
+ ('-creation-date:2014-01,2015', ['u2']),
+])
+def test_filter_by_creation_time(
+ verify_unpaged, session, input, expected_user_names):
+ user1 = mock_user('u1')
+ user2 = mock_user('u2')
+ user3 = mock_user('u3')
+ user1.creation_time = datetime(2014, 1, 1)
+ user2.creation_time = datetime(2014, 6, 1)
+ user3.creation_time = datetime(2015, 1, 1)
+ session.add_all([user1, user2, user3])
+ verify_unpaged(input, expected_user_names)
- def test_filter_by_negated_creation_time(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2])
- for alias in ['creation-time', 'creation-date']:
- self._test('-%s:2014' % alias, 1, 100, 1, ['u2'])
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize('input,expected_user_names', [
+ ('name:user1', ['user1']),
+ ('name:user2', ['user2']),
+ ('name:none', []),
+ ('name:', []),
+ ('name:*1', ['user1']),
+ ('name:*2', ['user2']),
+ ('name:*', ['user1', 'user2', 'user3']),
+ ('name:u*', ['user1', 'user2', 'user3']),
+ ('name:*ser*', ['user1', 'user2', 'user3']),
+ ('name:*zer*', []),
+ ('name:zer*', []),
+ ('name:*zer', []),
+ ('-name:user1', ['user2', 'user3']),
+ ('-name:user2', ['user1', 'user3']),
+ ('name:user1,user2', ['user1', 'user2']),
+ ('-name:user1,user3', ['user2']),
+])
+def test_filter_by_name(session, verify_unpaged, input, expected_user_names):
+ session.add(mock_user('user1'))
+ session.add(mock_user('user2'))
+ session.add(mock_user('user3'))
+ verify_unpaged(input, expected_user_names)
- def test_filter_by_ranged_creation_time(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user3 = util.mock_user('u3')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2014, 6, 1)
- user3.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2, user3])
- for alias in ['creation-time', 'creation-date']:
- self._test('%s:2014..2014-06' % alias, 1, 100, 2, ['u1', 'u2'])
- self._test('%s:2014-06..2015-01-01' % alias, 1, 100, 2, ['u2', 'u3'])
- self._test('%s:2014-06..' % alias, 1, 100, 2, ['u2', 'u3'])
- self._test('%s:..2014-06' % alias, 1, 100, 2, ['u1', 'u2'])
- self._test_raises('%s:..' % alias, 1, 100)
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize('input,expected_user_names', [
+ ('', ['u1', 'u2']),
+ ('u1', ['u1']),
+ ('u2', ['u2']),
+ ('u1,u2', ['u1', 'u2']),
+])
+def test_anonymous(session, verify_unpaged, input, expected_user_names):
+ session.add(mock_user('u1'))
+ session.add(mock_user('u2'))
+ verify_unpaged(input, expected_user_names)
- def test_filter_by_negated_ranged_creation_time(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user3 = util.mock_user('u3')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2014, 6, 1)
- user3.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2, user3])
- for alias in ['creation-time', 'creation-date']:
- self._test('-%s:2014..2014-06' % alias, 1, 100, 1, ['u3'])
- self._test('-%s:2014-06..2015-01-01' % alias, 1, 100, 1, ['u1'])
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize('input,expected_user_names', [
+ ('creation-time:2014 u1', ['u1']),
+ ('creation-time:2014 u2', ['u2']),
+ ('creation-time:2016 u2', []),
+])
+def test_combining_tokens(session, verify_unpaged, input, expected_user_names):
+ user1 = mock_user('u1')
+ user2 = mock_user('u2')
+ user3 = mock_user('u3')
+ user1.creation_time = datetime(2014, 1, 1)
+ user2.creation_time = datetime(2014, 6, 1)
+ user3.creation_time = datetime(2015, 1, 1)
+ session.add_all([user1, user2, user3])
+ verify_unpaged(input, expected_user_names)
- def test_filter_by_composite_creation_time(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user3 = util.mock_user('u3')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2014, 6, 1)
- user3.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2, user3])
- for alias in ['creation-time', 'creation-date']:
- self._test('%s:2014-01,2015' % alias, 1, 100, 2, ['u1', 'u3'])
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize(
+ 'page,page_size,expected_total_count,expected_user_names', [
+ (1, 1, 2, ['u1']),
+ (2, 1, 2, ['u2']),
+ (3, 1, 2, []),
+ (0, 1, 2, ['u1']),
+ (0, 0, 2, ['u1']),
+ ])
+def test_paging(
+ session, executor, page, page_size,
+ expected_total_count, expected_user_names):
+ session.add(mock_user('u1'))
+ session.add(mock_user('u2'))
+ actual_count, actual_users = executor.execute(
+ session, '', page=page, page_size=page_size)
+ actual_user_names = [u.name for u in actual_users]
+ assert actual_count == expected_total_count
+ assert actual_user_names == expected_user_names
- def test_filter_by_negated_composite_creation_time(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user3 = util.mock_user('u3')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2014, 6, 1)
- user3.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2, user3])
- for alias in ['creation-time', 'creation-date']:
- self._test('-%s:2014-01,2015' % alias, 1, 100, 1, ['u2'])
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize('input,expected_user_names', [
+ ('', ['u1', 'u2']),
+ ('order:name', ['u1', 'u2']),
+ ('-order:name', ['u2', 'u1']),
+ ('order:name,asc', ['u1', 'u2']),
+ ('order:name,desc', ['u2', 'u1']),
+ ('-order:name,asc', ['u2', 'u1']),
+ ('-order:name,desc', ['u1', 'u2']),
+])
+def test_order_by_name(session, verify_unpaged, input, expected_user_names):
+ session.add(mock_user('u2'))
+ session.add(mock_user('u1'))
+ verify_unpaged(input, expected_user_names)
- def test_filter_by_name(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self._test('name:u1', 1, 100, 1, ['u1'])
- self._test('name:u2', 1, 100, 1, ['u2'])
-
- def test_filter_by_name_wildcards(self):
- self.session.add(util.mock_user('user1'))
- self.session.add(util.mock_user('user2'))
- self._test('name:*1', 1, 100, 1, ['user1'])
- self._test('name:*2', 1, 100, 1, ['user2'])
- self._test('name:*', 1, 100, 2, ['user1', 'user2'])
- self._test('name:u*', 1, 100, 2, ['user1', 'user2'])
- self._test('name:*ser*', 1, 100, 2, ['user1', 'user2'])
- self._test('name:*zer*', 1, 100, 0, [])
- self._test('name:zer*', 1, 100, 0, [])
- self._test('name:*zer', 1, 100, 0, [])
-
- def test_filter_by_negated_name(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self._test('-name:u1', 1, 100, 1, ['u2'])
- self._test('-name:u2', 1, 100, 1, ['u1'])
-
- def test_filter_by_composite_name(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self.session.add(util.mock_user('u3'))
- self._test('name:u1,u2', 1, 100, 2, ['u1', 'u2'])
-
- def test_filter_by_negated_composite_name(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self.session.add(util.mock_user('u3'))
- self._test('-name:u1,u3', 1, 100, 1, ['u2'])
-
- def test_filter_by_ranged_name(self):
- self._test_raises('name:u1..u2', 1, 100)
-
- def test_paging(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self._test('', 1, 1, 2, ['u1'])
- self._test('', 2, 1, 2, ['u2'])
- self._test('', 3, 1, 2, [])
- self._test('', 0, 1, 2, ['u1'])
- self._test('', 0, 0, 2, ['u1'])
-
- def test_order_by_name(self):
- self.session.add(util.mock_user('u2'))
- self.session.add(util.mock_user('u1'))
- self._test('', 1, 100, 2, ['u1', 'u2'])
- self._test('order:name', 1, 100, 2, ['u1', 'u2'])
- self._test('-order:name', 1, 100, 2, ['u2', 'u1'])
- self._test('order:name,asc', 1, 100, 2, ['u1', 'u2'])
- self._test('order:name,desc', 1, 100, 2, ['u2', 'u1'])
- self._test('-order:name,asc', 1, 100, 2, ['u2', 'u1'])
- self._test('-order:name,desc', 1, 100, 2, ['u1', 'u2'])
-
- def test_invalid_tokens(self):
- for query in [
- 'order:',
- 'order:nam',
- 'order:name,as',
- 'order:name,asc,desc',
- 'bad:x',
- 'special:unsupported']:
- self._test_raises(query, 1, 100)
-
- def test_anonymous(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self._test('u1', 1, 100, 1, ['u1'])
- self._test('u2', 1, 100, 1, ['u2'])
-
- def test_empty_search(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self._test('', 1, 100, 2, ['u1', 'u2'])
-
- def test_negated_anonymous(self):
- self.session.add(util.mock_user('u1'))
- self.session.add(util.mock_user('u2'))
- self._test('-u1', 1, 100, 1, ['u2'])
- self._test('-u2', 1, 100, 1, ['u1'])
-
- def test_combining(self):
- user1 = util.mock_user('u1')
- user2 = util.mock_user('u2')
- user3 = util.mock_user('u3')
- user1.creation_time = datetime(2014, 1, 1)
- user2.creation_time = datetime(2014, 6, 1)
- user3.creation_time = datetime(2015, 1, 1)
- self.session.add_all([user1, user2, user3])
- self._test('creation-time:2014 u1', 1, 100, 1, ['u1'])
- self._test('creation-time:2014 u2', 1, 100, 1, ['u2'])
- self._test('creation-time:2016 u2', 1, 100, 0, [])
-
- def test_special(self):
- self._test_raises('special:-', 1, 100)
+# -----------------------------------------------------------------------------
+@pytest.mark.parametrize('input,expected_error', [
+ ('creation-date:..', errors.SearchError),
+ ('creation-date:bad..', errors.ValidationError),
+ ('creation-date:..bad', errors.ValidationError),
+ ('creation-date:bad..bad', errors.ValidationError),
+ ('name:a..b', errors.SearchError),
+ ('order:', errors.SearchError),
+ ('order:nam', errors.SearchError),
+ ('order:name,as', errors.SearchError),
+ ('order:name,asc,desc', errors.SearchError),
+ ('bad:x', errors.SearchError),
+ ('special:unsupported', errors.SearchError),
+])
+def test_bad_tokens(executor, session, input, expected_error):
+ with pytest.raises(expected_error):
+ executor.execute(session, input, page=1, page_size=100)
diff --git a/server/szurubooru/tests/util/test_misc.py b/server/szurubooru/tests/util/test_misc.py
index 171d483..01c667b 100644
--- a/server/szurubooru/tests/util/test_misc.py
+++ b/server/szurubooru/tests/util/test_misc.py
@@ -1,42 +1,30 @@
-import unittest
+import pytest
from datetime import datetime
from szurubooru import errors
from szurubooru.util import misc
+dt = datetime
+
class FakeDatetime(datetime):
@staticmethod
def now(tz=None):
return datetime(1997, 1, 2, 3, 4, 5, tzinfo=tz)
-class TestParseTime(unittest.TestCase):
- def test_empty(self):
- self.assertRaises(errors.ValidationError, misc.parse_time_range, '')
-
- def test_today(self):
- misc.datetime.datetime = FakeDatetime
- date_min, date_max = misc.parse_time_range('today')
- self.assertEqual(date_min, datetime(1997, 1, 2, 0, 0, 0))
- self.assertEqual(date_max, datetime(1997, 1, 2, 23, 59, 59))
-
- def test_yesterday(self):
- misc.datetime.datetime = FakeDatetime
- date_min, date_max = misc.parse_time_range('yesterday')
- self.assertEqual(date_min, datetime(1997, 1, 1, 0, 0, 0))
- self.assertEqual(date_max, datetime(1997, 1, 1, 23, 59, 59))
-
- def test_year(self):
- date_min, date_max = misc.parse_time_range('1999')
- self.assertEqual(date_min, datetime(1999, 1, 1, 0, 0, 0))
- self.assertEqual(date_max, datetime(1999, 12, 31, 23, 59, 59))
-
- def test_month(self):
- for text in ['1999-2', '1999-02']:
- date_min, date_max = misc.parse_time_range(text)
- self.assertEqual(date_min, datetime(1999, 2, 1, 0, 0, 0))
- self.assertEqual(date_max, datetime(1999, 2, 28, 23, 59, 59))
+def test_parsing_empty_date_time():
+ with pytest.raises(errors.ValidationError):
+ misc.parse_time_range('')
- def test_day(self):
- for text in ['1999-2-6', '1999-02-6', '1999-2-06', '1999-02-06']:
- date_min, date_max = misc.parse_time_range(text)
- self.assertEqual(date_min, datetime(1999, 2, 6, 0, 0, 0))
- self.assertEqual(date_max, datetime(1999, 2, 6, 23, 59, 59))
+@pytest.mark.parametrize('input,output', [
+ ('today', (dt(1997, 1, 2, 0, 0, 0), dt(1997, 1, 2, 23, 59, 59))),
+ ('yesterday', (dt(1997, 1, 1, 0, 0, 0), dt(1997, 1, 1, 23, 59, 59))),
+ ('1999', (dt(1999, 1, 1, 0, 0, 0), dt(1999, 12, 31, 23, 59, 59))),
+ ('1999-2', (dt(1999, 2, 1, 0, 0, 0), dt(1999, 2, 28, 23, 59, 59))),
+ ('1999-02', (dt(1999, 2, 1, 0, 0, 0), dt(1999, 2, 28, 23, 59, 59))),
+ ('1999-2-6', (dt(1999, 2, 6, 0, 0, 0), dt(1999, 2, 6, 23, 59, 59))),
+ ('1999-02-6', (dt(1999, 2, 6, 0, 0, 0), dt(1999, 2, 6, 23, 59, 59))),
+ ('1999-2-06', (dt(1999, 2, 6, 0, 0, 0), dt(1999, 2, 6, 23, 59, 59))),
+ ('1999-02-06', (dt(1999, 2, 6, 0, 0, 0), dt(1999, 2, 6, 23, 59, 59))),
+])
+def test_parsing_date_time(input, output):
+ misc.datetime.datetime = FakeDatetime
+ assert misc.parse_time_range(input) == output
diff --git a/server/test b/server/test
new file mode 100755
index 0000000..c688087
--- /dev/null
+++ b/server/test
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+import pytest
+import sys
+pytest.main(' '.join([
+ '--cov-report=term-missing',
+ '--cov=szurubooru',
+ 'szurubooru'] + sys.argv[1:]))