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_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?

View File

@ -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):

View File

@ -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()

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

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

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
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!")

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)