fix: format all code with black
and from now on should not deviate from that...
This commit is contained in:
		
							parent
							
								
									45dabce956
								
							
						
					
					
						commit
						33ac0e008e
					
				
					 17 changed files with 730 additions and 576 deletions
				
			
		| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
import inspect
 | 
			
		||||
from unittest import TestCase
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ class TestImportCommandHandler(DataTestCase):
 | 
			
		|||
        handler = self.make_handler()
 | 
			
		||||
        self.assertIsNone(handler.import_handler)
 | 
			
		||||
 | 
			
		||||
        FromCsvToWutta = self.app.load_object('wuttasync.importing.csv:FromCsvToWutta')
 | 
			
		||||
        FromCsvToWutta = self.app.load_object("wuttasync.importing.csv:FromCsvToWutta")
 | 
			
		||||
 | 
			
		||||
        # as spec
 | 
			
		||||
        handler = self.make_handler(import_handler=FromCsvToWutta.get_spec())
 | 
			
		||||
| 
						 | 
				
			
			@ -35,26 +35,30 @@ class TestImportCommandHandler(DataTestCase):
 | 
			
		|||
        self.assertIs(handler.import_handler, myhandler)
 | 
			
		||||
 | 
			
		||||
    def test_run(self):
 | 
			
		||||
        handler = self.make_handler(import_handler='wuttasync.importing.csv:FromCsvToWutta')
 | 
			
		||||
        handler = self.make_handler(
 | 
			
		||||
            import_handler="wuttasync.importing.csv:FromCsvToWutta"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        with patch.object(handler, 'list_models') as list_models:
 | 
			
		||||
            handler.run({'list_models': True})
 | 
			
		||||
            list_models.assert_called_once_with({'list_models': True})
 | 
			
		||||
        with patch.object(handler, "list_models") as list_models:
 | 
			
		||||
            handler.run({"list_models": True})
 | 
			
		||||
            list_models.assert_called_once_with({"list_models": True})
 | 
			
		||||
 | 
			
		||||
        with patch.object(handler, 'import_handler') as import_handler:
 | 
			
		||||
            handler.run({'models': []})
 | 
			
		||||
        with patch.object(handler, "import_handler") as import_handler:
 | 
			
		||||
            handler.run({"models": []})
 | 
			
		||||
            import_handler.process_data.assert_called_once_with()
 | 
			
		||||
 | 
			
		||||
    def test_list_models(self):
 | 
			
		||||
        handler = self.make_handler(import_handler='wuttasync.importing.csv:FromCsvToWutta')
 | 
			
		||||
        handler = self.make_handler(
 | 
			
		||||
            import_handler="wuttasync.importing.csv:FromCsvToWutta"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        with patch.object(mod, 'sys') as sys:
 | 
			
		||||
        with patch.object(mod, "sys") as sys:
 | 
			
		||||
            handler.list_models({})
 | 
			
		||||
            # just test a few random things we expect to see
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call('ALL MODELS:\n'))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call('Person'))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call('User'))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call('Upgrade'))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call("ALL MODELS:\n"))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call("Person"))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call("User"))
 | 
			
		||||
            self.assertTrue(sys.stdout.write.has_call("Upgrade"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestImporterCommand(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -64,12 +68,12 @@ class TestImporterCommand(TestCase):
 | 
			
		|||
            pass
 | 
			
		||||
 | 
			
		||||
        sig1 = inspect.signature(myfunc)
 | 
			
		||||
        self.assertIn('kwargs', sig1.parameters)
 | 
			
		||||
        self.assertNotIn('dry_run', sig1.parameters)
 | 
			
		||||
        self.assertIn("kwargs", sig1.parameters)
 | 
			
		||||
        self.assertNotIn("dry_run", sig1.parameters)
 | 
			
		||||
        wrapt = mod.import_command(myfunc)
 | 
			
		||||
        sig2 = inspect.signature(wrapt)
 | 
			
		||||
        self.assertNotIn('kwargs', sig2.parameters)
 | 
			
		||||
        self.assertIn('dry_run', sig2.parameters)
 | 
			
		||||
        self.assertNotIn("kwargs", sig2.parameters)
 | 
			
		||||
        self.assertIn("dry_run", sig2.parameters)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestFileImporterCommand(TestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -79,11 +83,11 @@ class TestFileImporterCommand(TestCase):
 | 
			
		|||
            pass
 | 
			
		||||
 | 
			
		||||
        sig1 = inspect.signature(myfunc)
 | 
			
		||||
        self.assertIn('kwargs', sig1.parameters)
 | 
			
		||||
        self.assertNotIn('dry_run', sig1.parameters)
 | 
			
		||||
        self.assertNotIn('input_file_path', sig1.parameters)
 | 
			
		||||
        self.assertIn("kwargs", sig1.parameters)
 | 
			
		||||
        self.assertNotIn("dry_run", sig1.parameters)
 | 
			
		||||
        self.assertNotIn("input_file_path", sig1.parameters)
 | 
			
		||||
        wrapt = mod.file_import_command(myfunc)
 | 
			
		||||
        sig2 = inspect.signature(wrapt)
 | 
			
		||||
        self.assertNotIn('kwargs', sig2.parameters)
 | 
			
		||||
        self.assertIn('dry_run', sig2.parameters)
 | 
			
		||||
        self.assertIn('input_file_path', sig2.parameters)
 | 
			
		||||
        self.assertNotIn("kwargs", sig2.parameters)
 | 
			
		||||
        self.assertIn("dry_run", sig2.parameters)
 | 
			
		||||
        self.assertIn("input_file_path", sig2.parameters)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
from unittest import TestCase
 | 
			
		||||
from unittest.mock import MagicMock, patch
 | 
			
		||||
| 
						 | 
				
			
			@ -6,14 +6,17 @@ from unittest.mock import MagicMock, patch
 | 
			
		|||
from wuttasync.cli import import_csv as mod, ImportCommandHandler
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestImportCsv(TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_basic(self):
 | 
			
		||||
        params = {'models': [],
 | 
			
		||||
                  'create': True, 'update': True, 'delete': False,
 | 
			
		||||
                  'dry_run': True}
 | 
			
		||||
        params = {
 | 
			
		||||
            "models": [],
 | 
			
		||||
            "create": True,
 | 
			
		||||
            "update": True,
 | 
			
		||||
            "delete": False,
 | 
			
		||||
            "dry_run": True,
 | 
			
		||||
        }
 | 
			
		||||
        ctx = MagicMock(params=params)
 | 
			
		||||
        with patch.object(ImportCommandHandler, 'run') as run:
 | 
			
		||||
        with patch.object(ImportCommandHandler, "run") as run:
 | 
			
		||||
            mod.import_csv(ctx)
 | 
			
		||||
            run.assert_called_once_with(params)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
from unittest.mock import patch
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        self.handler = ImportHandler(self.config)
 | 
			
		||||
 | 
			
		||||
    def make_importer(self, **kwargs):
 | 
			
		||||
        kwargs.setdefault('handler', self.handler)
 | 
			
		||||
        kwargs.setdefault("handler", self.handler)
 | 
			
		||||
        return mod.Importer(self.config, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_constructor(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -24,11 +24,11 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        # fields
 | 
			
		||||
        self.assertEqual(imp.fields, ['name', 'value'])
 | 
			
		||||
        self.assertEqual(imp.fields, ["name", "value"])
 | 
			
		||||
 | 
			
		||||
        # orientation etc.
 | 
			
		||||
        self.assertEqual(imp.orientation, Orientation.IMPORT)
 | 
			
		||||
        self.assertEqual(imp.actioning, 'importing')
 | 
			
		||||
        self.assertEqual(imp.actioning, "importing")
 | 
			
		||||
        self.assertTrue(imp.create)
 | 
			
		||||
        self.assertTrue(imp.update)
 | 
			
		||||
        self.assertTrue(imp.delete)
 | 
			
		||||
| 
						 | 
				
			
			@ -38,106 +38,111 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        model = self.app.model
 | 
			
		||||
 | 
			
		||||
        # basic importer
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, fields='name')
 | 
			
		||||
        self.assertEqual(imp.fields, ['name'])
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, fields="name")
 | 
			
		||||
        self.assertEqual(imp.fields, ["name"])
 | 
			
		||||
 | 
			
		||||
    def test_constructor_excluded_fields(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
 | 
			
		||||
        # basic importer
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, excluded_fields='value')
 | 
			
		||||
        self.assertEqual(imp.fields, ['name'])
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, excluded_fields="value")
 | 
			
		||||
        self.assertEqual(imp.fields, ["name"])
 | 
			
		||||
 | 
			
		||||
    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')
 | 
			
		||||
        self.assertEqual(imp.get_model_title(), "Setting")
 | 
			
		||||
        imp.model_title = "SeTtInG"
 | 
			
		||||
        self.assertEqual(imp.get_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'])
 | 
			
		||||
        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'])
 | 
			
		||||
        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'])
 | 
			
		||||
        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'])
 | 
			
		||||
        with patch.multiple(imp, create=True, key='value'):
 | 
			
		||||
            self.assertEqual(imp.get_keys(), ['value'])
 | 
			
		||||
        with patch.multiple(imp, create=True, keys=['foo', 'bar']):
 | 
			
		||||
            self.assertEqual(imp.get_keys(), ['foo', 'bar'])
 | 
			
		||||
        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"]):
 | 
			
		||||
            self.assertEqual(imp.get_keys(), ["foo", "bar"])
 | 
			
		||||
 | 
			
		||||
    def test_process_data(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, caches_target=True,
 | 
			
		||||
                                 delete=True)
 | 
			
		||||
        imp = self.make_importer(
 | 
			
		||||
            model_class=model.Setting, caches_target=True, delete=True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        def make_cache():
 | 
			
		||||
            setting1 = model.Setting(name='foo1', value='bar1')
 | 
			
		||||
            setting2 = model.Setting(name='foo2', value='bar2')
 | 
			
		||||
            setting3 = model.Setting(name='foo3', value='bar3')
 | 
			
		||||
            setting1 = model.Setting(name="foo1", value="bar1")
 | 
			
		||||
            setting2 = model.Setting(name="foo2", value="bar2")
 | 
			
		||||
            setting3 = model.Setting(name="foo3", value="bar3")
 | 
			
		||||
            cache = {
 | 
			
		||||
                ('foo1',): {
 | 
			
		||||
                    'object': setting1,
 | 
			
		||||
                    'data': {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                ("foo1",): {
 | 
			
		||||
                    "object": setting1,
 | 
			
		||||
                    "data": {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                },
 | 
			
		||||
                ('foo2',): {
 | 
			
		||||
                    'object': setting2,
 | 
			
		||||
                    'data': {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
                ("foo2",): {
 | 
			
		||||
                    "object": setting2,
 | 
			
		||||
                    "data": {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                },
 | 
			
		||||
                ('foo3',): {
 | 
			
		||||
                    'object': setting3,
 | 
			
		||||
                    'data': {'name': 'foo3', 'value': 'bar3'},
 | 
			
		||||
                ("foo3",): {
 | 
			
		||||
                    "object": setting3,
 | 
			
		||||
                    "data": {"name": "foo3", "value": "bar3"},
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
            return cache
 | 
			
		||||
 | 
			
		||||
        # nb. delete always succeeds
 | 
			
		||||
        with patch.object(imp, 'delete_target_object', return_value=True):
 | 
			
		||||
        with patch.object(imp, "delete_target_object", return_value=True):
 | 
			
		||||
 | 
			
		||||
            # create + update + delete all as needed
 | 
			
		||||
            with patch.object(imp, 'get_target_cache', return_value=make_cache()):
 | 
			
		||||
                created, updated, deleted = imp.process_data([
 | 
			
		||||
                    {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                    {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
                    {'name': 'foo5', 'value': 'BAR5'},
 | 
			
		||||
                ])
 | 
			
		||||
            with patch.object(imp, "get_target_cache", return_value=make_cache()):
 | 
			
		||||
                created, updated, deleted = imp.process_data(
 | 
			
		||||
                    [
 | 
			
		||||
                        {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                        {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                        {"name": "foo5", "value": "BAR5"},
 | 
			
		||||
                    ]
 | 
			
		||||
                )
 | 
			
		||||
                self.assertEqual(len(created), 2)
 | 
			
		||||
                self.assertEqual(len(updated), 1)
 | 
			
		||||
                self.assertEqual(len(deleted), 2)
 | 
			
		||||
 | 
			
		||||
            # same but with --max-total so delete gets skipped
 | 
			
		||||
            with patch.object(imp, 'get_target_cache', return_value=make_cache()):
 | 
			
		||||
                with patch.object(imp, 'max_total', new=3):
 | 
			
		||||
                    created, updated, deleted = imp.process_data([
 | 
			
		||||
                        {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                        {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
                        {'name': 'foo5', 'value': 'BAR5'},
 | 
			
		||||
                    ])
 | 
			
		||||
            with patch.object(imp, "get_target_cache", return_value=make_cache()):
 | 
			
		||||
                with patch.object(imp, "max_total", new=3):
 | 
			
		||||
                    created, updated, deleted = imp.process_data(
 | 
			
		||||
                        [
 | 
			
		||||
                            {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                            {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                            {"name": "foo5", "value": "BAR5"},
 | 
			
		||||
                        ]
 | 
			
		||||
                    )
 | 
			
		||||
                    self.assertEqual(len(created), 2)
 | 
			
		||||
                    self.assertEqual(len(updated), 1)
 | 
			
		||||
                    self.assertEqual(len(deleted), 0)
 | 
			
		||||
 | 
			
		||||
            # delete all if source data empty
 | 
			
		||||
            with patch.object(imp, 'get_target_cache', return_value=make_cache()):
 | 
			
		||||
            with patch.object(imp, "get_target_cache", return_value=make_cache()):
 | 
			
		||||
                created, updated, deleted = imp.process_data()
 | 
			
		||||
                self.assertEqual(len(created), 0)
 | 
			
		||||
                self.assertEqual(len(updated), 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -148,120 +153,140 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        imp = self.make_importer(model_class=model.Setting, caches_target=True)
 | 
			
		||||
 | 
			
		||||
        def make_cache():
 | 
			
		||||
            setting1 = model.Setting(name='foo1', value='bar1')
 | 
			
		||||
            setting2 = model.Setting(name='foo2', value='bar2')
 | 
			
		||||
            setting1 = model.Setting(name="foo1", value="bar1")
 | 
			
		||||
            setting2 = model.Setting(name="foo2", value="bar2")
 | 
			
		||||
            cache = {
 | 
			
		||||
                ('foo1',): {
 | 
			
		||||
                    'object': setting1,
 | 
			
		||||
                    'data': {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                ("foo1",): {
 | 
			
		||||
                    "object": setting1,
 | 
			
		||||
                    "data": {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                },
 | 
			
		||||
                ('foo2',): {
 | 
			
		||||
                    'object': setting2,
 | 
			
		||||
                    'data': {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
                ("foo2",): {
 | 
			
		||||
                    "object": setting2,
 | 
			
		||||
                    "data": {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
            return cache
 | 
			
		||||
 | 
			
		||||
        # change nothing if data matches
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache()):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                    {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 0)
 | 
			
		||||
            self.assertEqual(len(updated), 0)
 | 
			
		||||
 | 
			
		||||
        # update all as needed
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache()):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'BAR1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'BAR2'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "BAR1"},
 | 
			
		||||
                    {"name": "foo2", "value": "BAR2"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 0)
 | 
			
		||||
            self.assertEqual(len(updated), 2)
 | 
			
		||||
 | 
			
		||||
        # update all, with --max-update
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache(), max_update=1):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'BAR1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'BAR2'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "BAR1"},
 | 
			
		||||
                    {"name": "foo2", "value": "BAR2"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 0)
 | 
			
		||||
            self.assertEqual(len(updated), 1)
 | 
			
		||||
 | 
			
		||||
        # update all, with --max-total
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache(), max_total=1):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'BAR1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'BAR2'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "BAR1"},
 | 
			
		||||
                    {"name": "foo2", "value": "BAR2"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 0)
 | 
			
		||||
            self.assertEqual(len(updated), 1)
 | 
			
		||||
 | 
			
		||||
        # create all as needed
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache()):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
                {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                    {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                    {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                    {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 2)
 | 
			
		||||
            self.assertEqual(len(updated), 0)
 | 
			
		||||
 | 
			
		||||
        # what happens when create gets skipped
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache()):
 | 
			
		||||
            with patch.object(imp, 'create_target_object', return_value=None):
 | 
			
		||||
                created, updated = imp.do_create_update([
 | 
			
		||||
                    {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                    {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
                    {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                    {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
                ])
 | 
			
		||||
            with patch.object(imp, "create_target_object", return_value=None):
 | 
			
		||||
                created, updated = imp.do_create_update(
 | 
			
		||||
                    [
 | 
			
		||||
                        {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                        {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                        {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                        {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                    ]
 | 
			
		||||
                )
 | 
			
		||||
                self.assertEqual(len(created), 0)
 | 
			
		||||
                self.assertEqual(len(updated), 0)
 | 
			
		||||
 | 
			
		||||
        # create all, with --max-create
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache(), max_create=1):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
                {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                    {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                    {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                    {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 1)
 | 
			
		||||
            self.assertEqual(len(updated), 0)
 | 
			
		||||
 | 
			
		||||
        # create all, with --max-total
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache(), max_total=1):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
                {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "bar1"},
 | 
			
		||||
                    {"name": "foo2", "value": "bar2"},
 | 
			
		||||
                    {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                    {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 1)
 | 
			
		||||
            self.assertEqual(len(updated), 0)
 | 
			
		||||
 | 
			
		||||
        # create + update all as needed
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache()):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'BAR1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'BAR2'},
 | 
			
		||||
                {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "BAR1"},
 | 
			
		||||
                    {"name": "foo2", "value": "BAR2"},
 | 
			
		||||
                    {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                    {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(len(created), 2)
 | 
			
		||||
            self.assertEqual(len(updated), 2)
 | 
			
		||||
 | 
			
		||||
        # create + update all, with --max-total
 | 
			
		||||
        with patch.multiple(imp, create=True, cached_target=make_cache(), max_total=1):
 | 
			
		||||
            created, updated = imp.do_create_update([
 | 
			
		||||
                {'name': 'foo1', 'value': 'BAR1'},
 | 
			
		||||
                {'name': 'foo2', 'value': 'BAR2'},
 | 
			
		||||
                {'name': 'foo3', 'value': 'BAR3'},
 | 
			
		||||
                {'name': 'foo4', 'value': 'BAR4'},
 | 
			
		||||
            ])
 | 
			
		||||
            created, updated = imp.do_create_update(
 | 
			
		||||
                [
 | 
			
		||||
                    {"name": "foo1", "value": "BAR1"},
 | 
			
		||||
                    {"name": "foo2", "value": "BAR2"},
 | 
			
		||||
                    {"name": "foo3", "value": "BAR3"},
 | 
			
		||||
                    {"name": "foo4", "value": "BAR4"},
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
            # nb. foo1 is updated first
 | 
			
		||||
            self.assertEqual(len(created), 0)
 | 
			
		||||
            self.assertEqual(len(updated), 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -270,21 +295,21 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        model = self.app.model
 | 
			
		||||
 | 
			
		||||
        # this requires a mock target cache
 | 
			
		||||
        setting1 = model.Setting(name='foo1', value='bar1')
 | 
			
		||||
        setting2 = model.Setting(name='foo2', value='bar2')
 | 
			
		||||
        setting1 = model.Setting(name="foo1", value="bar1")
 | 
			
		||||
        setting2 = model.Setting(name="foo2", value="bar2")
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, caches_target=True)
 | 
			
		||||
        cache = {
 | 
			
		||||
            ('foo1',): {
 | 
			
		||||
                'object': setting1,
 | 
			
		||||
                'data': {'name': 'foo1', 'value': 'bar1'},
 | 
			
		||||
            ("foo1",): {
 | 
			
		||||
                "object": setting1,
 | 
			
		||||
                "data": {"name": "foo1", "value": "bar1"},
 | 
			
		||||
            },
 | 
			
		||||
            ('foo2',): {
 | 
			
		||||
                'object': setting2,
 | 
			
		||||
                'data': {'name': 'foo2', 'value': 'bar2'},
 | 
			
		||||
            ("foo2",): {
 | 
			
		||||
                "object": setting2,
 | 
			
		||||
                "data": {"name": "foo2", "value": "bar2"},
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        with patch.object(imp, 'delete_target_object') as delete_target_object:
 | 
			
		||||
        with patch.object(imp, "delete_target_object") as delete_target_object:
 | 
			
		||||
 | 
			
		||||
            # delete nothing if source has same keys
 | 
			
		||||
            with patch.multiple(imp, create=True, cached_target=dict(cache)):
 | 
			
		||||
| 
						 | 
				
			
			@ -305,7 +330,7 @@ class TestImporter(DataTestCase):
 | 
			
		|||
            delete_target_object.reset_mock()
 | 
			
		||||
            with patch.multiple(imp, create=True, cached_target=dict(cache)):
 | 
			
		||||
                source_keys = set()
 | 
			
		||||
                with patch.object(imp, 'max_delete', new=1):
 | 
			
		||||
                with patch.object(imp, "max_delete", new=1):
 | 
			
		||||
                    result = imp.do_delete(source_keys)
 | 
			
		||||
                    self.assertEqual(delete_target_object.call_count, 1)
 | 
			
		||||
                    self.assertEqual(len(result), 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +339,7 @@ class TestImporter(DataTestCase):
 | 
			
		|||
            delete_target_object.reset_mock()
 | 
			
		||||
            with patch.multiple(imp, create=True, cached_target=dict(cache)):
 | 
			
		||||
                source_keys = set()
 | 
			
		||||
                with patch.object(imp, 'max_total', new=1):
 | 
			
		||||
                with patch.object(imp, "max_total", new=1):
 | 
			
		||||
                    result = imp.do_delete(source_keys)
 | 
			
		||||
                    self.assertEqual(delete_target_object.call_count, 1)
 | 
			
		||||
                    self.assertEqual(len(result), 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -322,25 +347,25 @@ class TestImporter(DataTestCase):
 | 
			
		|||
    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'))
 | 
			
		||||
        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'}
 | 
			
		||||
        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'
 | 
			
		||||
        rec2["value"] = "baz"
 | 
			
		||||
        result = imp.data_diffs(rec1, rec2)
 | 
			
		||||
        self.assertEqual(result, ['value'])
 | 
			
		||||
        self.assertEqual(result, ["value"])
 | 
			
		||||
 | 
			
		||||
    def test_normalize_source_data(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
| 
						 | 
				
			
			@ -351,7 +376,7 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        self.assertEqual(data, [])
 | 
			
		||||
 | 
			
		||||
        # now with 1 record
 | 
			
		||||
        setting = model.Setting(name='foo', value='bar')
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			@ -361,17 +386,17 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        setting1 = model.Setting(name='foo', value='bar1')
 | 
			
		||||
        setting2 = model.Setting(name='foo', value='bar2')
 | 
			
		||||
        setting1 = model.Setting(name="foo", value="bar1")
 | 
			
		||||
        setting2 = model.Setting(name="foo", value="bar2")
 | 
			
		||||
 | 
			
		||||
        result = imp.get_unique_data([setting2, setting1])
 | 
			
		||||
        self.assertIsInstance(result, tuple)
 | 
			
		||||
        self.assertEqual(len(result), 2)
 | 
			
		||||
        self.assertIsInstance(result[0], list)
 | 
			
		||||
        self.assertEqual(len(result[0]), 1)
 | 
			
		||||
        self.assertIs(result[0][0], setting2) # nb. not setting1
 | 
			
		||||
        self.assertIs(result[0][0], setting2)  # nb. not setting1
 | 
			
		||||
        self.assertIsInstance(result[1], set)
 | 
			
		||||
        self.assertEqual(result[1], {('foo',)})
 | 
			
		||||
        self.assertEqual(result[1], {("foo",)})
 | 
			
		||||
 | 
			
		||||
    def test_get_source_objects(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
| 
						 | 
				
			
			@ -397,7 +422,7 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        with patch.object(imp, 'get_target_objects') as get_target_objects:
 | 
			
		||||
        with patch.object(imp, "get_target_objects") as get_target_objects:
 | 
			
		||||
            get_target_objects.return_value = []
 | 
			
		||||
 | 
			
		||||
            # empty cache
 | 
			
		||||
| 
						 | 
				
			
			@ -405,16 +430,16 @@ class TestImporter(DataTestCase):
 | 
			
		|||
            self.assertEqual(cache, {})
 | 
			
		||||
 | 
			
		||||
            # cache w/ one record
 | 
			
		||||
            setting = model.Setting(name='foo', value='bar')
 | 
			
		||||
            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.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'})
 | 
			
		||||
            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
 | 
			
		||||
| 
						 | 
				
			
			@ -423,36 +448,36 @@ class TestImporter(DataTestCase):
 | 
			
		|||
 | 
			
		||||
    def test_get_target_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
        setting = model.Setting(name='foo', value='bar')
 | 
			
		||||
        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'},
 | 
			
		||||
            ("foo",): {
 | 
			
		||||
                "object": setting,
 | 
			
		||||
                "data": {"name": "foo", "value": "bar"},
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        # returns same object
 | 
			
		||||
        result = imp.get_target_object(('foo',))
 | 
			
		||||
        result = imp.get_target_object(("foo",))
 | 
			
		||||
        self.assertIs(result, setting)
 | 
			
		||||
 | 
			
		||||
        # and one more time just for kicks
 | 
			
		||||
        result = imp.get_target_object(('foo',))
 | 
			
		||||
        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',))
 | 
			
		||||
        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')
 | 
			
		||||
        setting = model.Setting(name="foo", value="bar")
 | 
			
		||||
        data = imp.normalize_target_object(setting)
 | 
			
		||||
        self.assertEqual(data, {'name': 'foo', 'value': 'bar'})
 | 
			
		||||
        self.assertEqual(data, {"name": "foo", "value": "bar"})
 | 
			
		||||
 | 
			
		||||
    def test_get_deletable_keys(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
| 
						 | 
				
			
			@ -463,11 +488,11 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        self.assertIsInstance(result, set)
 | 
			
		||||
        self.assertEqual(result, set())
 | 
			
		||||
 | 
			
		||||
        setting = model.Setting(name='foo', value='bar')
 | 
			
		||||
        setting = model.Setting(name="foo", value="bar")
 | 
			
		||||
        cache = {
 | 
			
		||||
            ('foo',): {
 | 
			
		||||
                'object': setting,
 | 
			
		||||
                'data': {'name': 'foo', 'value': 'bar'},
 | 
			
		||||
            ("foo",): {
 | 
			
		||||
                "object": setting,
 | 
			
		||||
                "data": {"name": "foo", "value": "bar"},
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -475,10 +500,10 @@ class TestImporter(DataTestCase):
 | 
			
		|||
 | 
			
		||||
            # all are deletable by default
 | 
			
		||||
            result = imp.get_deletable_keys()
 | 
			
		||||
            self.assertEqual(result, {('foo',)})
 | 
			
		||||
            self.assertEqual(result, {("foo",)})
 | 
			
		||||
 | 
			
		||||
            # but some maybe can't be deleted
 | 
			
		||||
            with patch.object(imp, 'can_delete_object', return_value=False):
 | 
			
		||||
            with patch.object(imp, "can_delete_object", return_value=False):
 | 
			
		||||
                result = imp.get_deletable_keys()
 | 
			
		||||
                self.assertEqual(result, set())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -487,22 +512,23 @@ class TestImporter(DataTestCase):
 | 
			
		|||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        # basic
 | 
			
		||||
        setting = imp.create_target_object(('foo',), {'name': 'foo', 'value': 'bar'})
 | 
			
		||||
        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.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})
 | 
			
		||||
        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',))
 | 
			
		||||
        obj = imp.make_empty_object(("foo",))
 | 
			
		||||
        self.assertIsInstance(obj, model.Setting)
 | 
			
		||||
        self.assertEqual(obj.name, 'foo')
 | 
			
		||||
        self.assertEqual(obj.name, "foo")
 | 
			
		||||
 | 
			
		||||
    def test_make_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
| 
						 | 
				
			
			@ -513,23 +539,23 @@ class TestImporter(DataTestCase):
 | 
			
		|||
    def test_update_target_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
        setting = model.Setting(name='foo')
 | 
			
		||||
        setting = model.Setting(name="foo")
 | 
			
		||||
 | 
			
		||||
        # basic logic for updating *new* object
 | 
			
		||||
        obj = imp.update_target_object(setting, {'name': 'foo', 'value': 'bar'})
 | 
			
		||||
        obj = imp.update_target_object(setting, {"name": "foo", "value": "bar"})
 | 
			
		||||
        self.assertIs(obj, setting)
 | 
			
		||||
        self.assertEqual(setting.value, 'bar')
 | 
			
		||||
        self.assertEqual(setting.value, "bar")
 | 
			
		||||
 | 
			
		||||
    def test_can_delete_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
        setting = model.Setting(name='foo')
 | 
			
		||||
        setting = model.Setting(name="foo")
 | 
			
		||||
        self.assertTrue(imp.can_delete_object(setting))
 | 
			
		||||
 | 
			
		||||
    def test_delete_target_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
        setting = model.Setting(name='foo')
 | 
			
		||||
        setting = model.Setting(name="foo")
 | 
			
		||||
        # nb. default implementation always returns false
 | 
			
		||||
        self.assertFalse(imp.delete_target_object(setting))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -541,20 +567,20 @@ class TestFromFile(DataTestCase):
 | 
			
		|||
        self.handler = ImportHandler(self.config)
 | 
			
		||||
 | 
			
		||||
    def make_importer(self, **kwargs):
 | 
			
		||||
        kwargs.setdefault('handler', self.handler)
 | 
			
		||||
        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:
 | 
			
		||||
        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:
 | 
			
		||||
        with patch.object(imp, "close_input_file") as close_input_file:
 | 
			
		||||
            imp.teardown()
 | 
			
		||||
            close_input_file.assert_called_once_with()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -563,13 +589,13 @@ class TestFromFile(DataTestCase):
 | 
			
		|||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        # path is guessed from dir+filename
 | 
			
		||||
        path = self.write_file('data.txt', '')
 | 
			
		||||
        path = self.write_file("data.txt", "")
 | 
			
		||||
        imp.input_file_dir = self.tempdir
 | 
			
		||||
        imp.input_file_name = 'data.txt'
 | 
			
		||||
        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', '')
 | 
			
		||||
        path2 = self.write_file("data2.txt", "")
 | 
			
		||||
        imp.input_file_path = path2
 | 
			
		||||
        self.assertEqual(imp.get_input_file_path(), path2)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -592,8 +618,8 @@ class TestFromFile(DataTestCase):
 | 
			
		|||
        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')
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			@ -604,10 +630,10 @@ class TestFromFile(DataTestCase):
 | 
			
		|||
        model = self.app.model
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        path = self.write_file('data.txt', '')
 | 
			
		||||
        with open(path, 'rt') as f:
 | 
			
		||||
        path = self.write_file("data.txt", "")
 | 
			
		||||
        with open(path, "rt") as f:
 | 
			
		||||
            imp.input_file = f
 | 
			
		||||
            with patch.object(f, 'close') as close:
 | 
			
		||||
            with patch.object(f, "close") as close:
 | 
			
		||||
                imp.close_input_file()
 | 
			
		||||
                close.assert_called_once_with()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -619,16 +645,16 @@ class TestToSqlalchemy(DataTestCase):
 | 
			
		|||
        self.handler = ImportHandler(self.config)
 | 
			
		||||
 | 
			
		||||
    def make_importer(self, **kwargs):
 | 
			
		||||
        kwargs.setdefault('handler', self.handler)
 | 
			
		||||
        kwargs.setdefault("handler", self.handler)
 | 
			
		||||
        return mod.ToSqlalchemy(self.config, **kwargs)
 | 
			
		||||
 | 
			
		||||
    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')
 | 
			
		||||
        setting1 = model.Setting(name="foo", value="bar")
 | 
			
		||||
        self.session.add(setting1)
 | 
			
		||||
        setting2 = model.Setting(name='foo2', value='bar2')
 | 
			
		||||
        setting2 = model.Setting(name="foo2", value="bar2")
 | 
			
		||||
        self.session.add(setting2)
 | 
			
		||||
        self.session.commit()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -638,60 +664,60 @@ class TestToSqlalchemy(DataTestCase):
 | 
			
		|||
 | 
			
		||||
    def test_get_target_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
        setting = model.Setting(name='foo', value='bar')
 | 
			
		||||
        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'},
 | 
			
		||||
            ("foo",): {
 | 
			
		||||
                "object": setting,
 | 
			
		||||
                "data": {"name": "foo", "value": "bar"},
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        # returns same object
 | 
			
		||||
        result = imp.get_target_object(('foo',))
 | 
			
		||||
        result = imp.get_target_object(("foo",))
 | 
			
		||||
        self.assertIs(result, setting)
 | 
			
		||||
 | 
			
		||||
        # and one more time just for kicks
 | 
			
		||||
        result = imp.get_target_object(('foo',))
 | 
			
		||||
        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')
 | 
			
		||||
        setting2 = model.Setting(name="foo2", value="bar2")
 | 
			
		||||
        self.session.add(setting2)
 | 
			
		||||
        self.session.commit()
 | 
			
		||||
 | 
			
		||||
        # nb. disable target cache
 | 
			
		||||
        with patch.multiple(imp, create=True,
 | 
			
		||||
                            target_session=self.session,
 | 
			
		||||
                            caches_target=False):
 | 
			
		||||
        with patch.multiple(
 | 
			
		||||
            imp, create=True, target_session=self.session, caches_target=False
 | 
			
		||||
        ):
 | 
			
		||||
 | 
			
		||||
            # now we should be able to fetch that via query
 | 
			
		||||
            result = imp.get_target_object(('foo2',))
 | 
			
		||||
            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',))
 | 
			
		||||
            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')
 | 
			
		||||
        setting = model.Setting(name="foo", value="bar")
 | 
			
		||||
 | 
			
		||||
        # new object is added to session
 | 
			
		||||
        setting = imp.create_target_object(('foo',), {'name': 'foo', 'value': 'bar'})
 | 
			
		||||
        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.assertEqual(setting.name, "foo")
 | 
			
		||||
        self.assertEqual(setting.value, "bar")
 | 
			
		||||
        self.assertIn(setting, self.session)
 | 
			
		||||
 | 
			
		||||
    def test_delete_target_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
 | 
			
		||||
        setting = model.Setting(name='foo', value='bar')
 | 
			
		||||
        setting = model.Setting(name="foo", value="bar")
 | 
			
		||||
        self.session.add(setting)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.session.query(model.Setting).count(), 1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
import csv
 | 
			
		||||
import uuid as _uuid
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,12 @@ from unittest.mock import patch
 | 
			
		|||
 | 
			
		||||
from wuttjamaican.testing import DataTestCase
 | 
			
		||||
 | 
			
		||||
from wuttasync.importing import csv as mod, ImportHandler, ToSqlalchemyHandler, ToSqlalchemy
 | 
			
		||||
from wuttasync.importing import (
 | 
			
		||||
    csv as mod,
 | 
			
		||||
    ImportHandler,
 | 
			
		||||
    ToSqlalchemyHandler,
 | 
			
		||||
    ToSqlalchemy,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestFromCsv(DataTestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -15,14 +20,17 @@ class TestFromCsv(DataTestCase):
 | 
			
		|||
        self.setup_db()
 | 
			
		||||
        self.handler = ImportHandler(self.config)
 | 
			
		||||
 | 
			
		||||
        self.data_path = self.write_file('data.txt', """\
 | 
			
		||||
        self.data_path = self.write_file(
 | 
			
		||||
            "data.txt",
 | 
			
		||||
            """\
 | 
			
		||||
name,value
 | 
			
		||||
foo,bar
 | 
			
		||||
foo2,bar2
 | 
			
		||||
""")
 | 
			
		||||
""",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def make_importer(self, **kwargs):
 | 
			
		||||
        kwargs.setdefault('handler', self.handler)
 | 
			
		||||
        kwargs.setdefault("handler", self.handler)
 | 
			
		||||
        return mod.FromCsv(self.config, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_get_input_file_name(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -30,39 +38,41 @@ foo2,bar2
 | 
			
		|||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
 | 
			
		||||
        # name can be guessed
 | 
			
		||||
        self.assertEqual(imp.get_input_file_name(), 'Setting.csv')
 | 
			
		||||
        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')
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        # normal operation, input file includes all fields
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, input_file_path=self.data_path)
 | 
			
		||||
        self.assertEqual(imp.fields, ['name', 'value'])
 | 
			
		||||
        imp = self.make_importer(
 | 
			
		||||
            model_class=model.Setting, input_file_path=self.data_path
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(imp.fields, ["name", "value"])
 | 
			
		||||
        imp.open_input_file()
 | 
			
		||||
        self.assertEqual(imp.input_file.name, self.data_path)
 | 
			
		||||
        self.assertIsInstance(imp.input_reader, csv.DictReader)
 | 
			
		||||
        self.assertEqual(imp.fields, ['name', 'value'])
 | 
			
		||||
        self.assertEqual(imp.fields, ["name", "value"])
 | 
			
		||||
        imp.input_file.close()
 | 
			
		||||
 | 
			
		||||
        # this file is missing a field, plus we'll pretend more are
 | 
			
		||||
        # supported - but should wind up with just the one field
 | 
			
		||||
        missing = self.write_file('missing.txt', 'name')
 | 
			
		||||
        missing = self.write_file("missing.txt", "name")
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, input_file_path=missing)
 | 
			
		||||
        imp.fields.extend(['lots', 'more'])
 | 
			
		||||
        self.assertEqual(imp.fields, ['name', 'value', 'lots', 'more'])
 | 
			
		||||
        imp.fields.extend(["lots", "more"])
 | 
			
		||||
        self.assertEqual(imp.fields, ["name", "value", "lots", "more"])
 | 
			
		||||
        imp.open_input_file()
 | 
			
		||||
        self.assertEqual(imp.fields, ['name'])
 | 
			
		||||
        self.assertEqual(imp.fields, ["name"])
 | 
			
		||||
        imp.input_file.close()
 | 
			
		||||
 | 
			
		||||
        # and what happens when no known fields are found
 | 
			
		||||
        bogus = self.write_file('bogus.txt', 'blarg')
 | 
			
		||||
        bogus = self.write_file("bogus.txt", "blarg")
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting, input_file_path=bogus)
 | 
			
		||||
        self.assertEqual(imp.fields, ['name', 'value'])
 | 
			
		||||
        self.assertEqual(imp.fields, ["name", "value"])
 | 
			
		||||
        self.assertRaises(ValueError, imp.open_input_file)
 | 
			
		||||
 | 
			
		||||
    def test_close_input_file(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -72,8 +82,8 @@ foo2,bar2
 | 
			
		|||
        imp.input_file_path = self.data_path
 | 
			
		||||
        imp.open_input_file()
 | 
			
		||||
        imp.close_input_file()
 | 
			
		||||
        self.assertFalse(hasattr(imp, 'input_reader'))
 | 
			
		||||
        self.assertFalse(hasattr(imp, 'input_file'))
 | 
			
		||||
        self.assertFalse(hasattr(imp, "input_reader"))
 | 
			
		||||
        self.assertFalse(hasattr(imp, "input_file"))
 | 
			
		||||
 | 
			
		||||
    def test_get_source_objects(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
| 
						 | 
				
			
			@ -84,8 +94,8 @@ foo2,bar2
 | 
			
		|||
        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'})
 | 
			
		||||
        self.assertEqual(objects[0], {"name": "foo", "value": "bar"})
 | 
			
		||||
        self.assertEqual(objects[1], {"name": "foo2", "value": "bar2"})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MockMixinImporter(mod.FromCsvToSqlalchemyMixin, mod.FromCsv, ToSqlalchemy):
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +109,7 @@ class TestFromCsvToSqlalchemyMixin(DataTestCase):
 | 
			
		|||
        self.handler = ImportHandler(self.config)
 | 
			
		||||
 | 
			
		||||
    def make_importer(self, **kwargs):
 | 
			
		||||
        kwargs.setdefault('handler', self.handler)
 | 
			
		||||
        kwargs.setdefault("handler", self.handler)
 | 
			
		||||
        return MockMixinImporter(self.config, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_constructor(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -112,31 +122,50 @@ class TestFromCsvToSqlalchemyMixin(DataTestCase):
 | 
			
		|||
        # typical
 | 
			
		||||
        # nb. as of now Upgrade is the only table using proper UUID
 | 
			
		||||
        imp = self.make_importer(model_class=model.Upgrade)
 | 
			
		||||
        self.assertEqual(imp.uuid_keys, ['uuid'])
 | 
			
		||||
        self.assertEqual(imp.uuid_keys, ["uuid"])
 | 
			
		||||
 | 
			
		||||
    def test_normalize_source_object(self):
 | 
			
		||||
        model = self.app.model
 | 
			
		||||
 | 
			
		||||
        # no uuid keys
 | 
			
		||||
        imp = self.make_importer(model_class=model.Setting)
 | 
			
		||||
        result = imp.normalize_source_object({'name': 'foo', 'value': 'bar'})
 | 
			
		||||
        self.assertEqual(result, {'name': 'foo', 'value': 'bar'})
 | 
			
		||||
        result = imp.normalize_source_object({"name": "foo", "value": "bar"})
 | 
			
		||||
        self.assertEqual(result, {"name": "foo", "value": "bar"})
 | 
			
		||||
 | 
			
		||||
        # source has proper UUID
 | 
			
		||||
        # nb. as of now Upgrade is the only table using proper UUID
 | 
			
		||||
        imp = self.make_importer(model_class=model.Upgrade, fields=['uuid', 'description'])
 | 
			
		||||
        result = imp.normalize_source_object({'uuid': _uuid.UUID('06753693-d892-77f0-8000-ce71bf7ebbba'),
 | 
			
		||||
                                              'description': 'testing'})
 | 
			
		||||
        self.assertEqual(result, {'uuid': _uuid.UUID('06753693-d892-77f0-8000-ce71bf7ebbba'),
 | 
			
		||||
                                  'description': 'testing'})
 | 
			
		||||
        imp = self.make_importer(
 | 
			
		||||
            model_class=model.Upgrade, fields=["uuid", "description"]
 | 
			
		||||
        )
 | 
			
		||||
        result = imp.normalize_source_object(
 | 
			
		||||
            {
 | 
			
		||||
                "uuid": _uuid.UUID("06753693-d892-77f0-8000-ce71bf7ebbba"),
 | 
			
		||||
                "description": "testing",
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            result,
 | 
			
		||||
            {
 | 
			
		||||
                "uuid": _uuid.UUID("06753693-d892-77f0-8000-ce71bf7ebbba"),
 | 
			
		||||
                "description": "testing",
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # source has string uuid
 | 
			
		||||
        # nb. as of now Upgrade is the only table using proper UUID
 | 
			
		||||
        imp = self.make_importer(model_class=model.Upgrade, fields=['uuid', 'description'])
 | 
			
		||||
        result = imp.normalize_source_object({'uuid': '06753693d89277f08000ce71bf7ebbba',
 | 
			
		||||
                                              'description': 'testing'})
 | 
			
		||||
        self.assertEqual(result, {'uuid': _uuid.UUID('06753693-d892-77f0-8000-ce71bf7ebbba'),
 | 
			
		||||
                                  'description': 'testing'})
 | 
			
		||||
        imp = self.make_importer(
 | 
			
		||||
            model_class=model.Upgrade, fields=["uuid", "description"]
 | 
			
		||||
        )
 | 
			
		||||
        result = imp.normalize_source_object(
 | 
			
		||||
            {"uuid": "06753693d89277f08000ce71bf7ebbba", "description": "testing"}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            result,
 | 
			
		||||
            {
 | 
			
		||||
                "uuid": _uuid.UUID("06753693-d892-77f0-8000-ce71bf7ebbba"),
 | 
			
		||||
                "description": "testing",
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MockMixinHandler(mod.FromCsvToSqlalchemyHandlerMixin, ToSqlalchemyHandler):
 | 
			
		||||
| 
						 | 
				
			
			@ -149,27 +178,33 @@ class TestFromCsvToSqlalchemyHandlerMixin(DataTestCase):
 | 
			
		|||
        return MockMixinHandler(self.config, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_get_target_model(self):
 | 
			
		||||
        with patch.object(mod.FromCsvToSqlalchemyHandlerMixin, 'define_importers', return_value={}):
 | 
			
		||||
        with patch.object(
 | 
			
		||||
            mod.FromCsvToSqlalchemyHandlerMixin, "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.FromCsvToSqlalchemyHandlerMixin, 'get_target_model', return_value=model):
 | 
			
		||||
        with patch.object(
 | 
			
		||||
            mod.FromCsvToSqlalchemyHandlerMixin, "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)
 | 
			
		||||
            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.FromCsvToSqlalchemyHandlerMixin, 'define_importers', return_value={}):
 | 
			
		||||
        with patch.object(
 | 
			
		||||
            mod.FromCsvToSqlalchemyHandlerMixin, "define_importers", return_value={}
 | 
			
		||||
        ):
 | 
			
		||||
            handler = self.make_handler()
 | 
			
		||||
            factory = handler.make_importer_factory(model.Setting, 'Setting')
 | 
			
		||||
            factory = handler.make_importer_factory(model.Setting, "Setting")
 | 
			
		||||
            self.assertTrue(issubclass(factory, mod.FromCsv))
 | 
			
		||||
            self.assertTrue(issubclass(factory, ToSqlalchemy))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from unittest.mock import patch
 | 
			
		||||
| 
						 | 
				
			
			@ -17,34 +17,36 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
        handler = self.make_handler()
 | 
			
		||||
        self.assertEqual(str(handler), "None → None")
 | 
			
		||||
 | 
			
		||||
        handler.source_title = 'CSV'
 | 
			
		||||
        handler.target_title = 'Wutta'
 | 
			
		||||
        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')
 | 
			
		||||
        self.assertEqual(handler.actioning, "importing")
 | 
			
		||||
 | 
			
		||||
        handler.orientation = mod.Orientation.EXPORT
 | 
			
		||||
        self.assertEqual(handler.actioning, 'exporting')
 | 
			
		||||
        self.assertEqual(handler.actioning, "exporting")
 | 
			
		||||
 | 
			
		||||
    def test_get_key(self):
 | 
			
		||||
        handler = self.make_handler()
 | 
			
		||||
        self.assertEqual(handler.get_key(), 'to_None.from_None.import')
 | 
			
		||||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        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'
 | 
			
		||||
        handler.source_title = "CSV"
 | 
			
		||||
        handler.target_title = "Wutta"
 | 
			
		||||
        self.assertEqual(handler.get_title(), "CSV → Wutta")
 | 
			
		||||
 | 
			
		||||
    def test_get_source_title(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -54,16 +56,16 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        handler.source_title = "XXX"
 | 
			
		||||
        self.assertEqual(handler.get_source_title(), "XXX")
 | 
			
		||||
 | 
			
		||||
    def test_get_target_title(self):
 | 
			
		||||
        handler = self.make_handler()
 | 
			
		||||
| 
						 | 
				
			
			@ -72,23 +74,23 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        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:
 | 
			
		||||
        with patch.object(handler, "commit_transaction") as commit_transaction:
 | 
			
		||||
            handler.process_data()
 | 
			
		||||
            commit_transaction.assert_called_once_with()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +98,8 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
        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:
 | 
			
		||||
        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()
 | 
			
		||||
| 
						 | 
				
			
			@ -106,36 +108,38 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
        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')
 | 
			
		||||
        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'}]
 | 
			
		||||
        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
 | 
			
		||||
        handler.importers["Setting"] = SettingImporter
 | 
			
		||||
        self.assertEqual(self.session.query(model.Setting).count(), 0)
 | 
			
		||||
        handler.process_data('Setting')
 | 
			
		||||
        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')
 | 
			
		||||
        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)
 | 
			
		||||
        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):
 | 
			
		||||
| 
						 | 
				
			
			@ -148,10 +152,10 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
 | 
			
		||||
        # captures dry-run flag
 | 
			
		||||
        self.assertFalse(handler.dry_run)
 | 
			
		||||
        kw['dry_run'] = True
 | 
			
		||||
        kw["dry_run"] = True
 | 
			
		||||
        result = handler.consume_kwargs(kw)
 | 
			
		||||
        self.assertIs(result, kw)
 | 
			
		||||
        self.assertTrue(kw['dry_run'])
 | 
			
		||||
        self.assertTrue(kw["dry_run"])
 | 
			
		||||
        self.assertTrue(handler.dry_run)
 | 
			
		||||
 | 
			
		||||
    def test_define_importers(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -165,24 +169,23 @@ class TestImportHandler(DataTestCase):
 | 
			
		|||
        handler = self.make_handler()
 | 
			
		||||
 | 
			
		||||
        # normal
 | 
			
		||||
        handler.importers['Setting'] = Importer
 | 
			
		||||
        importer = handler.get_importer('Setting', model_class=model.Setting)
 | 
			
		||||
        handler.importers["Setting"] = Importer
 | 
			
		||||
        importer = handler.get_importer("Setting", model_class=model.Setting)
 | 
			
		||||
        self.assertIsInstance(importer, Importer)
 | 
			
		||||
 | 
			
		||||
        # specifying empty keys
 | 
			
		||||
        handler.importers['Setting'] = Importer
 | 
			
		||||
        importer = handler.get_importer('Setting', model_class=model.Setting,
 | 
			
		||||
                                        keys=None)
 | 
			
		||||
        handler.importers["Setting"] = Importer
 | 
			
		||||
        importer = handler.get_importer("Setting", model_class=model.Setting, keys=None)
 | 
			
		||||
        self.assertIsInstance(importer, Importer)
 | 
			
		||||
        importer = handler.get_importer('Setting', model_class=model.Setting,
 | 
			
		||||
                                        keys='')
 | 
			
		||||
        importer = handler.get_importer("Setting", model_class=model.Setting, keys="")
 | 
			
		||||
        self.assertIsInstance(importer, Importer)
 | 
			
		||||
        importer = handler.get_importer('Setting', model_class=model.Setting,
 | 
			
		||||
                                        keys=[])
 | 
			
		||||
        importer = handler.get_importer("Setting", model_class=model.Setting, keys=[])
 | 
			
		||||
        self.assertIsInstance(importer, Importer)
 | 
			
		||||
 | 
			
		||||
        # key not found
 | 
			
		||||
        self.assertRaises(KeyError, handler.get_importer, 'BunchOfNonsense', model_class=model.Setting)
 | 
			
		||||
        self.assertRaises(
 | 
			
		||||
            KeyError, handler.get_importer, "BunchOfNonsense", model_class=model.Setting
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestFromFileHandler(DataTestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -192,8 +195,8 @@ class TestFromFileHandler(DataTestCase):
 | 
			
		|||
 | 
			
		||||
    def test_process_data(self):
 | 
			
		||||
        handler = self.make_handler()
 | 
			
		||||
        path = self.write_file('data.txt', '')
 | 
			
		||||
        with patch.object(mod.ImportHandler, 'process_data') as process_data:
 | 
			
		||||
        path = self.write_file("data.txt", "")
 | 
			
		||||
        with patch.object(mod.ImportHandler, "process_data") as process_data:
 | 
			
		||||
 | 
			
		||||
            # bare
 | 
			
		||||
            handler.process_data()
 | 
			
		||||
| 
						 | 
				
			
			@ -217,7 +220,7 @@ class TestToSqlalchemyHandler(DataTestCase):
 | 
			
		|||
 | 
			
		||||
    def test_begin_target_transaction(self):
 | 
			
		||||
        handler = self.make_handler()
 | 
			
		||||
        with patch.object(handler, 'make_target_session') as make_target_session:
 | 
			
		||||
        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()
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +228,7 @@ class TestToSqlalchemyHandler(DataTestCase):
 | 
			
		|||
 | 
			
		||||
    def test_rollback_target_transaction(self):
 | 
			
		||||
        handler = self.make_handler()
 | 
			
		||||
        with patch.object(handler, 'make_target_session') as make_target_session:
 | 
			
		||||
        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()
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +238,7 @@ class TestToSqlalchemyHandler(DataTestCase):
 | 
			
		|||
 | 
			
		||||
    def test_commit_target_transaction(self):
 | 
			
		||||
        handler = self.make_handler()
 | 
			
		||||
        with patch.object(handler, 'make_target_session') as make_target_session:
 | 
			
		||||
        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()
 | 
			
		||||
| 
						 | 
				
			
			@ -250,6 +253,6 @@ class TestToSqlalchemyHandler(DataTestCase):
 | 
			
		|||
    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)
 | 
			
		||||
        kw = handler.get_importer_kwargs("Setting")
 | 
			
		||||
        self.assertIn("target_session", kw)
 | 
			
		||||
        self.assertIs(kw["target_session"], self.session)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,3 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
from wuttasync.importing import model as mod
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#-*- coding: utf-8; -*-
 | 
			
		||||
# -*- coding: utf-8; -*-
 | 
			
		||||
 | 
			
		||||
from unittest.mock import patch
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,22 +16,22 @@ class TestToWuttaHandler(DataTestCase):
 | 
			
		|||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        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')
 | 
			
		||||
        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:
 | 
			
		||||
        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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,22 +8,24 @@ from wuttasync import util as mod
 | 
			
		|||
class TestDataDiffs(TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_source_missing_field(self):
 | 
			
		||||
        source = {'foo': 'bar'}
 | 
			
		||||
        target = {'baz': 'xyz', 'foo': 'bar'}
 | 
			
		||||
        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'])
 | 
			
		||||
        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'}
 | 
			
		||||
        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'}
 | 
			
		||||
        source = {"foo": "bar", "baz": "xyz"}
 | 
			
		||||
        target = {"baz": "xyz", "foo": "BAR"}
 | 
			
		||||
        result = mod.data_diffs(source, target)
 | 
			
		||||
        self.assertEqual(result, ['foo'])
 | 
			
		||||
        self.assertEqual(result, ["foo"])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue