2
0
Fork 0

test: add 'nodb' test runner

ensure things work as expected if sqlalchemy is not installed
This commit is contained in:
Lance Edgar 2024-07-04 08:00:42 -05:00
parent 132073177c
commit f5825e964c
5 changed files with 193 additions and 148 deletions

View file

@ -5,128 +5,131 @@ import shutil
import tempfile import tempfile
from unittest import TestCase from unittest import TestCase
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.engine import Engine
from sqlalchemy.pool import NullPool
from wuttjamaican.db import conf
from wuttjamaican.conf import WuttaConfig from wuttjamaican.conf import WuttaConfig
try:
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.engine import Engine
from sqlalchemy.pool import NullPool
from wuttjamaican.db import conf
except ImportError:
pass
else:
class TestGetEngines(TestCase): class TestGetEngines(TestCase):
def setUp(self): def setUp(self):
self.tempdir = tempfile.mkdtemp() self.tempdir = tempfile.mkdtemp()
def tearDown(self): def tearDown(self):
shutil.rmtree(self.tempdir) shutil.rmtree(self.tempdir)
def write_file(self, filename, content): def write_file(self, filename, content):
path = os.path.join(self.tempdir, filename) path = os.path.join(self.tempdir, filename)
with open(path, 'wt') as f: with open(path, 'wt') as f:
f.write(content) f.write(content)
return path return path
def test_no_default(self): def test_no_default(self):
myfile = self.write_file('my.conf', '') myfile = self.write_file('my.conf', '')
config = WuttaConfig([myfile]) config = WuttaConfig([myfile])
self.assertEqual(conf.get_engines(config, 'wuttadb'), {}) self.assertEqual(conf.get_engines(config, 'wuttadb'), {})
def test_default(self): def test_default(self):
myfile = self.write_file('my.conf', """\ myfile = self.write_file('my.conf', """\
[wuttadb] [wuttadb]
default.url = sqlite:// default.url = sqlite://
""") """)
config = WuttaConfig([myfile]) config = WuttaConfig([myfile])
result = conf.get_engines(config, 'wuttadb') result = conf.get_engines(config, 'wuttadb')
self.assertEqual(len(result), 1) self.assertEqual(len(result), 1)
self.assertIn('default', result) self.assertIn('default', result)
engine = result['default'] engine = result['default']
self.assertEqual(engine.dialect.name, 'sqlite') self.assertEqual(engine.dialect.name, 'sqlite')
def test_default_fallback(self): def test_default_fallback(self):
myfile = self.write_file('my.conf', """\ myfile = self.write_file('my.conf', """\
[wuttadb] [wuttadb]
sqlalchemy.url = sqlite:// sqlalchemy.url = sqlite://
""") """)
config = WuttaConfig([myfile]) config = WuttaConfig([myfile])
result = conf.get_engines(config, 'wuttadb') result = conf.get_engines(config, 'wuttadb')
self.assertEqual(len(result), 1) self.assertEqual(len(result), 1)
self.assertIn('default', result) self.assertIn('default', result)
engine = result['default'] engine = result['default']
self.assertEqual(engine.dialect.name, 'sqlite') self.assertEqual(engine.dialect.name, 'sqlite')
def test_other(self): def test_other(self):
myfile = self.write_file('my.conf', """\ myfile = self.write_file('my.conf', """\
[otherdb] [otherdb]
keys = first, second keys = first, second
first.url = sqlite:// first.url = sqlite://
second.url = sqlite:// second.url = sqlite://
""") """)
config = WuttaConfig([myfile]) config = WuttaConfig([myfile])
result = conf.get_engines(config, 'otherdb') result = conf.get_engines(config, 'otherdb')
self.assertEqual(len(result), 2) self.assertEqual(len(result), 2)
self.assertIn('first', result) self.assertIn('first', result)
self.assertIn('second', result) self.assertIn('second', result)
class TestGetSetting(TestCase): class TestGetSetting(TestCase):
def setUp(self): def setUp(self):
Session = orm.sessionmaker() Session = orm.sessionmaker()
engine = sa.create_engine('sqlite://') engine = sa.create_engine('sqlite://')
self.session = Session(bind=engine) self.session = Session(bind=engine)
self.session.execute(sa.text(""" self.session.execute(sa.text("""
create table setting ( create table setting (
name varchar(255) primary key, name varchar(255) primary key,
value text value text
); );
""")) """))
def tearDown(self): def tearDown(self):
self.session.close() self.session.close()
def test_basic_value(self): def test_basic_value(self):
self.session.execute(sa.text("insert into setting values ('foo', 'bar');")) self.session.execute(sa.text("insert into setting values ('foo', 'bar');"))
value = conf.get_setting(self.session, 'foo') value = conf.get_setting(self.session, 'foo')
self.assertEqual(value, 'bar') self.assertEqual(value, 'bar')
def test_missing_value(self): def test_missing_value(self):
value = conf.get_setting(self.session, 'foo') value = conf.get_setting(self.session, 'foo')
self.assertIsNone(value) self.assertIsNone(value)
class TestMakeEngineFromConfig(TestCase): class TestMakeEngineFromConfig(TestCase):
def test_basic(self): def test_basic(self):
engine = conf.make_engine_from_config({ engine = conf.make_engine_from_config({
'sqlalchemy.url': 'sqlite://', 'sqlalchemy.url': 'sqlite://',
}) })
self.assertIsInstance(engine, Engine) self.assertIsInstance(engine, Engine)
def test_poolclass(self): def test_poolclass(self):
engine = conf.make_engine_from_config({ engine = conf.make_engine_from_config({
'sqlalchemy.url': 'sqlite://', 'sqlalchemy.url': 'sqlite://',
}) })
self.assertNotIsInstance(engine.pool, NullPool) self.assertNotIsInstance(engine.pool, NullPool)
engine = conf.make_engine_from_config({ engine = conf.make_engine_from_config({
'sqlalchemy.url': 'sqlite://', 'sqlalchemy.url': 'sqlite://',
'sqlalchemy.poolclass': 'sqlalchemy.pool:NullPool', 'sqlalchemy.poolclass': 'sqlalchemy.pool:NullPool',
}) })
self.assertIsInstance(engine.pool, NullPool) self.assertIsInstance(engine.pool, NullPool)
def test_pool_pre_ping(self): def test_pool_pre_ping(self):
engine = conf.make_engine_from_config({ engine = conf.make_engine_from_config({
'sqlalchemy.url': 'sqlite://', 'sqlalchemy.url': 'sqlite://',
}) })
self.assertFalse(engine.pool._pre_ping) self.assertFalse(engine.pool._pre_ping)
engine = conf.make_engine_from_config({ engine = conf.make_engine_from_config({
'sqlalchemy.url': 'sqlite://', 'sqlalchemy.url': 'sqlite://',
'sqlalchemy.pool_pre_ping': 'true', 'sqlalchemy.pool_pre_ping': 'true',
}) })
self.assertTrue(engine.pool._pre_ping) self.assertTrue(engine.pool._pre_ping)

