diff --git a/InvenTree/plugin/broken/__init__.py b/InvenTree/plugin/broken/__init__.py new file mode 100644 index 0000000000..f9765eae24 --- /dev/null +++ b/InvenTree/plugin/broken/__init__.py @@ -0,0 +1 @@ +# InvenTree plugin directory diff --git a/InvenTree/plugin/samples/integration/broken_file.py b/InvenTree/plugin/broken/broken_file.py similarity index 100% rename from InvenTree/plugin/samples/integration/broken_file.py rename to InvenTree/plugin/broken/broken_file.py diff --git a/InvenTree/plugin/samples/integration/broken_sample.py b/InvenTree/plugin/broken/broken_sample.py similarity index 100% rename from InvenTree/plugin/samples/integration/broken_sample.py rename to InvenTree/plugin/broken/broken_sample.py diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 358be4651a..dba2e9e56e 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -202,12 +202,13 @@ class PluginsRegistry: logger.info('Finished unloading plugins') - def reload_plugins(self, full_reload: bool = False, force_reload: bool = False): + def reload_plugins(self, full_reload: bool = False, force_reload: bool = False, collect: bool = False): """Safely reload. Args: full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False. force_reload (bool, optional): Also reload base apps. Defaults to False. + collect (bool, optional): Collect plugins before reloading. Defaults to False. """ # Do not reload when currently loading if self.is_loading: @@ -216,6 +217,10 @@ class PluginsRegistry: logger.info('Start reloading plugins') with maintenance_mode_on(): + if collect: + logger.info('Collecting plugins') + self.plugin_modules = self.collect_plugins() + self.plugins_loaded = False self.unload_plugins(force_reload=force_reload) self.plugins_loaded = True diff --git a/InvenTree/plugin/test_plugin.py b/InvenTree/plugin/test_plugin.py index 75453cdada..eba818cebd 100644 --- a/InvenTree/plugin/test_plugin.py +++ b/InvenTree/plugin/test_plugin.py @@ -195,7 +195,7 @@ class InvenTreePluginTests(TestCase): class RegistryTests(TestCase): """Tests for registry loading methods.""" - def mockDir(self) -> None: + def mockDir(self) -> str: """Returns path to mock dir""" return str(Path(__file__).parent.joinpath('mock').absolute()) @@ -206,7 +206,7 @@ class RegistryTests(TestCase): envs = {'INVENTREE_PLUGIN_TEST_DIR': directory} with mock.patch.dict(os.environ, envs): # Reload to redicsover plugins - registry.reload_plugins(full_reload=True) + registry.reload_plugins(full_reload=True, collect=True) # Depends on the meta set in InvenTree/plugin/mock/simple:SimplePlugin plg = registry.get_plugin('simple') @@ -252,9 +252,29 @@ class RegistryTests(TestCase): subprocess.check_output('pip install inventree-zapier'.split()) # Reload to discover plugin - registry.reload_plugins(full_reload=True) + registry.reload_plugins(full_reload=True, collect=True) # Test that plugin was installed plg = registry.get_plugin('zapier') self.assertEqual(plg.slug, 'zapier') self.assertEqual(plg.name, 'inventree_zapier') + + def test_broken_samples(self): + """Test that the broken samples trigger reloads.""" + + # In the base setup there are no errors + self.assertEqual(len(registry.errors), 1) + + # Reload the registry with the broken samples dir + brokenDir = str(Path(__file__).parent.joinpath('broken').absolute()) + with mock.patch.dict(os.environ, {'INVENTREE_PLUGIN_TEST_DIR': brokenDir}): + # Reload to redicsover plugins + registry.reload_plugins(full_reload=True, collect=True) + + self.assertEqual(len(registry.errors), 3) + # There should be at least one discovery error in the module `broken_file` + self.assertTrue(len(registry.errors.get('discovery')) > 0) + self.assertEqual(registry.errors.get('discovery')[0]['broken_file'], "name 'bb' is not defined") + # There should be at least one load error with an intentional KeyError + self.assertTrue(len(registry.errors.get('load')) > 0) + self.assertEqual(registry.errors.get('load')[0]['broken_sample'], "'This is a dummy error'")