feat: initial release
just the basics in place for an ad-hoc CSV -> Wutta import. framework API only at this point, no CLI yet
This commit is contained in:
commit
e19eab418a
37 changed files with 3793 additions and 0 deletions
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
0
tests/importing/__init__.py
Normal file
0
tests/importing/__init__.py
Normal file
437
tests/importing/test_base.py
Normal file
437
tests/importing/test_base.py
Normal file
|
@ -0,0 +1,437 @@
|
|||
#-*- coding: utf-8; -*-
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from wuttjamaican.testing import DataTestCase
|
||||
|
||||
from wuttasync.importing import base as mod, ImportHandler, Orientation
|
||||
|
||||
|
||||
class TestImporter(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.handler = ImportHandler(self.config)
|
||||
|
||||
def make_importer(self, **kwargs):
|
||||
kwargs.setdefault('handler', self.handler)
|
||||
return mod.Importer(self.config, **kwargs)
|
||||
|
||||
def test_constructor(self):
|
||||
model = self.app.model
|
||||
|
||||
# basic importer
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# fields
|
||||
self.assertEqual(imp.supported_fields, ['name', 'value'])
|
||||
self.assertEqual(imp.simple_fields, ['name', 'value'])
|
||||
self.assertEqual(imp.fields, ['name', 'value'])
|
||||
|
||||
# orientation etc.
|
||||
self.assertEqual(imp.orientation, Orientation.IMPORT)
|
||||
self.assertEqual(imp.actioning, 'importing')
|
||||
self.assertTrue(imp.create)
|
||||
self.assertTrue(imp.update)
|
||||
self.assertTrue(imp.delete)
|
||||
self.assertFalse(imp.dry_run)
|
||||
|
||||
def test_get_model_title(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertEqual(imp.get_model_title(), 'Setting')
|
||||
imp.model_title = "SeTtInG"
|
||||
self.assertEqual(imp.get_model_title(), 'SeTtInG')
|
||||
|
||||
def test_get_simple_fields(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertEqual(imp.get_simple_fields(), ['name', 'value'])
|
||||
imp.simple_fields = ['name']
|
||||
self.assertEqual(imp.get_simple_fields(), ['name'])
|
||||
|
||||
def test_get_supported_fields(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertEqual(imp.get_supported_fields(), ['name', 'value'])
|
||||
imp.supported_fields = ['name']
|
||||
self.assertEqual(imp.get_supported_fields(), ['name'])
|
||||
|
||||
def test_get_fields(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertEqual(imp.get_fields(), ['name', 'value'])
|
||||
imp.fields = ['name']
|
||||
self.assertEqual(imp.get_fields(), ['name'])
|
||||
|
||||
def test_get_keys(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertEqual(imp.get_keys(), ['name'])
|
||||
imp.key = 'value'
|
||||
self.assertEqual(imp.get_keys(), ['value'])
|
||||
|
||||
def test_process_data(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting, caches_target=True)
|
||||
|
||||
# empty data set / just for coverage
|
||||
with patch.object(imp, 'normalize_source_data') as normalize_source_data:
|
||||
normalize_source_data.return_value = []
|
||||
|
||||
with patch.object(imp, 'get_target_cache') as get_target_cache:
|
||||
get_target_cache.return_value = {}
|
||||
|
||||
result = imp.process_data()
|
||||
self.assertEqual(result, ([], [], []))
|
||||
|
||||
def test_do_create_update(self):
|
||||
model = self.app.model
|
||||
|
||||
# this requires a mock target cache
|
||||
imp = self.make_importer(model_class=model.Setting, caches_target=True)
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
imp.cached_target = {
|
||||
('foo',): {
|
||||
'object': setting,
|
||||
'data': {'name': 'foo', 'value': 'bar'},
|
||||
},
|
||||
}
|
||||
|
||||
# will update the one record
|
||||
result = imp.do_create_update([{'name': 'foo', 'value': 'baz'}])
|
||||
self.assertIs(result[1][0][0], setting)
|
||||
self.assertEqual(result, ([], [(setting,
|
||||
# nb. target
|
||||
{'name': 'foo', 'value': 'bar'},
|
||||
# nb. source
|
||||
{'name': 'foo', 'value': 'baz'})]))
|
||||
self.assertEqual(setting.value, 'baz')
|
||||
|
||||
# will create a new record
|
||||
result = imp.do_create_update([{'name': 'blah', 'value': 'zay'}])
|
||||
self.assertIsNot(result[0][0][0], setting)
|
||||
setting_new = result[0][0][0]
|
||||
self.assertEqual(result, ([(setting_new,
|
||||
# nb. source
|
||||
{'name': 'blah', 'value': 'zay'})],
|
||||
[]))
|
||||
self.assertEqual(setting_new.name, 'blah')
|
||||
self.assertEqual(setting_new.value, 'zay')
|
||||
|
||||
# but what if new record is *not* created
|
||||
with patch.object(imp, 'create_target_object', return_value=None):
|
||||
result = imp.do_create_update([{'name': 'another', 'value': 'one'}])
|
||||
self.assertEqual(result, ([], []))
|
||||
|
||||
# def test_do_delete(self):
|
||||
# model = self.app.model
|
||||
# imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
def test_get_record_key(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
record = {'name': 'foo', 'value': 'bar'}
|
||||
self.assertEqual(imp.get_record_key(record), ('foo',))
|
||||
imp.key = ('name', 'value')
|
||||
self.assertEqual(imp.get_record_key(record), ('foo', 'bar'))
|
||||
|
||||
def test_data_diffs(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# 2 identical records
|
||||
rec1 = {'name': 'foo', 'value': 'bar'}
|
||||
rec2 = {'name': 'foo', 'value': 'bar'}
|
||||
result = imp.data_diffs(rec1, rec2)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
# now they're different
|
||||
rec2['value'] = 'baz'
|
||||
result = imp.data_diffs(rec1, rec2)
|
||||
self.assertEqual(result, ['value'])
|
||||
|
||||
def test_normalize_source_data(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# empty source data
|
||||
data = imp.normalize_source_data()
|
||||
self.assertEqual(data, [])
|
||||
|
||||
# now with 1 record
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
data = imp.normalize_source_data(source_objects=[setting])
|
||||
self.assertEqual(len(data), 1)
|
||||
# nb. default normalizer returns object as-is
|
||||
self.assertIs(data[0], setting)
|
||||
|
||||
def test_get_source_objects(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertEqual(imp.get_source_objects(), [])
|
||||
|
||||
def test_normalize_source_object_all(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
setting = model.Setting()
|
||||
result = imp.normalize_source_object_all(setting)
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assertIs(result[0], setting)
|
||||
|
||||
def test_normalize_source_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
setting = model.Setting()
|
||||
result = imp.normalize_source_object(setting)
|
||||
self.assertIs(result, setting)
|
||||
|
||||
def test_get_target_cache(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
with patch.object(imp, 'get_target_objects') as get_target_objects:
|
||||
get_target_objects.return_value = []
|
||||
|
||||
# empty cache
|
||||
cache = imp.get_target_cache()
|
||||
self.assertEqual(cache, {})
|
||||
|
||||
# cache w/ one record
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
get_target_objects.return_value = [setting]
|
||||
cache = imp.get_target_cache()
|
||||
self.assertEqual(len(cache), 1)
|
||||
self.assertIn(('foo',), cache)
|
||||
foo = cache[('foo',)]
|
||||
self.assertEqual(len(foo), 2)
|
||||
self.assertEqual(set(foo), {'object', 'data'})
|
||||
self.assertIs(foo['object'], setting)
|
||||
self.assertEqual(foo['data'], {'name': 'foo', 'value': 'bar'})
|
||||
|
||||
def test_get_target_objects(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertRaises(NotImplementedError, imp.get_target_objects)
|
||||
|
||||
def test_get_target_object(self):
|
||||
model = self.app.model
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
|
||||
# nb. must mock up a target cache for this one
|
||||
imp = self.make_importer(model_class=model.Setting, caches_target=True)
|
||||
imp.cached_target = {
|
||||
('foo',): {
|
||||
'object': setting,
|
||||
'data': {'name': 'foo', 'value': 'bar'},
|
||||
},
|
||||
}
|
||||
|
||||
# returns same object
|
||||
result = imp.get_target_object(('foo',))
|
||||
self.assertIs(result, setting)
|
||||
|
||||
# and one more time just for kicks
|
||||
result = imp.get_target_object(('foo',))
|
||||
self.assertIs(result, setting)
|
||||
|
||||
# but then not if cache flag is off
|
||||
imp.caches_target = False
|
||||
result = imp.get_target_object(('foo',))
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_normalize_target_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
data = imp.normalize_target_object(setting)
|
||||
self.assertEqual(data, {'name': 'foo', 'value': 'bar'})
|
||||
|
||||
def test_create_target_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# basic
|
||||
setting = imp.create_target_object(('foo',), {'name': 'foo', 'value': 'bar'})
|
||||
self.assertIsInstance(setting, model.Setting)
|
||||
self.assertEqual(setting.name, 'foo')
|
||||
self.assertEqual(setting.value, 'bar')
|
||||
|
||||
# will skip if magic delete flag is set
|
||||
setting = imp.create_target_object(('foo',), {'name': 'foo', 'value': 'bar',
|
||||
'__ignoreme__': True})
|
||||
self.assertIsNone(setting)
|
||||
|
||||
def test_make_empty_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
obj = imp.make_empty_object(('foo',))
|
||||
self.assertIsInstance(obj, model.Setting)
|
||||
self.assertEqual(obj.name, 'foo')
|
||||
|
||||
def test_make_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
obj = imp.make_object()
|
||||
self.assertIsInstance(obj, model.Setting)
|
||||
|
||||
def test_update_target_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
setting = model.Setting(name='foo')
|
||||
|
||||
# basic logic for updating *new* object
|
||||
obj = imp.update_target_object(setting, {'name': 'foo', 'value': 'bar'})
|
||||
self.assertIs(obj, setting)
|
||||
self.assertEqual(setting.value, 'bar')
|
||||
|
||||
|
||||
class TestFromFile(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.handler = ImportHandler(self.config)
|
||||
|
||||
def make_importer(self, **kwargs):
|
||||
kwargs.setdefault('handler', self.handler)
|
||||
return mod.FromFile(self.config, **kwargs)
|
||||
|
||||
def test_setup(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
with patch.object(imp, 'open_input_file') as open_input_file:
|
||||
imp.setup()
|
||||
open_input_file.assert_called_once_with()
|
||||
|
||||
def test_teardown(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
with patch.object(imp, 'close_input_file') as close_input_file:
|
||||
imp.teardown()
|
||||
close_input_file.assert_called_once_with()
|
||||
|
||||
def test_get_input_file_path(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# path is guessed from dir+filename
|
||||
path = self.write_file('data.txt', '')
|
||||
imp.input_file_dir = self.tempdir
|
||||
imp.input_file_name = 'data.txt'
|
||||
self.assertEqual(imp.get_input_file_path(), path)
|
||||
|
||||
# path can be explicitly set
|
||||
path2 = self.write_file('data2.txt', '')
|
||||
imp.input_file_path = path2
|
||||
self.assertEqual(imp.get_input_file_path(), path2)
|
||||
|
||||
def test_get_input_file_dir(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# path cannot be guessed
|
||||
self.assertRaises(NotImplementedError, imp.get_input_file_dir)
|
||||
|
||||
# path can be explicitly set
|
||||
imp.input_file_dir = self.tempdir
|
||||
self.assertEqual(imp.get_input_file_dir(), self.tempdir)
|
||||
|
||||
def test_get_input_file_name(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# name cannot be guessed
|
||||
self.assertRaises(NotImplementedError, imp.get_input_file_name)
|
||||
|
||||
# name can be explicitly set
|
||||
imp.input_file_name = 'data.txt'
|
||||
self.assertEqual(imp.get_input_file_name(), 'data.txt')
|
||||
|
||||
def test_open_input_file(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
self.assertRaises(NotImplementedError, imp.open_input_file)
|
||||
|
||||
def test_close_input_file(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
path = self.write_file('data.txt', '')
|
||||
with open(path, 'rt') as f:
|
||||
imp.input_file = f
|
||||
with patch.object(f, 'close') as close:
|
||||
imp.close_input_file()
|
||||
close.assert_called_once_with()
|
||||
|
||||
|
||||
class TestToSqlalchemy(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.handler = ImportHandler(self.config)
|
||||
|
||||
def make_importer(self, **kwargs):
|
||||
kwargs.setdefault('handler', self.handler)
|
||||
return mod.ToSqlalchemy(self.config, **kwargs)
|
||||
|
||||
def test_get_target_object(self):
|
||||
model = self.app.model
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
|
||||
# nb. must mock up a target cache for this one
|
||||
imp = self.make_importer(model_class=model.Setting, caches_target=True)
|
||||
imp.cached_target = {
|
||||
('foo',): {
|
||||
'object': setting,
|
||||
'data': {'name': 'foo', 'value': 'bar'},
|
||||
},
|
||||
}
|
||||
|
||||
# returns same object
|
||||
result = imp.get_target_object(('foo',))
|
||||
self.assertIs(result, setting)
|
||||
|
||||
# and one more time just for kicks
|
||||
result = imp.get_target_object(('foo',))
|
||||
self.assertIs(result, setting)
|
||||
|
||||
# now let's put a 2nd setting in the db
|
||||
setting2 = model.Setting(name='foo2', value='bar2')
|
||||
self.session.add(setting2)
|
||||
self.session.commit()
|
||||
|
||||
# then we should be able to fetch that via query
|
||||
imp.target_session = self.session
|
||||
result = imp.get_target_object(('foo2',))
|
||||
self.assertIsInstance(result, model.Setting)
|
||||
self.assertIs(result, setting2)
|
||||
|
||||
# but sometimes it will not be found
|
||||
result = imp.get_target_object(('foo3',))
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_create_target_object(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting, target_session=self.session)
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
|
||||
# new object is added to session
|
||||
setting = imp.create_target_object(('foo',), {'name': 'foo', 'value': 'bar'})
|
||||
self.assertIsInstance(setting, model.Setting)
|
||||
self.assertEqual(setting.name, 'foo')
|
||||
self.assertEqual(setting.value, 'bar')
|
||||
self.assertIn(setting, self.session)
|
||||
|
||||
def test_get_target_objects(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting, target_session=self.session)
|
||||
|
||||
setting1 = model.Setting(name='foo', value='bar')
|
||||
self.session.add(setting1)
|
||||
setting2 = model.Setting(name='foo2', value='bar2')
|
||||
self.session.add(setting2)
|
||||
self.session.commit()
|
||||
|
||||
result = imp.get_target_objects()
|
||||
self.assertEqual(len(result), 2)
|
||||
self.assertEqual(set(result), {setting1, setting2})
|
114
tests/importing/test_csv.py
Normal file
114
tests/importing/test_csv.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
#-*- coding: utf-8; -*-
|
||||
|
||||
import csv
|
||||
from unittest.mock import patch
|
||||
|
||||
from wuttjamaican.testing import DataTestCase
|
||||
|
||||
from wuttasync.importing import csv as mod, ImportHandler, ToSqlalchemyHandler, ToSqlalchemy
|
||||
|
||||
|
||||
class TestFromCsv(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.handler = ImportHandler(self.config)
|
||||
|
||||
def make_importer(self, **kwargs):
|
||||
kwargs.setdefault('handler', self.handler)
|
||||
return mod.FromCsv(self.config, **kwargs)
|
||||
|
||||
def test_get_input_file_name(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
# name can be guessed
|
||||
self.assertEqual(imp.get_input_file_name(), 'Setting.csv')
|
||||
|
||||
# name can be explicitly set
|
||||
imp.input_file_name = 'data.txt'
|
||||
self.assertEqual(imp.get_input_file_name(), 'data.txt')
|
||||
|
||||
def test_open_input_file(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
path = self.write_file('data.txt', '')
|
||||
imp.input_file_path = path
|
||||
imp.open_input_file()
|
||||
self.assertEqual(imp.input_file.name, path)
|
||||
self.assertIsInstance(imp.input_reader, csv.DictReader)
|
||||
imp.input_file.close()
|
||||
|
||||
def test_close_input_file(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
path = self.write_file('data.txt', '')
|
||||
imp.input_file_path = path
|
||||
imp.open_input_file()
|
||||
imp.close_input_file()
|
||||
self.assertFalse(hasattr(imp, 'input_reader'))
|
||||
self.assertFalse(hasattr(imp, 'input_file'))
|
||||
|
||||
def test_get_source_objects(self):
|
||||
model = self.app.model
|
||||
imp = self.make_importer(model_class=model.Setting)
|
||||
|
||||
path = self.write_file('data.csv', """\
|
||||
name,value
|
||||
foo,bar
|
||||
foo2,bar2
|
||||
""")
|
||||
imp.input_file_path = path
|
||||
imp.open_input_file()
|
||||
objects = imp.get_source_objects()
|
||||
imp.close_input_file()
|
||||
self.assertEqual(len(objects), 2)
|
||||
self.assertEqual(objects[0], {'name': 'foo', 'value': 'bar'})
|
||||
self.assertEqual(objects[1], {'name': 'foo2', 'value': 'bar2'})
|
||||
|
||||
|
||||
class MockMixinHandler(mod.FromCsvToSqlalchemyMixin, ToSqlalchemyHandler):
|
||||
ToImporterBase = ToSqlalchemy
|
||||
|
||||
|
||||
class TestFromCsvToSqlalchemyMixin(DataTestCase):
|
||||
|
||||
def make_handler(self, **kwargs):
|
||||
return MockMixinHandler(self.config, **kwargs)
|
||||
|
||||
def test_get_target_model(self):
|
||||
with patch.object(mod.FromCsvToSqlalchemyMixin, 'define_importers', return_value={}):
|
||||
handler = self.make_handler()
|
||||
self.assertRaises(NotImplementedError, handler.get_target_model)
|
||||
|
||||
def test_define_importers(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.FromCsvToSqlalchemyMixin, 'get_target_model', return_value=model):
|
||||
handler = self.make_handler()
|
||||
importers = handler.define_importers()
|
||||
self.assertIn('Setting', importers)
|
||||
self.assertTrue(issubclass(importers['Setting'], mod.FromCsv))
|
||||
self.assertTrue(issubclass(importers['Setting'], ToSqlalchemy))
|
||||
self.assertIn('User', importers)
|
||||
self.assertIn('Person', importers)
|
||||
self.assertIn('Role', importers)
|
||||
|
||||
def test_make_importer_factory(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.FromCsvToSqlalchemyMixin, 'define_importers', return_value={}):
|
||||
handler = self.make_handler()
|
||||
factory = handler.make_importer_factory(model.Setting, 'Setting')
|
||||
self.assertTrue(issubclass(factory, mod.FromCsv))
|
||||
self.assertTrue(issubclass(factory, ToSqlalchemy))
|
||||
|
||||
|
||||
class TestFromCsvToWutta(DataTestCase):
|
||||
|
||||
def make_handler(self, **kwargs):
|
||||
return mod.FromCsvToWutta(self.config, **kwargs)
|
||||
|
||||
def test_get_target_model(self):
|
||||
handler = self.make_handler()
|
||||
self.assertIs(handler.get_target_model(), self.app.model)
|
218
tests/importing/test_handlers.py
Normal file
218
tests/importing/test_handlers.py
Normal file
|
@ -0,0 +1,218 @@
|
|||
#-*- coding: utf-8; -*-
|
||||
|
||||
from collections import OrderedDict
|
||||
from unittest.mock import patch
|
||||
|
||||
from wuttjamaican.testing import DataTestCase
|
||||
|
||||
from wuttasync.importing import handlers as mod, Importer, ToSqlalchemy
|
||||
|
||||
|
||||
class TestImportHandler(DataTestCase):
|
||||
|
||||
def make_handler(self, **kwargs):
|
||||
return mod.ImportHandler(self.config, **kwargs)
|
||||
|
||||
def test_str(self):
|
||||
handler = self.make_handler()
|
||||
self.assertEqual(str(handler), "None → None")
|
||||
|
||||
handler.source_title = 'CSV'
|
||||
handler.target_title = 'Wutta'
|
||||
self.assertEqual(str(handler), "CSV → Wutta")
|
||||
|
||||
def test_actioning(self):
|
||||
handler = self.make_handler()
|
||||
self.assertEqual(handler.actioning, 'importing')
|
||||
|
||||
handler.orientation = mod.Orientation.EXPORT
|
||||
self.assertEqual(handler.actioning, 'exporting')
|
||||
|
||||
def test_get_key(self):
|
||||
handler = self.make_handler()
|
||||
self.assertEqual(handler.get_key(), 'to_None.from_None.import')
|
||||
|
||||
with patch.multiple(mod.ImportHandler, source_key='csv', target_key='wutta'):
|
||||
self.assertEqual(handler.get_key(), 'to_wutta.from_csv.import')
|
||||
|
||||
def test_get_spec(self):
|
||||
handler = self.make_handler()
|
||||
self.assertEqual(handler.get_spec(), 'wuttasync.importing.handlers:ImportHandler')
|
||||
|
||||
def test_get_title(self):
|
||||
handler = self.make_handler()
|
||||
self.assertEqual(handler.get_title(), "None → None")
|
||||
|
||||
handler.source_title = 'CSV'
|
||||
handler.target_title = 'Wutta'
|
||||
self.assertEqual(handler.get_title(), "CSV → Wutta")
|
||||
|
||||
def test_get_source_title(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# null by default
|
||||
self.assertIsNone(handler.get_source_title())
|
||||
|
||||
# which is really using source_key as fallback
|
||||
handler.source_key = 'csv'
|
||||
self.assertEqual(handler.get_source_title(), 'csv')
|
||||
|
||||
# can also use (defined) generic fallback
|
||||
handler.generic_source_title = 'CSV'
|
||||
self.assertEqual(handler.get_source_title(), 'CSV')
|
||||
|
||||
# or can set explicitly
|
||||
handler.source_title = 'XXX'
|
||||
self.assertEqual(handler.get_source_title(), 'XXX')
|
||||
|
||||
def test_get_target_title(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# null by default
|
||||
self.assertIsNone(handler.get_target_title())
|
||||
|
||||
# which is really using target_key as fallback
|
||||
handler.target_key = 'wutta'
|
||||
self.assertEqual(handler.get_target_title(), 'wutta')
|
||||
|
||||
# can also use (defined) generic fallback
|
||||
handler.generic_target_title = 'Wutta'
|
||||
self.assertEqual(handler.get_target_title(), 'Wutta')
|
||||
|
||||
# or can set explicitly
|
||||
handler.target_title = 'XXX'
|
||||
self.assertEqual(handler.get_target_title(), 'XXX')
|
||||
|
||||
def test_process_data(self):
|
||||
model = self.app.model
|
||||
handler = self.make_handler()
|
||||
|
||||
# empy/no-op should commit (not fail)
|
||||
with patch.object(handler, 'commit_transaction') as commit_transaction:
|
||||
handler.process_data()
|
||||
commit_transaction.assert_called_once_with()
|
||||
|
||||
# do that again with no patch, just for kicks
|
||||
handler.process_data()
|
||||
|
||||
# dry-run should rollback
|
||||
with patch.object(handler, 'commit_transaction') as commit_transaction:
|
||||
with patch.object(handler, 'rollback_transaction') as rollback_transaction:
|
||||
handler.process_data(dry_run=True)
|
||||
self.assertFalse(commit_transaction.called)
|
||||
rollback_transaction.assert_called_once_with()
|
||||
|
||||
# and do that with no patch, for kicks
|
||||
handler.process_data(dry_run=True)
|
||||
|
||||
# outright error should cause rollback
|
||||
with patch.object(handler, 'commit_transaction') as commit_transaction:
|
||||
with patch.object(handler, 'rollback_transaction') as rollback_transaction:
|
||||
with patch.object(handler, 'get_importer', side_effect=RuntimeError):
|
||||
self.assertRaises(RuntimeError, handler.process_data, 'BlahBlah')
|
||||
self.assertFalse(commit_transaction.called)
|
||||
rollback_transaction.assert_called_once_with()
|
||||
|
||||
# fake importer class/data
|
||||
mock_source_objects = [{'name': 'foo', 'value': 'bar'}]
|
||||
class SettingImporter(ToSqlalchemy):
|
||||
model_class = model.Setting
|
||||
target_session = self.session
|
||||
def get_source_objects(self):
|
||||
return mock_source_objects
|
||||
|
||||
# now for a "normal" one
|
||||
handler.importers['Setting'] = SettingImporter
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 0)
|
||||
handler.process_data('Setting')
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 1)
|
||||
|
||||
# then add another mock record
|
||||
mock_source_objects.append({'name': 'foo2', 'value': 'bar2'})
|
||||
handler.process_data('Setting')
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 2)
|
||||
|
||||
# nb. even if dry-run, record is added
|
||||
# (rollback would happen later in that case)
|
||||
mock_source_objects.append({'name': 'foo3', 'value': 'bar3'})
|
||||
handler.process_data('Setting', dry_run=True)
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 3)
|
||||
|
||||
def test_consume_kwargs(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# kwargs are returned as-is
|
||||
kw = {}
|
||||
result = handler.consume_kwargs(kw)
|
||||
self.assertIs(result, kw)
|
||||
|
||||
# captures dry-run flag
|
||||
self.assertFalse(handler.dry_run)
|
||||
kw['dry_run'] = True
|
||||
result = handler.consume_kwargs(kw)
|
||||
self.assertIs(result, kw)
|
||||
self.assertTrue(kw['dry_run'])
|
||||
self.assertTrue(handler.dry_run)
|
||||
|
||||
def test_define_importers(self):
|
||||
handler = self.make_handler()
|
||||
importers = handler.define_importers()
|
||||
self.assertEqual(importers, {})
|
||||
self.assertIsInstance(importers, OrderedDict)
|
||||
|
||||
def test_get_importer(self):
|
||||
model = self.app.model
|
||||
handler = self.make_handler()
|
||||
|
||||
# normal
|
||||
handler.importers['Setting'] = Importer
|
||||
importer = handler.get_importer('Setting', model_class=model.Setting)
|
||||
self.assertIsInstance(importer, Importer)
|
||||
|
||||
# key not found
|
||||
self.assertRaises(KeyError, handler.get_importer, 'BunchOfNonsense', model_class=model.Setting)
|
||||
|
||||
|
||||
class TestToSqlalchemyHandler(DataTestCase):
|
||||
|
||||
def make_handler(self, **kwargs):
|
||||
return mod.ToSqlalchemyHandler(self.config, **kwargs)
|
||||
|
||||
def test_begin_target_transaction(self):
|
||||
handler = self.make_handler()
|
||||
with patch.object(handler, 'make_target_session') as make_target_session:
|
||||
make_target_session.return_value = self.session
|
||||
self.assertIsNone(handler.target_session)
|
||||
handler.begin_target_transaction()
|
||||
make_target_session.assert_called_once_with()
|
||||
|
||||
def test_rollback_target_transaction(self):
|
||||
handler = self.make_handler()
|
||||
with patch.object(handler, 'make_target_session') as make_target_session:
|
||||
make_target_session.return_value = self.session
|
||||
self.assertIsNone(handler.target_session)
|
||||
handler.begin_target_transaction()
|
||||
self.assertIs(handler.target_session, self.session)
|
||||
handler.rollback_target_transaction()
|
||||
self.assertIsNone(handler.target_session)
|
||||
|
||||
def test_commit_target_transaction(self):
|
||||
handler = self.make_handler()
|
||||
with patch.object(handler, 'make_target_session') as make_target_session:
|
||||
make_target_session.return_value = self.session
|
||||
self.assertIsNone(handler.target_session)
|
||||
handler.begin_target_transaction()
|
||||
self.assertIs(handler.target_session, self.session)
|
||||
handler.commit_target_transaction()
|
||||
self.assertIsNone(handler.target_session)
|
||||
|
||||
def test_make_target_session(self):
|
||||
handler = self.make_handler()
|
||||
self.assertRaises(NotImplementedError, handler.make_target_session)
|
||||
|
||||
def test_get_importer_kwargs(self):
|
||||
handler = self.make_handler()
|
||||
handler.target_session = self.session
|
||||
kw = handler.get_importer_kwargs('Setting')
|
||||
self.assertIn('target_session', kw)
|
||||
self.assertIs(kw['target_session'], self.session)
|
3
tests/importing/test_model.py
Normal file
3
tests/importing/test_model.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
#-*- coding: utf-8; -*-
|
||||
|
||||
from wuttasync.importing import model as mod
|
38
tests/importing/test_wutta.py
Normal file
38
tests/importing/test_wutta.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
#-*- coding: utf-8; -*-
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from wuttjamaican.testing import DataTestCase
|
||||
|
||||
from wuttasync.importing import wutta as mod
|
||||
|
||||
|
||||
class TestToWuttaHandler(DataTestCase):
|
||||
|
||||
def make_handler(self, **kwargs):
|
||||
return mod.ToWuttaHandler(self.config, **kwargs)
|
||||
|
||||
def test_get_target_title(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# uses app title by default
|
||||
self.config.setdefault('wutta.app_title', "What About This")
|
||||
self.assertEqual(handler.get_target_title(), 'What About This')
|
||||
|
||||
# or generic default if present
|
||||
handler.generic_target_title = "WHATABOUTTHIS"
|
||||
self.assertEqual(handler.get_target_title(), 'WHATABOUTTHIS')
|
||||
|
||||
# but prefer specific title if present
|
||||
handler.target_title = "what_about_this"
|
||||
self.assertEqual(handler.get_target_title(), 'what_about_this')
|
||||
|
||||
def test_make_target_session(self):
|
||||
handler = self.make_handler()
|
||||
|
||||
# makes "new" (mocked in our case) app session
|
||||
with patch.object(self.app, 'make_session') as make_session:
|
||||
make_session.return_value = self.session
|
||||
session = handler.make_target_session()
|
||||
make_session.assert_called_once_with()
|
||||
self.assertIs(session, self.session)
|
29
tests/test_util.py
Normal file
29
tests/test_util.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from wuttasync import util as mod
|
||||
|
||||
|
||||
class TestDataDiffs(TestCase):
|
||||
|
||||
def test_source_missing_field(self):
|
||||
source = {'foo': 'bar'}
|
||||
target = {'baz': 'xyz', 'foo': 'bar'}
|
||||
self.assertRaises(KeyError, mod.data_diffs, source, target)
|
||||
|
||||
def test_target_missing_field(self):
|
||||
source = {'foo': 'bar', 'baz': 'xyz'}
|
||||
target = {'baz': 'xyz'}
|
||||
self.assertRaises(KeyError, mod.data_diffs, source, target, fields=['foo', 'baz'])
|
||||
|
||||
def test_no_diffs(self):
|
||||
source = {'foo': 'bar', 'baz': 'xyz'}
|
||||
target = {'baz': 'xyz', 'foo': 'bar'}
|
||||
self.assertFalse(mod.data_diffs(source, target))
|
||||
|
||||
def test_with_diffs(self):
|
||||
source = {'foo': 'bar', 'baz': 'xyz'}
|
||||
target = {'baz': 'xyz', 'foo': 'BAR'}
|
||||
result = mod.data_diffs(source, target)
|
||||
self.assertEqual(result, ['foo'])
|
Loading…
Add table
Add a link
Reference in a new issue