View file

@ -3,52 +3,55 @@
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock from unittest.mock import MagicMock
import sqlalchemy as sa
from sqlalchemy import orm
from wuttjamaican.db import sess
from wuttjamaican.conf import WuttaConfig from wuttjamaican.conf import WuttaConfig
try:
import sqlalchemy as sa
from sqlalchemy import orm
from wuttjamaican.db import sess
except ImportError:
pass
else:
class TestShortSession(TestCase): class TestShortSession(TestCase):
def test_none(self): def test_none(self):
with sess.short_session() as s: with sess.short_session() as s:
self.assertIsInstance(s, sess.Session.class_) self.assertIsInstance(s, sess.Session.class_)
def test_factory(self): def test_factory(self):
TestSession = orm.sessionmaker() TestSession = orm.sessionmaker()
with sess.short_session(factory=TestSession) as s: with sess.short_session(factory=TestSession) as s:
self.assertIsInstance(s, TestSession.class_) self.assertIsInstance(s, TestSession.class_)
def test_instance(self): def test_instance(self):
# nb. nothing really happens if we provide the session instance # nb. nothing really happens if we provide the session instance
session = MagicMock() session = MagicMock()
with sess.short_session(session=session) as s: with sess.short_session(session=session) as s:
pass pass
session.commit.assert_not_called() session.commit.assert_not_called()
session.close.assert_not_called() session.close.assert_not_called()
def test_config(self): def test_config(self):
config = MagicMock() config = MagicMock()
TestSession = orm.sessionmaker() TestSession = orm.sessionmaker()
config.get_app.return_value.make_session = TestSession config.get_app.return_value.make_session = TestSession
# nb. config may be first arg (or kwarg) # nb. config may be first arg (or kwarg)
with sess.short_session(config) as s: with sess.short_session(config) as s:
self.assertIsInstance(s, TestSession.class_) self.assertIsInstance(s, TestSession.class_)
def test_without_commit(self): def test_without_commit(self):
session = MagicMock() session = MagicMock()
TestSession = MagicMock(return_value=session) TestSession = MagicMock(return_value=session)
with sess.short_session(factory=TestSession, commit=False) as s: with sess.short_session(factory=TestSession, commit=False) as s:
pass pass
session.commit.assert_not_called() session.commit.assert_not_called()
session.close.assert_called_once_with() session.close.assert_called_once_with()
def test_with_commit(self): def test_with_commit(self):
session = MagicMock() session = MagicMock()
TestSession = MagicMock(return_value=session) TestSession = MagicMock(return_value=session)
with sess.short_session(factory=TestSession, commit=True) as s: with sess.short_session(factory=TestSession, commit=True) as s:
pass pass
session.commit.assert_called_once_with() session.commit.assert_called_once_with()
session.close.assert_called_once_with() session.close.assert_called_once_with()

View file

