2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 03:56:43 +00:00
Matthias Mair 1c6e5f0f20
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
2022-05-24 09:23:06 +10:00

393 lines
13 KiB
Python

""" Unit tests for base mixins for plugins """
from django.conf import settings
from django.test import TestCase
from django.urls import include, re_path, reverse
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)
from plugin.registry import registry
from plugin.urls import PLUGIN_BASE
class BaseMixinDefinition:
def test_mixin_name(self):
# mixin name
self.assertIn(self.MIXIN_NAME, [item['key'] for item in self.mixin.registered_mixins])
# human name
self.assertIn(self.MIXIN_HUMAN_NAME, [item['human_name'] for item in self.mixin.registered_mixins])
class SettingsMixinTest(BaseMixinDefinition, InvenTreeTestCase):
MIXIN_HUMAN_NAME = 'Settings'
MIXIN_NAME = 'settings'
MIXIN_ENABLE_CHECK = 'has_settings'
TEST_SETTINGS = {'SETTING1': {'default': '123', }}
def setUp(self):
class SettingsCls(SettingsMixin, InvenTreePlugin):
SETTINGS = self.TEST_SETTINGS
self.mixin = SettingsCls()
class NoSettingsCls(SettingsMixin, InvenTreePlugin):
pass
self.mixin_nothing = NoSettingsCls()
super().setUp()
def test_function(self):
# settings variable
self.assertEqual(self.mixin.settings, self.TEST_SETTINGS)
# calling settings
# not existing
self.assertEqual(self.mixin.get_setting('ABCD'), '')
self.assertEqual(self.mixin_nothing.get_setting('ABCD'), '')
# right setting
self.mixin.set_setting('SETTING1', '12345', self.user)
self.assertEqual(self.mixin.get_setting('SETTING1'), '12345')
# no setting
self.assertEqual(self.mixin_nothing.get_setting(''), '')
class UrlsMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'URLs'
MIXIN_NAME = 'urls'
MIXIN_ENABLE_CHECK = 'has_urls'
def setUp(self):
class UrlsCls(UrlsMixin, InvenTreePlugin):
def test():
return 'ccc'
URLS = [re_path('testpath', test, name='test'), ]
self.mixin = UrlsCls()
class NoUrlsCls(UrlsMixin, InvenTreePlugin):
pass
self.mixin_nothing = NoUrlsCls()
def test_function(self):
plg_name = self.mixin.plugin_name()
# base_url
target_url = f'{PLUGIN_BASE}/{plg_name}/'
self.assertEqual(self.mixin.base_url, target_url)
# urlpattern
target_pattern = re_path(f'^{plg_name}/', include((self.mixin.urls, plg_name)), name=plg_name)
self.assertEqual(self.mixin.urlpatterns.reverse_dict, target_pattern.reverse_dict)
# resolve the view
self.assertEqual(self.mixin.urlpatterns.resolve('/testpath').func(), 'ccc')
self.assertEqual(self.mixin.urlpatterns.reverse('test'), 'testpath')
# no url
self.assertIsNone(self.mixin_nothing.urls)
self.assertIsNone(self.mixin_nothing.urlpatterns)
# internal name
self.assertEqual(self.mixin.internal_name, f'plugin:{self.mixin.slug}:')
class AppMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'App registration'
MIXIN_NAME = 'app'
MIXIN_ENABLE_CHECK = 'has_app'
def setUp(self):
class TestCls(AppMixin, InvenTreePlugin):
pass
self.mixin = TestCls()
def test_function(self):
# test that this plugin is in settings
self.assertIn('plugin.samples.integration', settings.INSTALLED_APPS)
class NavigationMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'Navigation Links'
MIXIN_NAME = 'navigation'
MIXIN_ENABLE_CHECK = 'has_naviation'
def setUp(self):
class NavigationCls(NavigationMixin, InvenTreePlugin):
NAVIGATION = [
{'name': 'aa', 'link': 'plugin:test:test_view'},
]
NAVIGATION_TAB_NAME = 'abcd1'
self.mixin = NavigationCls()
class NothingNavigationCls(NavigationMixin, InvenTreePlugin):
pass
self.nothing_mixin = NothingNavigationCls()
def test_function(self):
# check right configuration
self.assertEqual(self.mixin.navigation, [{'name': 'aa', 'link': 'plugin:test:test_view'}, ])
# navigation name
self.assertEqual(self.mixin.navigation_name, 'abcd1')
self.assertEqual(self.nothing_mixin.navigation_name, '')
def test_fail(self):
# check wrong links fails
with self.assertRaises(NotImplementedError):
class NavigationCls(NavigationMixin, InvenTreePlugin):
NAVIGATION = ['aa', 'aa']
NavigationCls()
class APICallMixinTest(BaseMixinDefinition, TestCase):
MIXIN_HUMAN_NAME = 'API calls'
MIXIN_NAME = 'api_call'
MIXIN_ENABLE_CHECK = 'has_api_call'
def setUp(self):
class MixinCls(APICallMixin, SettingsMixin, InvenTreePlugin):
NAME = "Sample API Caller"
SETTINGS = {
'API_TOKEN': {
'name': 'API Token',
'protected': True,
},
'API_URL': {
'name': 'External URL',
'description': 'Where is your API located?',
'default': 'reqres.in',
},
}
API_URL_SETTING = 'API_URL'
API_TOKEN_SETTING = 'API_TOKEN'
def get_external_url(self, simple: bool = True):
'''
returns data from the sample endpoint
'''
return self.api_call('api/users/2', simple_response=simple)
self.mixin = MixinCls()
class WrongCLS(APICallMixin, InvenTreePlugin):
pass
self.mixin_wrong = WrongCLS()
class WrongCLS2(APICallMixin, InvenTreePlugin):
API_URL_SETTING = 'test'
self.mixin_wrong2 = WrongCLS2()
def test_base_setup(self):
"""Test that the base settings work"""
# check init
self.assertTrue(self.mixin.has_api_call)
# api_url
self.assertEqual('https://reqres.in', self.mixin.api_url)
# api_headers
headers = self.mixin.api_headers
self.assertEqual(headers, {'Bearer': '', 'Content-Type': 'application/json'})
def test_args(self):
"""Test that building up args work"""
# api_build_url_args
# 1 arg
result = self.mixin.api_build_url_args({'a': 'b'})
self.assertEqual(result, '?a=b')
# more args
result = self.mixin.api_build_url_args({'a': 'b', 'c': 'd'})
self.assertEqual(result, '?a=b&c=d')
# list args
result = self.mixin.api_build_url_args({'a': 'b', 'c': ['d', 'e', 'f', ]})
self.assertEqual(result, '?a=b&c=d,e,f')
def test_api_call(self):
"""Test that api calls work"""
# api_call
result = self.mixin.get_external_url()
self.assertTrue(result)
self.assertIn('data', result,)
# api_call without json conversion
result = self.mixin.get_external_url(False)
self.assertTrue(result)
self.assertEqual(result.reason, 'OK')
# api_call with full url
result = self.mixin.api_call('https://reqres.in/api/users/2', endpoint_is_url=True)
self.assertTrue(result)
# api_call with post and data
result = self.mixin.api_call(
'api/users/',
data={"name": "morpheus", "job": "leader"},
method='POST'
)
self.assertTrue(result)
self.assertEqual(result['name'], 'morpheus')
# api_call with filter
result = self.mixin.api_call('api/users', url_args={'page': '2'})
self.assertTrue(result)
self.assertEqual(result['page'], 2)
def test_function_errors(self):
"""Test function errors"""
# wrongly defined plugins should not load
with self.assertRaises(MixinNotImplementedError):
self.mixin_wrong.has_api_call()
# cover wrong token setting
with self.assertRaises(MixinNotImplementedError):
self.mixin_wrong2.has_api_call()
class PanelMixinTests(InvenTreeTestCase):
"""Test that the PanelMixin plugin operates correctly"""
fixtures = [
'category',
'part',
'location',
'stock',
]
roles = 'all'
def test_installed(self):
"""Test that the sample panel plugin is installed"""
plugins = registry.with_mixin('panel')
self.assertTrue(len(plugins) > 0)
self.assertIn('samplepanel', [p.slug for p in plugins])
plugins = registry.with_mixin('panel', active=True)
self.assertEqual(len(plugins), 0)
def test_disabled(self):
"""Test that the panels *do not load* if the plugin is not enabled"""
plugin = registry.get_plugin('samplepanel')
plugin.set_setting('ENABLE_HELLO_WORLD', True)
plugin.set_setting('ENABLE_BROKEN_PANEL', True)
# Ensure that the plugin is *not* enabled
config = plugin.plugin_config()
self.assertFalse(config.active)
# Load some pages, ensure that the panel content is *not* loaded
for url in [
reverse('part-detail', kwargs={'pk': 1}),
reverse('stock-item-detail', kwargs={'pk': 2}),
reverse('stock-location-detail', kwargs={'pk': 1}),
]:
response = self.client.get(
url
)
self.assertEqual(response.status_code, 200)
# Test that these panels have *not* been loaded
self.assertNotIn('No Content', str(response.content))
self.assertNotIn('Hello world', str(response.content))
self.assertNotIn('Custom Part Panel', str(response.content))
def test_enabled(self):
"""
Test that the panels *do* load if the plugin is enabled
"""
plugin = registry.get_plugin('samplepanel')
self.assertEqual(len(registry.with_mixin('panel', active=True)), 0)
# Ensure that the plugin is enabled
config = plugin.plugin_config()
config.active = True
config.save()
self.assertTrue(config.active)
self.assertEqual(len(registry.with_mixin('panel', active=True)), 1)
# Load some pages, ensure that the panel content is *not* loaded
urls = [
reverse('part-detail', kwargs={'pk': 1}),
reverse('stock-item-detail', kwargs={'pk': 2}),
reverse('stock-location-detail', kwargs={'pk': 2}),
]
plugin.set_setting('ENABLE_HELLO_WORLD', False)
plugin.set_setting('ENABLE_BROKEN_PANEL', False)
for url in urls:
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn('No Content', str(response.content))
# This panel is disabled by plugin setting
self.assertNotIn('Hello world!', str(response.content))
# This panel is only active for the "Part" view
if url == urls[0]:
self.assertIn('Custom Part Panel', str(response.content))
else:
self.assertNotIn('Custom Part Panel', str(response.content))
# Enable the 'Hello World' panel
plugin.set_setting('ENABLE_HELLO_WORLD', True)
for url in urls:
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn('Hello world!', str(response.content))
# The 'Custom Part' panel should still be there, too
if url == urls[0]:
self.assertIn('Custom Part Panel', str(response.content))
else:
self.assertNotIn('Custom Part Panel', str(response.content))
# Enable the 'broken panel' setting - this will cause all panels to not render
plugin.set_setting('ENABLE_BROKEN_PANEL', True)
n_errors = Error.objects.count()
for url in urls:
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
# No custom panels should have been loaded
self.assertNotIn('No Content', str(response.content))
self.assertNotIn('Hello world!', str(response.content))
self.assertNotIn('Broken Panel', str(response.content))
self.assertNotIn('Custom Part Panel', str(response.content))
# 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')