2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 03:56:43 +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:
Matthias Mair 2022-05-24 01:23:06 +02:00 committed by GitHub
parent a7ef560ee3
commit 1c6e5f0f20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 151 additions and 15 deletions

View File

@ -910,6 +910,7 @@ if DEBUG or TESTING:
# Plugin test settings # Plugin test settings
PLUGIN_TESTING = get_setting('PLUGIN_TESTING', TESTING) # are plugins beeing tested? 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_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_RETRY = get_setting('PLUGIN_RETRY', 5) # how often should plugin loading be tried?
PLUGIN_FILE_CHECKED = False # Was the plugin file checked? PLUGIN_FILE_CHECKED = False # Was the plugin file checked?

View File

@ -76,7 +76,7 @@ class BulkNotificationMethodTests(BaseNotificationIntegrationTest):
def test_BulkNotificationMethod(self): def test_BulkNotificationMethod(self):
""" """
Ensure the implementation requirements are tested. 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): class WrongImplementation(BulkNotificationMethod):
@ -94,7 +94,7 @@ class SingleNotificationMethodTests(BaseNotificationIntegrationTest):
def test_SingleNotificationMethod(self): def test_SingleNotificationMethod(self):
""" """
Ensure the implementation requirements are tested. 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): class WrongImplementation(SingleNotificationMethod):

View File

@ -26,9 +26,10 @@ def trigger_event(event, *args, **kwargs):
if not settings.PLUGINS_ENABLED: if not settings.PLUGINS_ENABLED:
# Do nothing if plugins are not 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") logger.debug(f"Ignoring triggered event '{event}' - database not ready")
return return
@ -91,7 +92,7 @@ def process_event(plugin_slug, event, *args, **kwargs):
plugin = registry.plugins.get(plugin_slug, None) 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}'") logger.error(f"Could not find matching plugin for '{plugin_slug}'")
return return
@ -106,7 +107,7 @@ def allow_table_event(table_name):
if isImportingData(): if isImportingData():
# Prevent table events during the data import process # Prevent table events during the data import process
return False return False # pragma: no cover
table_name = table_name.lower().strip() table_name = table_name.lower().strip()

View File

@ -541,7 +541,7 @@ class PanelMixin:
def get_custom_panels(self, view, request): def get_custom_panels(self, view, request):
""" This method *must* be implemented by the plugin class """ """ 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): def get_panel_context(self, view, request, context):
""" """
@ -559,7 +559,7 @@ class PanelMixin:
try: try:
context['object'] = view.get_object() context['object'] = view.get_object()
except AttributeError: except AttributeError: # pragma: no cover
pass pass
return context return context

View File

@ -8,6 +8,7 @@ from error_report.models import Error
from InvenTree.helpers import InvenTreeTestCase from InvenTree.helpers import InvenTreeTestCase
from plugin import InvenTreePlugin from plugin import InvenTreePlugin
from plugin.base.integration.mixins import PanelMixin
from plugin.helpers import MixinNotImplementedError from plugin.helpers import MixinNotImplementedError
from plugin.mixins import (APICallMixin, AppMixin, NavigationMixin, from plugin.mixins import (APICallMixin, AppMixin, NavigationMixin,
SettingsMixin, UrlsMixin) SettingsMixin, UrlsMixin)
@ -324,7 +325,7 @@ class PanelMixinTests(InvenTreeTestCase):
urls = [ urls = [
reverse('part-detail', kwargs={'pk': 1}), reverse('part-detail', kwargs={'pk': 1}),
reverse('stock-item-detail', kwargs={'pk': 2}), 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) plugin.set_setting('ENABLE_HELLO_WORLD', False)
@ -379,3 +380,13 @@ class PanelMixinTests(InvenTreeTestCase):
# Assert that each request threw an error # Assert that each request threw an error
self.assertEqual(Error.objects.count(), n_errors + len(urls)) 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')

View File

@ -2,7 +2,7 @@
import logging import logging
from plugin.helpers import MixinImplementationError from plugin.helpers import MixinNotImplementedError
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
@ -58,7 +58,7 @@ class LocateMixin:
if item.in_stock and item.location is not None: if item.in_stock and item.location is not None:
self.locate_stock_location(item.location.pk) 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") logger.warning("LocateMixin: StockItem pk={item_pk} not found")
pass pass
@ -71,4 +71,4 @@ class LocateMixin:
Note: The default implementation here does nothing! Note: The default implementation here does nothing!
""" """
raise MixinImplementationError raise MixinNotImplementedError

View File

@ -5,7 +5,8 @@ Unit tests for the 'locate' plugin mixin class
from django.urls import reverse from django.urls import reverse
from InvenTree.api_tester import InvenTreeAPITestCase 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 from stock.models import StockItem, StockLocation
@ -145,3 +146,17 @@ class LocatePluginTests(InvenTreeAPITestCase):
# Item metadata should have been altered! # Item metadata should have been altered!
self.assertTrue(location.metadata['located']) 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)

View File

@ -2,6 +2,10 @@
Sample plugin which responds to events Sample plugin which responds to events
""" """
import warnings
from django.conf import settings
from plugin import InvenTreePlugin from plugin import InvenTreePlugin
from plugin.mixins import EventMixin from plugin.mixins import EventMixin
@ -21,3 +25,7 @@ class EventPluginSample(EventMixin, InvenTreePlugin):
print(f"Processing triggered event: '{event}'") print(f"Processing triggered event: '{event}'")
print("args:", str(args)) print("args:", str(args))
print("kwargs:", str(kwargs)) print("kwargs:", str(kwargs))
# Issue warning that we can test for
if settings.PLUGIN_TESTING:
warnings.warn(f'Event `{event}` triggered')

View 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')

View File

@ -37,7 +37,7 @@ class SampleLocatePlugin(LocateMixin, InvenTreePlugin):
# Tag metadata # Tag metadata
item.set_metadata('located', True) 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!") logger.error(f"StockItem ID {item_pk} does not exist!")
def locate_stock_location(self, location_pk): def locate_stock_location(self, location_pk):
@ -53,5 +53,5 @@ class SampleLocatePlugin(LocateMixin, InvenTreePlugin):
# Tag metadata # Tag metadata
location.set_metadata('located', True) 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!") logger.error(f"Location ID {location_pk} does not exist!")

View 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)