@ -7,10 +7,9 @@ import warnings
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
import sqlalchemy as sa import pytest
from sqlalchemy import orm
from wuttjamaican import app, db from wuttjamaican import app
from wuttjamaican.conf import WuttaConfig from wuttjamaican.conf import WuttaConfig
@ -46,6 +45,11 @@ class TestAppHandler(TestCase):
shutil.rmtree(tempdir) shutil.rmtree(tempdir)
def test_make_session(self): def test_make_session(self):
try:
from wuttjamaican import db
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
session = self.app.make_session() session = self.app.make_session()
self.assertIsInstance(session, db.Session.class_) self.assertIsInstance(session, db.Session.class_)
@ -60,6 +64,12 @@ class TestAppHandler(TestCase):
foo='bar', factory=self.app.make_session) foo='bar', factory=self.app.make_session)
def test_get_setting(self): def test_get_setting(self):
try:
import sqlalchemy as sa
from sqlalchemy import orm
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
Session = orm.sessionmaker() Session = orm.sessionmaker()
engine = sa.create_engine('sqlite://') engine = sa.create_engine('sqlite://')
session = Session(bind=engine) session = Session(bind=engine)

View file

@ -5,12 +5,10 @@ import os
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
import sqlalchemy as sa import pytest
from wuttjamaican import conf from wuttjamaican import conf
from wuttjamaican.exc import ConfigurationError from wuttjamaican.exc import ConfigurationError
from wuttjamaican.db import Session
from wuttjamaican.db.conf import make_engine_from_config
from wuttjamaican.app import AppHandler from wuttjamaican.app import AppHandler
from wuttjamaican.testing import FileConfigTestCase from wuttjamaican.testing import FileConfigTestCase
@ -133,6 +131,13 @@ require = %(here)s/first.conf
self.assertEqual(config.get('foo'), 'bar') self.assertEqual(config.get('foo'), 'bar')
def test_constructor_db_flags(self): def test_constructor_db_flags(self):
try:
# nb. we don't need this import but the test will not
# behave correctly unless the lib is installed
import sqlalchemy
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
myfile = self.write_file('my.conf', """\ myfile = self.write_file('my.conf', """\
[wutta.config] [wutta.config]
usedb = true usedb = true
@ -155,6 +160,12 @@ preferdb = true
self.assertTrue(config.preferdb) self.assertTrue(config.preferdb)
def test_constructor_db_not_supported(self): def test_constructor_db_not_supported(self):
try:
# nb. we don't need this import but the test will not
# behave correctly unless the lib is installed
import sqlalchemy
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
# flags are off by default # flags are off by default
config = conf.WuttaConfig() config = conf.WuttaConfig()
@ -269,6 +280,12 @@ configure_logging = true
self.assertEqual(config.get('foo', default='bar'), 'bar') self.assertEqual(config.get('foo', default='bar'), 'bar')
def test_get_from_db(self): def test_get_from_db(self):
try:
import sqlalchemy as sa
from wuttjamaican.db import Session
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
# minimal config, but at least it needs db cxn info # minimal config, but at least it needs db cxn info
config = conf.WuttaConfig(defaults={'wutta.db.default.url': 'sqlite://'}) config = conf.WuttaConfig(defaults={'wutta.db.default.url': 'sqlite://'})
@ -317,6 +334,11 @@ configure_logging = true
self.assertIn("makin stuff up", str(error)) self.assertIn("makin stuff up", str(error))
def test_get_preferdb(self): def test_get_preferdb(self):
try:
import sqlalchemy as sa
from wuttjamaican.db import Session
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
# start out with a default value # start out with a default value
config = conf.WuttaConfig(defaults={'wutta.db.default.url': 'sqlite://', config = conf.WuttaConfig(defaults={'wutta.db.default.url': 'sqlite://',
@ -403,6 +425,10 @@ configure_logging = true
self.assertIsInstance(app, CustomAppHandler) self.assertIsInstance(app, CustomAppHandler)
def test_get_engine_maker(self): def test_get_engine_maker(self):
try:
from wuttjamaican.db.conf import make_engine_from_config
except ImportError:
pytest.skip("test is not relevant without sqlalchemy")
# default func # default func
config = conf.WuttaConfig() config = conf.WuttaConfig()
@ -421,7 +447,7 @@ class CustomAppHandler(AppHandler):
pass pass
def custom_make_engine_from_config(*args, **kwargs): def custom_make_engine_from_config():
pass pass

View file

@ -1,6 +1,6 @@
[tox] [tox]
envlist = py36, py37, py38, py39, py310, py311 envlist = py36, py37, py38, py39, py310, py311, nodb
# TODO: can remove this when we drop py36 support # TODO: can remove this when we drop py36 support
# nb. need this for testing older python versions # nb. need this for testing older python versions
@ -15,6 +15,9 @@ commands = pytest {posargs}
# nb. newer coverage is causing segfault for this one, so must avoid that # nb. newer coverage is causing segfault for this one, so must avoid that
deps = coverage<6.5 deps = coverage<6.5
[testenv:nodb]
extras = tests
[testenv:coverage] [testenv:coverage]
basepython = python3.11 basepython = python3.11
extras = db,tests extras = db,tests