From 87b1ee45640386282f0f70053d0f0155901b49e2 Mon Sep 17 00:00:00 2001 From: rr- Date: Mon, 15 Aug 2016 17:53:01 +0200 Subject: [PATCH] server/tests: use real database I'm experimenting with snapshots and found following limitation of SQLite: https://www.sqlite.org/isolation.html --- config.yaml.dist | 9 ++ server/szurubooru/tests/conftest.py | 44 ++++--- server/szurubooru/tests/db/test_user.py | 4 +- server/szurubooru/tests/func/test_comments.py | 10 +- server/szurubooru/tests/func/test_posts.py | 14 ++- server/szurubooru/tests/func/test_tags.py | 114 ++++++++++-------- 6 files changed, 117 insertions(+), 78 deletions(-) diff --git a/config.yaml.dist b/config.yaml.dist index b0b33ec5..bcf98469 100644 --- a/config.yaml.dist +++ b/config.yaml.dist @@ -25,6 +25,15 @@ database: pass: # example: dog name: # example: szuru +# required for runing the test suite +test_database: + schema: postgres + host: # example: localhost + port: # example: 5432 + user: # example: szuru + pass: # example: dog + name: # example: szuru_test + # used to send password reminders smtp: host: # example: localhost diff --git a/server/szurubooru/tests/conftest.py b/server/szurubooru/tests/conftest.py index 99db52eb..aca4682c 100644 --- a/server/szurubooru/tests/conftest.py +++ b/server/szurubooru/tests/conftest.py @@ -1,8 +1,9 @@ # pylint: disable=redefined-outer-name import contextlib import os -import datetime -import uuid +import random +import string +from datetime import datetime import pytest import freezegun import sqlalchemy @@ -30,8 +31,19 @@ class QueryCounter(object): return self._statements +if not config.config['test_database']['host']: + raise RuntimeError('Test database not configured.') + _query_counter = QueryCounter() -_engine = sqlalchemy.create_engine('sqlite:///:memory:') +_engine = sqlalchemy.create_engine( + '{schema}://{user}:{password}@{host}:{port}/{name}'.format( + schema=config.config['test_database']['schema'], + user=config.config['test_database']['user'], + password=config.config['test_database']['pass'], + host=config.config['test_database']['host'], + port=config.config['test_database']['port'], + name=config.config['test_database']['name'])) +db.Base.metadata.drop_all(bind=_engine) db.Base.metadata.create_all(bind=_engine) sqlalchemy.event.listen( _engine, @@ -40,7 +52,8 @@ sqlalchemy.event.listen( def get_unique_name(): - return str(uuid.uuid4()) + alphabet = string.ascii_letters + string.digits + return ''.join(random.choice(alphabet) for _ in range(8)) @pytest.fixture @@ -72,16 +85,15 @@ def query_logger(): @pytest.yield_fixture(scope='function', autouse=True) def session(query_logger): # pylint: disable=unused-argument - session_maker = sqlalchemy.orm.sessionmaker(bind=_engine) - session = sqlalchemy.orm.scoped_session(session_maker) - db.session = session + db.sessionmaker = sqlalchemy.orm.sessionmaker(bind=_engine) + db.session = sqlalchemy.orm.scoped_session(db.sessionmaker) try: - yield session + yield db.session finally: - session.remove() + db.session.remove() for table in reversed(db.Base.metadata.sorted_tables): - session.execute(table.delete()) - session.commit() + db.session.execute(table.delete()) + db.session.commit() @pytest.fixture @@ -115,7 +127,7 @@ def user_factory(): user.password_hash = 'dummy' user.email = email user.rank = rank - user.creation_time = datetime.datetime(1997, 1, 1) + user.creation_time = datetime(1997, 1, 1) user.avatar_style = db.User.AVATAR_GRAVATAR return user return factory @@ -142,7 +154,7 @@ def tag_factory(): tag.names = [ db.TagName(name) for name in names or [get_unique_name()]] tag.category = category - tag.creation_time = datetime.datetime(1996, 1, 1) + tag.creation_time = datetime(1996, 1, 1) return tag return factory @@ -162,14 +174,14 @@ def post_factory(): post.checksum = checksum post.flags = [] post.mime_type = 'application/octet-stream' - post.creation_time = datetime.datetime(1996, 1, 1) + post.creation_time = datetime(1996, 1, 1) return post return factory @pytest.fixture def comment_factory(user_factory, post_factory): - def factory(user=None, post=None, text='dummy'): + def factory(user=None, post=None, text='dummy', time=None): if not user: user = user_factory() db.session.add(user) @@ -180,7 +192,7 @@ def comment_factory(user_factory, post_factory): comment.user = user comment.post = post comment.text = text - comment.creation_time = datetime.datetime(1996, 1, 1) + comment.creation_time = time or datetime(1996, 1, 1) return comment return factory diff --git a/server/szurubooru/tests/db/test_user.py b/server/szurubooru/tests/db/test_user.py index c7c925d2..e63ad619 100644 --- a/server/szurubooru/tests/db/test_user.py +++ b/server/szurubooru/tests/db/test_user.py @@ -145,12 +145,12 @@ def test_cascade_deletions(post_factory, user_factory, comment_factory): snapshot.user = user snapshot.creation_time = datetime(1997, 1, 1) snapshot.resource_type = '-' - snapshot.resource_id = '-' + snapshot.resource_id = 1 snapshot.resource_repr = '-' snapshot.operation = '-' db.session.add_all([user, post, comment, snapshot]) - db.session.flush() + db.session.commit() assert not db.session.dirty assert post.user is not None and post.user.user_id is not None diff --git a/server/szurubooru/tests/func/test_comments.py b/server/szurubooru/tests/func/test_comments.py index 0cdeca72..c3c2fde1 100644 --- a/server/szurubooru/tests/func/test_comments.py +++ b/server/szurubooru/tests/func/test_comments.py @@ -35,8 +35,9 @@ def test_serialize_user(user_factory, comment_factory): def test_try_get_comment(comment_factory): comment = comment_factory() db.session.add(comment) - assert comments.try_get_comment_by_id(999) is None - assert comments.try_get_comment_by_id(1) is comment + db.session.flush() + assert comments.try_get_comment_by_id(comment.comment_id + 1) is None + assert comments.try_get_comment_by_id(comment.comment_id) is comment with pytest.raises(comments.InvalidCommentIdError): comments.try_get_comment_by_id('-') @@ -44,9 +45,10 @@ def test_try_get_comment(comment_factory): def test_get_comment(comment_factory): comment = comment_factory() db.session.add(comment) + db.session.flush() with pytest.raises(comments.CommentNotFoundError): - comments.get_comment_by_id(999) - assert comments.get_comment_by_id(1) is comment + comments.get_comment_by_id(comment.comment_id + 1) + assert comments.get_comment_by_id(comment.comment_id) is comment with pytest.raises(comments.InvalidCommentIdError): comments.get_comment_by_id('-') diff --git a/server/szurubooru/tests/func/test_posts.py b/server/szurubooru/tests/func/test_posts.py index accf75e7..a2b91693 100644 --- a/server/szurubooru/tests/func/test_posts.py +++ b/server/szurubooru/tests/func/test_posts.py @@ -110,8 +110,14 @@ def test_serialize_post( db.session.flush() db.session.add_all([ - comment_factory(user=user_factory(name='commenter1'), post=post), - comment_factory(user=user_factory(name='commenter2'), post=post), + comment_factory( + user=user_factory(name='commenter1'), + post=post, + time=datetime(1999, 1, 1)), + comment_factory( + user=user_factory(name='commenter2'), + post=post, + time=datetime(1999, 1, 2)), db.PostFavorite( post=post, user=user_factory(name='fav1'), @@ -458,8 +464,8 @@ def test_update_post_relations(post_factory): post = post_factory() posts.update_post_relations(post, [relation1.post_id, relation2.post_id]) assert len(post.relations) == 2 - assert post.relations[0].post_id == relation1.post_id - assert post.relations[1].post_id == relation2.post_id + assert sorted(r.post_id for r in post.relations) == [ + relation1.post_id, relation2.post_id] def test_update_post_relations_bidirectionality(post_factory): diff --git a/server/szurubooru/tests/func/test_tags.py b/server/szurubooru/tests/func/test_tags.py index 061639c7..dfaa2f4f 100644 --- a/server/szurubooru/tests/func/test_tags.py +++ b/server/szurubooru/tests/func/test_tags.py @@ -110,7 +110,12 @@ def test_export_to_json( export_path = os.path.join(str(tmpdir), 'tags.json') assert os.path.exists(export_path) with open(export_path, 'r') as handle: - assert json.loads(handle.read()) == { + actual_json = json.loads(handle.read()) + assert actual_json['tags'] + assert actual_json['categories'] + actual_json['tags'].sort(key=lambda tag: tag['names'][0]) + actual_json['categories'].sort(key=lambda category: category['name']) + assert actual_json == { 'tags': [ { 'names': ['alias1', 'alias2'], @@ -119,14 +124,14 @@ def test_export_to_json( 'suggestions': ['sug1', 'sug2'], 'implications': ['imp1', 'imp2'], }, - {'names': ['sug1'], 'usages': 0, 'category': 'cat1'}, - {'names': ['sug2'], 'usages': 0, 'category': 'cat1'}, {'names': ['imp1'], 'usages': 0, 'category': 'cat1'}, {'names': ['imp2'], 'usages': 0, 'category': 'cat1'}, + {'names': ['sug1'], 'usages': 0, 'category': 'cat1'}, + {'names': ['sug2'], 'usages': 0, 'category': 'cat1'}, ], 'categories': [ - {'name': 'cat2', 'color': 'white'}, {'name': 'cat1', 'color': 'black'}, + {'name': 'cat2', 'color': 'white'}, ] } @@ -164,76 +169,81 @@ def test_get_tag_by_name(name_to_search, expected_to_find, tag_factory): tags.get_tag_by_name(name_to_search) -@pytest.mark.parametrize('names,expected_ids', [ +@pytest.mark.parametrize('names,expected_indexes', [ ([], []), - (['name1'], [1]), - (['NAME1'], [1]), - (['alias1'], [1]), - (['ALIAS1'], [1]), - (['name2'], [2]), - (['name1', 'name1'], [1]), - (['name1', 'NAME1'], [1]), - (['name1', 'alias1'], [1]), - (['name1', 'alias2'], [1, 2]), - (['NAME1', 'alias2'], [1, 2]), - (['name1', 'ALIAS2'], [1, 2]), - (['name2', 'alias1'], [1, 2]), + (['name1'], [0]), + (['NAME1'], [0]), + (['alias1'], [0]), + (['ALIAS1'], [0]), + (['name2'], [1]), + (['name1', 'name1'], [0]), + (['name1', 'NAME1'], [0]), + (['name1', 'alias1'], [0]), + (['name1', 'alias2'], [0, 1]), + (['NAME1', 'alias2'], [0, 1]), + (['name1', 'ALIAS2'], [0, 1]), + (['name2', 'alias1'], [0, 1]), ]) -def test_get_tag_by_names(names, expected_ids, tag_factory): - tag1 = tag_factory(names=['name1', 'ALIAS1']) - tag2 = tag_factory(names=['name2', 'ALIAS2']) - tag1.tag_id = 1 - tag2.tag_id = 2 - db.session.add_all([tag1, tag2]) +def test_get_tag_by_names(names, expected_indexes, tag_factory): + input_tags = [ + tag_factory(names=['name1', 'ALIAS1']), + tag_factory(names=['name2', 'ALIAS2']), + ] + db.session.add_all(input_tags) + db.session.flush() + expected_ids = [input_tags[i].tag_id for i in expected_indexes] actual_ids = [tag.tag_id for tag in tags.get_tags_by_names(names)] assert actual_ids == expected_ids @pytest.mark.parametrize( - 'names,expected_ids,expected_created_names', [ + 'names,expected_indexes,expected_created_names', [ ([], [], []), - (['name1'], [1], []), - (['NAME1'], [1], []), - (['alias1'], [1], []), - (['ALIAS1'], [1], []), - (['name2'], [2], []), - (['name1', 'name1'], [1], []), - (['name1', 'NAME1'], [1], []), - (['name1', 'alias1'], [1], []), - (['name1', 'alias2'], [1, 2], []), - (['NAME1', 'alias2'], [1, 2], []), - (['name1', 'ALIAS2'], [1, 2], []), - (['name2', 'alias1'], [1, 2], []), + (['name1'], [0], []), + (['NAME1'], [0], []), + (['alias1'], [0], []), + (['ALIAS1'], [0], []), + (['name2'], [1], []), + (['name1', 'name1'], [0], []), + (['name1', 'NAME1'], [0], []), + (['name1', 'alias1'], [0], []), + (['name1', 'alias2'], [0, 1], []), + (['NAME1', 'alias2'], [0, 1], []), + (['name1', 'ALIAS2'], [0, 1], []), + (['name2', 'alias1'], [0, 1], []), (['new'], [], ['new']), - (['new', 'name1'], [1], ['new']), - (['new', 'NAME1'], [1], ['new']), - (['new', 'alias1'], [1], ['new']), - (['new', 'ALIAS1'], [1], ['new']), - (['new', 'name2'], [2], ['new']), - (['new', 'name1', 'name1'], [1], ['new']), - (['new', 'name1', 'NAME1'], [1], ['new']), - (['new', 'name1', 'alias1'], [1], ['new']), - (['new', 'name1', 'alias2'], [1, 2], ['new']), - (['new', 'NAME1', 'alias2'], [1, 2], ['new']), - (['new', 'name1', 'ALIAS2'], [1, 2], ['new']), - (['new', 'name2', 'alias1'], [1, 2], ['new']), + (['new', 'name1'], [0], ['new']), + (['new', 'NAME1'], [0], ['new']), + (['new', 'alias1'], [0], ['new']), + (['new', 'ALIAS1'], [0], ['new']), + (['new', 'name2'], [1], ['new']), + (['new', 'name1', 'name1'], [0], ['new']), + (['new', 'name1', 'NAME1'], [0], ['new']), + (['new', 'name1', 'alias1'], [0], ['new']), + (['new', 'name1', 'alias2'], [0, 1], ['new']), + (['new', 'NAME1', 'alias2'], [0, 1], ['new']), + (['new', 'name1', 'ALIAS2'], [0, 1], ['new']), + (['new', 'name2', 'alias1'], [0, 1], ['new']), (['new', 'new'], [], ['new']), (['new', 'NEW'], [], ['new']), (['new', 'new2'], [], ['new', 'new2']), ]) def test_get_or_create_tags_by_names( names, - expected_ids, + expected_indexes, expected_created_names, tag_factory, tag_category_factory, config_injector): config_injector({'tag_name_regex': '.*'}) category = tag_category_factory() - tag1 = tag_factory(names=['name1', 'ALIAS1'], category=category) - tag2 = tag_factory(names=['name2', 'ALIAS2'], category=category) - db.session.add_all([tag1, tag2]) + input_tags = [ + tag_factory(names=['name1', 'ALIAS1'], category=category), + tag_factory(names=['name2', 'ALIAS2'], category=category), + ] + db.session.add_all(input_tags) result = tags.get_or_create_tags_by_names(names) + expected_ids = [input_tags[i].tag_id for i in expected_indexes] actual_ids = [tag.tag_id for tag in result[0]] actual_created_names = [tag.names[0].name for tag in result[1]] assert actual_ids == expected_ids