mirror of https://gitlab.com/tildes/tildes.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
6.6 KiB
218 lines
6.6 KiB
from http.cookiejar import CookieJar
|
|
import os
|
|
|
|
from pyramid import testing
|
|
from pyramid.paster import get_app, get_appsettings
|
|
from pytest import fixture
|
|
from redis import StrictRedis
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.engine.url import make_url
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy.orm.session import Session
|
|
from testing.redis import RedisServer # noqa
|
|
from webtest import TestApp
|
|
|
|
from scripts.initialize_db import create_tables
|
|
from tildes.models.group import Group
|
|
from tildes.models.user import User
|
|
|
|
|
|
class NestedSessionWrapper(Session):
|
|
"""Wrapper that starts a new nested transaction on commit/rollback."""
|
|
|
|
def commit(self):
|
|
"""Commit the transaction, then start a new nested one."""
|
|
super().commit()
|
|
self.begin_nested()
|
|
|
|
def rollback(self):
|
|
"""Rollback the transaction, then start a new nested one."""
|
|
super().rollback()
|
|
self.begin_nested()
|
|
|
|
def rollback_all_nested(self):
|
|
"""Rollback all nested transactions to return to "top-level"."""
|
|
while self.transaction.parent:
|
|
super().rollback()
|
|
|
|
|
|
@fixture(scope='session', autouse=True)
|
|
def pyramid_config():
|
|
"""Set up the Pyramid environment."""
|
|
settings = get_appsettings('development.ini')
|
|
config = testing.setUp(settings=settings)
|
|
config.include('tildes.auth')
|
|
|
|
yield config
|
|
|
|
testing.tearDown()
|
|
|
|
|
|
@fixture(scope='session', autouse=True)
|
|
def overall_db_session(pyramid_config):
|
|
"""Handle setup and teardown of test database for testing session."""
|
|
# read the database url from the pyramid INI file, and replace the db name
|
|
sqlalchemy_url = pyramid_config.registry.settings['sqlalchemy.url']
|
|
parsed_url = make_url(sqlalchemy_url)
|
|
parsed_url.database = 'tildes_test'
|
|
|
|
engine = create_engine(parsed_url)
|
|
session_factory = sessionmaker(bind=engine)
|
|
session = session_factory()
|
|
|
|
create_tables(session.connection())
|
|
|
|
# SQL init scripts need to be executed "manually" instead of using psql
|
|
# like the normal database init process does, since the tables only exist
|
|
# inside this transaction
|
|
init_scripts_dir = 'sql/init/'
|
|
for root, _, files in os.walk(init_scripts_dir):
|
|
sql_files = [
|
|
filename for filename in files
|
|
if filename.endswith('.sql')
|
|
]
|
|
for sql_file in sql_files:
|
|
with open(os.path.join(root, sql_file)) as current_file:
|
|
session.execute(current_file.read())
|
|
|
|
# convert the Session to the wrapper class to enforce staying inside
|
|
# nested transactions in the test functions
|
|
session.__class__ = NestedSessionWrapper
|
|
|
|
yield session
|
|
|
|
# "Teardown" code at the end of testing session
|
|
session.__class__ = Session
|
|
session.rollback()
|
|
|
|
|
|
@fixture(scope='session')
|
|
def sdb(overall_db_session):
|
|
"""Testing-session-level db session with a nested transaction."""
|
|
overall_db_session.begin_nested()
|
|
|
|
yield overall_db_session
|
|
|
|
overall_db_session.rollback_all_nested()
|
|
|
|
|
|
@fixture(scope='function')
|
|
def db(overall_db_session):
|
|
"""Function-level db session with a nested transaction."""
|
|
overall_db_session.begin_nested()
|
|
|
|
yield overall_db_session
|
|
|
|
overall_db_session.rollback_all_nested()
|
|
|
|
|
|
@fixture(scope='session', autouse=True)
|
|
def overall_redis_session():
|
|
"""Create a session-level connection to a temporary redis server."""
|
|
# list of redis modules that need to be loaded (would be much nicer to do
|
|
# this automatically somehow, maybe reading from the real redis.conf?)
|
|
redis_modules = [
|
|
'/opt/redis-cell/libredis_cell.so',
|
|
]
|
|
|
|
with RedisServer() as temp_redis_server:
|
|
redis = StrictRedis(**temp_redis_server.dsn())
|
|
|
|
for module in redis_modules:
|
|
redis.execute_command('MODULE LOAD', module)
|
|
|
|
yield redis
|
|
|
|
|
|
@fixture(scope='function')
|
|
def redis(overall_redis_session):
|
|
"""Create a function-level redis connection that wipes the db after use."""
|
|
yield overall_redis_session
|
|
|
|
overall_redis_session.flushdb()
|
|
|
|
|
|
@fixture(scope='session', autouse=True)
|
|
def session_user(sdb):
|
|
"""Create a user named 'SessionUser' in the db for test session."""
|
|
# note that some tests may depend on this username/password having these
|
|
# specific values, so make sure to search for and update those tests if you
|
|
# change the username or password for any reason
|
|
user = User('SessionUser', 'session user password')
|
|
sdb.add(user)
|
|
sdb.commit()
|
|
|
|
yield user
|
|
|
|
|
|
@fixture(scope='session', autouse=True)
|
|
def session_user2(sdb):
|
|
"""Create a second user named 'OtherUser' in the db for test session.
|
|
|
|
This is useful for cases where two different users are needed, such as
|
|
when testing private messages.
|
|
"""
|
|
user = User('OtherUser', 'other user password')
|
|
sdb.add(user)
|
|
sdb.commit()
|
|
|
|
yield user
|
|
|
|
|
|
@fixture(scope='session', autouse=True)
|
|
def session_group(sdb):
|
|
"""Create a group named 'sessiongroup' in the db for test session."""
|
|
group = Group('sessiongroup')
|
|
sdb.add(group)
|
|
sdb.commit()
|
|
|
|
yield group
|
|
|
|
|
|
@fixture(scope='session')
|
|
def base_app(overall_redis_session, sdb):
|
|
"""Configure a base WSGI app that webtest can create TestApps based on."""
|
|
testing_app = get_app('development.ini')
|
|
|
|
# replace the redis connection used by the redis-sessions library with a
|
|
# connection to the temporary server for this test session
|
|
testing_app.app.registry._redis_sessions = overall_redis_session
|
|
|
|
def redis_factory(request):
|
|
# pylint: disable=unused-argument
|
|
return overall_redis_session
|
|
testing_app.app.registry['redis_connection_factory'] = redis_factory
|
|
|
|
# replace the session factory function with one that will return the
|
|
# testing db session (inside a nested transaction)
|
|
def session_factory():
|
|
return sdb
|
|
testing_app.app.registry['db_session_factory'] = session_factory
|
|
|
|
yield testing_app
|
|
|
|
|
|
@fixture(scope='session')
|
|
def webtest(base_app):
|
|
"""Create a webtest TestApp and log in as the SessionUser account in it."""
|
|
# create the TestApp - note that specifying wsgi.url_scheme is necessary
|
|
# so that the secure cookies from the session library will work
|
|
app = TestApp(
|
|
base_app,
|
|
extra_environ={'wsgi.url_scheme': 'https'},
|
|
cookiejar=CookieJar(),
|
|
)
|
|
|
|
# fetch the login page, fill in the form, and submit it (sets the cookie)
|
|
login_page = app.get('/login')
|
|
login_page.form['username'] = 'SessionUser'
|
|
login_page.form['password'] = 'session user password'
|
|
login_page.form.submit()
|
|
|
|
yield app
|
|
|
|
|
|
@fixture(scope='session')
|
|
def webtest_loggedout(base_app):
|
|
"""Create a logged-out webtest TestApp (no cookies retained)."""
|
|
yield TestApp(base_app)
|