feat: add the import-versions command, handler logic

only works if wutta-continuum is already installed and enabled.

this also rearranges some existing classes, for better consistency
This commit is contained in:
Lance Edgar 2025-12-18 20:03:47 -06:00
parent c38cd2c179
commit fc250a433c
19 changed files with 1345 additions and 76 deletions

View file

@ -2,6 +2,8 @@
from unittest.mock import patch
from sqlalchemy import orm
from wuttjamaican.testing import DataTestCase
from wuttasync.importing import base as mod, ImportHandler, Orientation
@ -78,13 +80,31 @@ class TestImporter(DataTestCase):
def test_get_keys(self):
model = self.app.model
# nb. get_keys() will cache the return value, so must
# re-create importer for each test
# keys inspected from model by default
imp = self.make_importer(model_class=model.Setting)
self.assertEqual(imp.get_keys(), ["name"])
with patch.multiple(imp, create=True, key="value"):
self.assertEqual(imp.get_keys(), ["value"])
with patch.multiple(imp, create=True, keys=["foo", "bar"]):
imp = self.make_importer(model_class=model.User)
self.assertEqual(imp.get_keys(), ["uuid"])
# class may define 'keys'
imp = self.make_importer(model_class=model.User)
with patch.object(imp, "keys", new=["foo", "bar"], create=True):
self.assertEqual(imp.get_keys(), ["foo", "bar"])
# class may define 'key'
imp = self.make_importer(model_class=model.User)
with patch.object(imp, "key", new="whatever", create=True):
self.assertEqual(imp.get_keys(), ["whatever"])
# class may define 'default_keys'
imp = self.make_importer(model_class=model.User)
with patch.object(imp, "default_keys", new=["baz", "foo"]):
self.assertEqual(imp.get_keys(), ["baz", "foo"])
def test_process_data(self):
model = self.app.model
imp = self.make_importer(
@ -651,6 +671,105 @@ class TestFromFile(DataTestCase):
close.assert_called_once_with()
class TestQueryWrapper(DataTestCase):
def test_basic(self):
model = self.app.model
p1 = model.Person(full_name="John Doe")
self.session.add(p1)
p2 = model.Person(full_name="Jane Doe")
self.session.add(p2)
self.session.commit()
# cannot get count via len(query), must use query.count()
query = self.session.query(model.Person)
self.assertEqual(query.count(), 2)
self.assertRaises(TypeError, len, query)
# but can use len(wrapper)
wrapper = mod.QueryWrapper(query)
self.assertEqual(len(wrapper), 2)
# iter(wrapper) should work too
people = [p for p in wrapper]
self.assertEqual(people, [p1, p2])
people = [p for p in iter(wrapper)]
self.assertEqual(people, [p1, p2])
people = [p for p in list(wrapper)]
self.assertEqual(people, [p1, p2])
class TestFromSqlalchemy(DataTestCase):
def setUp(self):
self.setup_db()
self.handler = ImportHandler(self.config)
def make_importer(self, **kwargs):
kwargs.setdefault("handler", self.handler)
return mod.FromSqlalchemy(self.config, **kwargs)
def test_get_source_query(self):
model = self.app.model
imp = self.make_importer(
source_model_class=model.Upgrade, source_session=self.session
)
query = imp.get_source_query()
self.assertIsInstance(query, orm.Query)
self.assertEqual(len(query.selectable.froms), 1)
table = query.selectable.froms[0]
self.assertEqual(table.name, "upgrade")
def test_get_source_objects(self):
model = self.app.model
user1 = model.User(username="fred")
self.session.add(user1)
user2 = model.User(username="bettie")
self.session.add(user2)
self.session.commit()
imp = self.make_importer(
source_model_class=model.User, source_session=self.session
)
result = imp.get_source_objects()
self.assertIsInstance(result, mod.QueryWrapper)
self.assertEqual(len(result), 2)
self.assertEqual(list(result), [user1, user2])
class TestFromSqlalchemyMirror(DataTestCase):
def setUp(self):
self.setup_db()
self.handler = ImportHandler(self.config)
def make_importer(self, **kwargs):
kwargs.setdefault("handler", self.handler)
return mod.FromSqlalchemyMirror(self.config, **kwargs)
def test_source_model_class(self):
model = self.app.model
# source_model_class will mirror model_class
imp = self.make_importer(model_class=model.Upgrade)
self.assertIs(imp.model_class, model.Upgrade)
self.assertIs(imp.source_model_class, model.Upgrade)
def test_normalize_source_object(self):
model = self.app.model
imp = self.make_importer(model_class=model.Upgrade)
upgrade = model.Upgrade()
# normalize_source_object() should invoke normalize_target_object()
with patch.object(imp, "normalize_target_object") as normalize_target_object:
normalize_target_object.return_value = 42
result = imp.normalize_source_object(upgrade)
self.assertEqual(result, 42)
normalize_target_object.assert_called_once_with(upgrade)
class TestToSqlalchemy(DataTestCase):
def setUp(self):