fix: cleanup old code for "record changes" session feature

had some tests failing, and one thing led to another..  apparenty we
still had support for some pretty old sqlalchemy so no need to keep
that around.
This commit is contained in:
Lance Edgar 2024-08-27 22:56:32 -05:00
parent fe5951786c
commit d4f5515f20
6 changed files with 68 additions and 95 deletions

View file

@ -69,7 +69,7 @@ db = [
memcached = ["pylibmc"]
supervisor = ["supervisor"]
docs = ["Sphinx", "sphinx-paramlinks", "sphinxcontrib-programoutput", "furo"]
tests = ["coverage", "mock", "pytest", "pytest-cov"]
tests = ["pytest-cov", "tox"]
[project.scripts]

View file

@ -30,17 +30,13 @@ from packaging.version import parse as parse_version
import sqlalchemy as sa
from sqlalchemy.orm import object_mapper, RelationshipProperty
from sqlalchemy.orm.session import Session
try:
from sqlalchemy.event import listen
except ImportError:
listen = None # old SQLAlchemy; will have to work around that, below
from sqlalchemy.event import listen
from rattail.db.model import Setting, Change, DataSyncChange
try:
from rattail.db.continuum import versioning_manager
except ImportError: # assume no continuum
except ImportError: # pragma: no cover
versioning_manager = None
@ -49,10 +45,14 @@ log = logging.getLogger(__name__)
def record_changes(session, recorder=None, config=None):
"""
Record all relevant data changes which occur within a session.
Turn on the "record changes" feature.
:param session: A :class:`sqlalchemy:sqlalchemy.orm.session.Session` class,
or instance thereof.
With this enabled, all relevant data changes which occur in the
sesion will be recorded.
:param session: A
:class:`sqlalchemy:sqlalchemy.orm.session.Session` class, or
instance thereof.
"""
if isinstance(recorder, ChangeRecorder):
pass
@ -61,23 +61,15 @@ def record_changes(session, recorder=None, config=None):
elif recorder is None:
if config:
app = config.get_app()
spec = config.get('rattail.db', 'changes.recorder', usedb=False)
spec = config.get('rattail.db.changes.recorder', usedb=False)
if spec:
recorder = app.load_object(spec)(config)
if not recorder:
recorder = ChangeRecorder(config)
else:
raise ValueError("Invalid 'recorder' parameter: {}".format(repr(recorder)))
if listen:
listen(session, 'before_flush', recorder)
else:
extension = ChangeRecorderExtension(recorder)
if isinstance(session, Session):
session.extensions.append(extension)
else:
session.configure(extension=extension)
raise ValueError(f"recorder not valid: {recorder}")
listen(session, 'before_flush', recorder)
session.rattail_record_changes = True
session.rattail_change_recorder = recorder

View file

@ -1,10 +1,10 @@
# -*- coding: utf-8; -*-
from wuttjamaican.testing import FileConfigTestCase
from wuttjamaican.testing import FileTestCase
from rattail.config import RattailConfig
class DataTestCase(FileConfigTestCase):
class DataTestCase(FileTestCase):
"""
Base class for test suites requiring a full (typical) database.
"""
@ -14,7 +14,9 @@ class DataTestCase(FileConfigTestCase):
def setup_db(self):
self.setup_files()
self.config = self.make_config()
self.config = self.make_config(defaults={
'rattail.db.default.url': 'sqlite://',
})
self.app = self.config.get_app()
# init db
@ -28,7 +30,5 @@ class DataTestCase(FileConfigTestCase):
def teardown_db(self):
self.teardown_files()
def make_config(self):
return RattailConfig(defaults={
'rattail.db.default.url': 'sqlite://',
})
def make_config(self, **kwargs):
return RattailConfig(**kwargs)

View file

@ -1,86 +1,58 @@
# -*- coding: utf-8; -*-
from unittest import TestCase
from unittest.mock import patch, DEFAULT, Mock, MagicMock, call
from unittest.mock import patch, DEFAULT, Mock, MagicMock
from rattail.config import RattailConfig
from rattail.testing import DataTestCase
try:
from sqlalchemy import orm
from rattail import db
from rattail.db import changes as mod
from rattail.db import changes, model
from .. import DataTestCase
except ImportError:
pass
else:
class TestRecordChangesFunc(TestCase):
def setUp(self):
self.config = RattailConfig()
self.app = self.config.get_app()
class MockRecorder:
def __init__(self, config):
pass
def test_session_class(self):
Session = orm.sessionmaker()
if hasattr(Session, 'kw'):
self.assertRaises(KeyError, Session.kw.__getitem__, 'rattail_record_changes')
self.assertRaises(AttributeError, getattr, Session, 'rattail_record_changes')
changes.record_changes(Session)
self.assertTrue(Session.rattail_record_changes)
def test_session_instance(self):
session = db.Session()
self.assertFalse(session.rattail_record_changes)
changes.record_changes(session)
self.assertTrue(session.rattail_record_changes)
session.close()
class TestRecordChanges(DataTestCase):
def test_recorder(self):
def test_recorder_instance(self):
recorder = mod.ChangeRecorder(self.config)
self.assertFalse(self.session.rattail_record_changes)
mod.record_changes(self.session, recorder=recorder)
self.assertTrue(self.session.rattail_record_changes)
# no recorder
session = db.Session()
self.assertRaises(AttributeError, getattr, session, 'rattail_change_recorder')
session.close()
def test_recorder_factory(self):
self.assertFalse(self.session.rattail_record_changes)
mod.record_changes(self.session, recorder=mod.ChangeRecorder, config=self.config)
self.assertTrue(self.session.rattail_record_changes)
# default recorder
session = db.Session()
changes.record_changes(session)
self.assertIs(type(session.rattail_change_recorder), changes.ChangeRecorder)
session.close()
def test_configured_recorder(self):
self.config.setdefault('rattail.db.changes.recorder', 'tests.db.test_changes:MockRecorder')
self.assertFalse(self.session.rattail_record_changes)
mod.record_changes(self.session, config=self.config)
self.assertTrue(self.session.rattail_record_changes)
# specify recorder instance
recorder = changes.ChangeRecorder(self.config)
session = db.Session()
changes.record_changes(session, recorder=recorder)
self.assertIs(session.rattail_change_recorder, recorder)
session.close()
def test_default_recorder(self):
self.assertFalse(self.session.rattail_record_changes)
mod.record_changes(self.session, config=self.config)
self.assertTrue(self.session.rattail_record_changes)
# specify recorder factory
session = db.Session()
changes.record_changes(session, recorder=changes.ChangeRecorder, config=self.config)
self.assertIs(type(session.rattail_change_recorder), changes.ChangeRecorder)
session.close()
# specify recorder spec via config
config = RattailConfig()
config.setdefault('rattail.db', 'changes.recorder', 'rattail.db.changes:ChangeRecorder')
session = db.Session()
changes.record_changes(session, config=config)
self.assertIs(type(session.rattail_change_recorder), changes.ChangeRecorder)
session.close()
# invalid recorder
session = db.Session()
self.assertRaises(ValueError, changes.record_changes, session, recorder='bogus')
session.close()
def test_invalid_recorder(self):
self.assertFalse(self.session.rattail_record_changes)
self.assertRaises(ValueError, mod.record_changes, self.session, recorder=42)
class TestChangeRecorder(DataTestCase):
def extra_setup(self):
self.config = RattailConfig()
self.app = self.config.get_app()
def test_ignore_object(self):
recorder = changes.ChangeRecorder(self.config)
self.assertTrue(recorder.ignore_object(model.Setting()))
@ -311,8 +283,7 @@ else:
class TestFunctionalChanges(DataTestCase):
def setUp(self):
super().setUp()
self.config = RattailConfig()
self.setup_db()
changes.record_changes(self.session, config=self.config)
def test_add(self):

View file

@ -16,11 +16,20 @@ from rattail.core import Object
from rattail.db import Session
from rattail.autocomplete import Autocompleter
from rattail.batch import BatchHandler
from rattail.bouncer import BounceHandler
from rattail.importing import ImportHandler
from rattail.gpc import GPC
try:
from rattail.bouncer import BounceHandler
except ImportError:
pass
else:
class FooBarBounceHandler(BounceHandler):
pass
class TestAppHandler(TestCase):
def setUp(self):
@ -251,6 +260,11 @@ class TestAppHandler(TestCase):
def test_get_bounce_handler(self):
try:
from rattail.bouncer import BounceHandler
except ImportError:
pytest.skip("test not relevant without flufl.bounce")
# unknown type raises error by default
self.assertRaises(ValueError, self.app.get_bounce_handler, 'foobar')
@ -827,10 +841,6 @@ class FooBarBatchHandler(BatchHandler):
pass
class FooBarBounceHandler(BounceHandler):
pass
class FromFooToBar(ImportHandler):
host_key = 'rattail'
local_key = 'rattail'

View file

@ -1,13 +1,13 @@
[tox]
envlist = py38, py39, py310, py311, nodb
envlist = py38, py39, py310, py311, nox
[testenv]
extras = bouncer,db,tests
commands = pytest {posargs}
[testenv:nodb]
extras = bouncer,tests
[testenv:nox]
extras = tests
[testenv:coverage]
basepython = python3