mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	More plugin testing (#3052)
* Add a check of a child panel too * do not cover error catching * test for implementation error * Add warning to test for * Add test for event_sample * ignore safety switches * Add a settings flag to enable event testing * test if not implemented is raises * raise plugin specific errors * use plugin specific error * fix assertation * add test for mixin * this point can't be reached * add tests for locate plugin * fix assertations * fix function call * refert switch * this is already caught by the internal API * also cover mixin redirect
This commit is contained in:
		| @@ -910,6 +910,7 @@ if DEBUG or TESTING: | ||||
| # Plugin test settings | ||||
| PLUGIN_TESTING = get_setting('PLUGIN_TESTING', TESTING)  # are plugins beeing tested? | ||||
| PLUGIN_TESTING_SETUP = get_setting('PLUGIN_TESTING_SETUP', False)  # load plugins from setup hooks in testing? | ||||
| PLUGIN_TESTING_EVENTS = False                  # Flag if events are tested right now | ||||
| PLUGIN_RETRY = get_setting('PLUGIN_RETRY', 5)  # how often should plugin loading be tried? | ||||
| PLUGIN_FILE_CHECKED = False                    # Was the plugin file checked? | ||||
|  | ||||
|   | ||||
| @@ -76,7 +76,7 @@ class BulkNotificationMethodTests(BaseNotificationIntegrationTest): | ||||
|     def test_BulkNotificationMethod(self): | ||||
|         """ | ||||
|         Ensure the implementation requirements are tested. | ||||
|         NotImplementedError needs to raise if the send_bulk() method is not set. | ||||
|         MixinNotImplementedError needs to raise if the send_bulk() method is not set. | ||||
|         """ | ||||
|  | ||||
|         class WrongImplementation(BulkNotificationMethod): | ||||
| @@ -94,7 +94,7 @@ class SingleNotificationMethodTests(BaseNotificationIntegrationTest): | ||||
|     def test_SingleNotificationMethod(self): | ||||
|         """ | ||||
|         Ensure the implementation requirements are tested. | ||||
|         NotImplementedError needs to raise if the send() method is not set. | ||||
|         MixinNotImplementedError needs to raise if the send() method is not set. | ||||
|         """ | ||||
|  | ||||
|         class WrongImplementation(SingleNotificationMethod): | ||||
|   | ||||
| @@ -26,9 +26,10 @@ def trigger_event(event, *args, **kwargs): | ||||
|  | ||||
|     if not settings.PLUGINS_ENABLED: | ||||
|         # Do nothing if plugins are not enabled | ||||
|         return | ||||
|         return  # pragma: no cover | ||||
|  | ||||
|     if not canAppAccessDatabase(): | ||||
|     # Make sure the database can be accessed and is not beeing tested rn | ||||
|     if not canAppAccessDatabase() and not settings.PLUGIN_TESTING_EVENTS: | ||||
|         logger.debug(f"Ignoring triggered event '{event}' - database not ready") | ||||
|         return | ||||
|  | ||||
| @@ -91,7 +92,7 @@ def process_event(plugin_slug, event, *args, **kwargs): | ||||
|  | ||||
|     plugin = registry.plugins.get(plugin_slug, None) | ||||
|  | ||||
|     if plugin is None: | ||||
|     if plugin is None:  # pragma: no cover | ||||
|         logger.error(f"Could not find matching plugin for '{plugin_slug}'") | ||||
|         return | ||||
|  | ||||
| @@ -106,7 +107,7 @@ def allow_table_event(table_name): | ||||
|  | ||||
|     if isImportingData(): | ||||
|         # Prevent table events during the data import process | ||||
|         return False | ||||
|         return False  # pragma: no cover | ||||
|  | ||||
|     table_name = table_name.lower().strip() | ||||
|  | ||||
|   | ||||
| @@ -541,7 +541,7 @@ class PanelMixin: | ||||
|  | ||||
|     def get_custom_panels(self, view, request): | ||||
|         """ This method *must* be implemented by the plugin class """ | ||||
|         raise NotImplementedError(f"{__class__} is missing the 'get_custom_panels' method") | ||||
|         raise MixinNotImplementedError(f"{__class__} is missing the 'get_custom_panels' method") | ||||
|  | ||||
|     def get_panel_context(self, view, request, context): | ||||
|         """ | ||||
| @@ -559,7 +559,7 @@ class PanelMixin: | ||||
|  | ||||
|         try: | ||||
|             context['object'] = view.get_object() | ||||
|         except AttributeError: | ||||
|         except AttributeError:  # pragma: no cover | ||||
|             pass | ||||
|  | ||||
|         return context | ||||
|   | ||||
| @@ -8,6 +8,7 @@ from error_report.models import Error | ||||
|  | ||||
| from InvenTree.helpers import InvenTreeTestCase | ||||
| from plugin import InvenTreePlugin | ||||
| from plugin.base.integration.mixins import PanelMixin | ||||
| from plugin.helpers import MixinNotImplementedError | ||||
| from plugin.mixins import (APICallMixin, AppMixin, NavigationMixin, | ||||
|                            SettingsMixin, UrlsMixin) | ||||
| @@ -324,7 +325,7 @@ class PanelMixinTests(InvenTreeTestCase): | ||||
|         urls = [ | ||||
|             reverse('part-detail', kwargs={'pk': 1}), | ||||
|             reverse('stock-item-detail', kwargs={'pk': 2}), | ||||
|             reverse('stock-location-detail', kwargs={'pk': 1}), | ||||
|             reverse('stock-location-detail', kwargs={'pk': 2}), | ||||
|         ] | ||||
|  | ||||
|         plugin.set_setting('ENABLE_HELLO_WORLD', False) | ||||
| @@ -379,3 +380,13 @@ class PanelMixinTests(InvenTreeTestCase): | ||||
|  | ||||
|         # Assert that each request threw an error | ||||
|         self.assertEqual(Error.objects.count(), n_errors + len(urls)) | ||||
|  | ||||
|     def test_mixin(self): | ||||
|         """Test that ImplementationError is raised""" | ||||
|  | ||||
|         with self.assertRaises(MixinNotImplementedError): | ||||
|             class Wrong(PanelMixin, InvenTreePlugin): | ||||
|                 pass | ||||
|  | ||||
|             plugin = Wrong() | ||||
|             plugin.get_custom_panels('abc', 'abc') | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| import logging | ||||
|  | ||||
| from plugin.helpers import MixinImplementationError | ||||
| from plugin.helpers import MixinNotImplementedError | ||||
|  | ||||
| logger = logging.getLogger('inventree') | ||||
|  | ||||
| @@ -58,7 +58,7 @@ class LocateMixin: | ||||
|             if item.in_stock and item.location is not None: | ||||
|                 self.locate_stock_location(item.location.pk) | ||||
|  | ||||
|         except StockItem.DoesNotExist: | ||||
|         except StockItem.DoesNotExist:  # pragma: no cover | ||||
|             logger.warning("LocateMixin: StockItem pk={item_pk} not found") | ||||
|             pass | ||||
|  | ||||
| @@ -71,4 +71,4 @@ class LocateMixin: | ||||
|  | ||||
|         Note: The default implementation here does nothing! | ||||
|         """ | ||||
|         raise MixinImplementationError | ||||
|         raise MixinNotImplementedError | ||||
|   | ||||
| @@ -5,7 +5,8 @@ Unit tests for the 'locate' plugin mixin class | ||||
| from django.urls import reverse | ||||
|  | ||||
| from InvenTree.api_tester import InvenTreeAPITestCase | ||||
| from plugin.registry import registry | ||||
| from plugin import InvenTreePlugin, MixinNotImplementedError, registry | ||||
| from plugin.base.locate.mixins import LocateMixin | ||||
| from stock.models import StockItem, StockLocation | ||||
|  | ||||
|  | ||||
| @@ -145,3 +146,17 @@ class LocatePluginTests(InvenTreeAPITestCase): | ||||
|  | ||||
|             # Item metadata should have been altered! | ||||
|             self.assertTrue(location.metadata['located']) | ||||
|  | ||||
|     def test_mixin_locate(self): | ||||
|         """Test the sample mixin redirection""" | ||||
|         class SamplePlugin(LocateMixin, InvenTreePlugin): | ||||
|             pass | ||||
|  | ||||
|         plugin = SamplePlugin() | ||||
|  | ||||
|         # Test that the request is patched through to location | ||||
|         with self.assertRaises(MixinNotImplementedError): | ||||
|             plugin.locate_stock_item(1) | ||||
|  | ||||
|         # Test that it runs through | ||||
|         plugin.locate_stock_item(999) | ||||
|   | ||||
| @@ -2,6 +2,10 @@ | ||||
| Sample plugin which responds to events | ||||
| """ | ||||
|  | ||||
| import warnings | ||||
|  | ||||
| from django.conf import settings | ||||
|  | ||||
| from plugin import InvenTreePlugin | ||||
| from plugin.mixins import EventMixin | ||||
|  | ||||
| @@ -21,3 +25,7 @@ class EventPluginSample(EventMixin, InvenTreePlugin): | ||||
|         print(f"Processing triggered event: '{event}'") | ||||
|         print("args:", str(args)) | ||||
|         print("kwargs:", str(kwargs)) | ||||
|  | ||||
|         # Issue warning that we can test for | ||||
|         if settings.PLUGIN_TESTING: | ||||
|             warnings.warn(f'Event `{event}` triggered') | ||||
|   | ||||
							
								
								
									
										40
									
								
								InvenTree/plugin/samples/event/test_event_sample.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								InvenTree/plugin/samples/event/test_event_sample.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| """Unit tests for event_sample sample plugins""" | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.test import TestCase | ||||
|  | ||||
| from plugin import InvenTreePlugin, registry | ||||
| from plugin.base.event.events import trigger_event | ||||
| from plugin.helpers import MixinNotImplementedError | ||||
| from plugin.mixins import EventMixin | ||||
|  | ||||
|  | ||||
| class EventPluginSampleTests(TestCase): | ||||
|     """Tests for EventPluginSample""" | ||||
|  | ||||
|     def test_run_event(self): | ||||
|         """Check if the event is issued""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('sampleevent').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|  | ||||
|         # Enable event testing | ||||
|         settings.PLUGIN_TESTING_EVENTS = True | ||||
|         # Check that an event is issued | ||||
|         with self.assertWarns(Warning) as cm: | ||||
|             trigger_event('test.event') | ||||
|         self.assertEqual(cm.warning.args[0], 'Event `test.event` triggered') | ||||
|  | ||||
|         # Disable again | ||||
|         settings.PLUGIN_TESTING_EVENTS = False | ||||
|  | ||||
|     def test_mixin(self): | ||||
|         """Test that MixinNotImplementedError is raised""" | ||||
|  | ||||
|         with self.assertRaises(MixinNotImplementedError): | ||||
|             class Wrong(EventMixin, InvenTreePlugin): | ||||
|                 pass | ||||
|  | ||||
|             plugin = Wrong() | ||||
|             plugin.process_event('abc') | ||||
| @@ -37,7 +37,7 @@ class SampleLocatePlugin(LocateMixin, InvenTreePlugin): | ||||
|             # Tag metadata | ||||
|             item.set_metadata('located', True) | ||||
|  | ||||
|         except (ValueError, StockItem.DoesNotExist): | ||||
|         except (ValueError, StockItem.DoesNotExist):  # pragma: no cover | ||||
|             logger.error(f"StockItem ID {item_pk} does not exist!") | ||||
|  | ||||
|     def locate_stock_location(self, location_pk): | ||||
| @@ -53,5 +53,5 @@ class SampleLocatePlugin(LocateMixin, InvenTreePlugin): | ||||
|             # Tag metadata | ||||
|             location.set_metadata('located', True) | ||||
|  | ||||
|         except (ValueError, StockLocation.DoesNotExist): | ||||
|         except (ValueError, StockLocation.DoesNotExist):  # pragma: no cover | ||||
|             logger.error(f"Location ID {location_pk} does not exist!") | ||||
|   | ||||
							
								
								
									
										60
									
								
								InvenTree/plugin/samples/locate/test_locate_sample.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								InvenTree/plugin/samples/locate/test_locate_sample.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| """Unit tests for locate_sample sample plugins""" | ||||
|  | ||||
| from django.urls import reverse | ||||
|  | ||||
| from InvenTree.api_tester import InvenTreeAPITestCase | ||||
| from plugin import InvenTreePlugin, registry | ||||
| from plugin.helpers import MixinNotImplementedError | ||||
| from plugin.mixins import LocateMixin | ||||
|  | ||||
|  | ||||
| class SampleLocatePlugintests(InvenTreeAPITestCase): | ||||
|     """Tests for SampleLocatePlugin""" | ||||
|  | ||||
|     fixtures = [ | ||||
|         'location', | ||||
|         'category', | ||||
|         'part', | ||||
|         'stock' | ||||
|     ] | ||||
|  | ||||
|     def test_run_locator(self): | ||||
|         """Check if the event is issued""" | ||||
|         # Activate plugin | ||||
|         config = registry.get_plugin('samplelocate').plugin_config() | ||||
|         config.active = True | ||||
|         config.save() | ||||
|  | ||||
|         # Test APIs | ||||
|         url = reverse('api-locate-plugin') | ||||
|  | ||||
|         # No plugin | ||||
|         self.post(url, {}, expected_code=400) | ||||
|  | ||||
|         # Wrong plugin | ||||
|         self.post(url, {'plugin': 'sampleevent'}, expected_code=400) | ||||
|  | ||||
|         # Right plugin - no search item | ||||
|         self.post(url, {'plugin': 'samplelocate'}, expected_code=400) | ||||
|  | ||||
|         # Right plugin - wrong reference | ||||
|         self.post(url, {'plugin': 'samplelocate', 'item': 999}, expected_code=404) | ||||
|  | ||||
|         # Right plugin - right reference | ||||
|         self.post(url, {'plugin': 'samplelocate', 'item': 1}, expected_code=200) | ||||
|  | ||||
|         # Right plugin - wrong reference | ||||
|         self.post(url, {'plugin': 'samplelocate', 'location': 999}, expected_code=404) | ||||
|  | ||||
|         # Right plugin - right reference | ||||
|         self.post(url, {'plugin': 'samplelocate', 'location': 1}, expected_code=200) | ||||
|  | ||||
|     def test_mixin(self): | ||||
|         """Test that MixinNotImplementedError is raised""" | ||||
|  | ||||
|         with self.assertRaises(MixinNotImplementedError): | ||||
|             class Wrong(LocateMixin, InvenTreePlugin): | ||||
|                 pass | ||||
|  | ||||
|             plugin = Wrong() | ||||
|             plugin.locate_stock_location(1) | ||||
		Reference in New Issue
	
	Block a user