mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Merge pull request #2928 from matmair/coverage-adaption
Increase coverage
This commit is contained in:
commit
b65a969433
@ -35,7 +35,7 @@ def _convert_model(apps, line_item_ref, extra_line_ref, price_ref):
|
|||||||
print(f'Done converting line items - now at {OrderExtraLine.objects.all().count()} {extra_line_ref} / {OrderLineItem.objects.all().count()} {line_item_ref} instance(s)')
|
print(f'Done converting line items - now at {OrderExtraLine.objects.all().count()} {extra_line_ref} / {OrderLineItem.objects.all().count()} {line_item_ref} instance(s)')
|
||||||
|
|
||||||
|
|
||||||
def _reconvert_model(apps, line_item_ref, extra_line_ref):
|
def _reconvert_model(apps, line_item_ref, extra_line_ref): # pragma: no cover
|
||||||
"""Convert ExtraLine instances back to OrderLineItem instances"""
|
"""Convert ExtraLine instances back to OrderLineItem instances"""
|
||||||
OrderLineItem = apps.get_model('order', line_item_ref)
|
OrderLineItem = apps.get_model('order', line_item_ref)
|
||||||
OrderExtraLine = apps.get_model('order', extra_line_ref)
|
OrderExtraLine = apps.get_model('order', extra_line_ref)
|
||||||
|
@ -35,7 +35,7 @@ class PluginAppConfig(AppConfig):
|
|||||||
if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP', create=False):
|
if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP', create=False):
|
||||||
# make sure all plugins are installed
|
# make sure all plugins are installed
|
||||||
registry.install_plugin_file()
|
registry.install_plugin_file()
|
||||||
except:
|
except: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# get plugins and init them
|
# get plugins and init them
|
||||||
|
@ -69,7 +69,7 @@ class BarcodeMixin:
|
|||||||
Default implementation returns None
|
Default implementation returns None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return None
|
return None # pragma: no cover
|
||||||
|
|
||||||
def getStockItemByHash(self):
|
def getStockItemByHash(self):
|
||||||
"""
|
"""
|
||||||
@ -97,7 +97,7 @@ class BarcodeMixin:
|
|||||||
Default implementation returns None
|
Default implementation returns None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return None
|
return None # pragma: no cover
|
||||||
|
|
||||||
def renderStockLocation(self, loc):
|
def renderStockLocation(self, loc):
|
||||||
"""
|
"""
|
||||||
@ -113,7 +113,7 @@ class BarcodeMixin:
|
|||||||
Default implementation returns None
|
Default implementation returns None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return None
|
return None # pragma: no cover
|
||||||
|
|
||||||
def renderPart(self, part):
|
def renderPart(self, part):
|
||||||
"""
|
"""
|
||||||
@ -143,4 +143,4 @@ class BarcodeMixin:
|
|||||||
"""
|
"""
|
||||||
Default implementation returns False
|
Default implementation returns False
|
||||||
"""
|
"""
|
||||||
return False
|
return False # pragma: no cover
|
||||||
|
@ -56,7 +56,7 @@ class SettingsMixin:
|
|||||||
|
|
||||||
if not plugin:
|
if not plugin:
|
||||||
# Cannot find associated plugin model, return
|
# Cannot find associated plugin model, return
|
||||||
return
|
return # pragma: no cover
|
||||||
|
|
||||||
PluginSetting.set_setting(key, value, user, plugin=plugin)
|
PluginSetting.set_setting(key, value, user, plugin=plugin)
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ class ScheduleMixin:
|
|||||||
|
|
||||||
if Schedule.objects.filter(name=task_name).exists():
|
if Schedule.objects.filter(name=task_name).exists():
|
||||||
# Scheduled task already exists - continue!
|
# Scheduled task already exists - continue!
|
||||||
continue
|
continue # pragma: no cover
|
||||||
|
|
||||||
logger.info(f"Adding scheduled task '{task_name}'")
|
logger.info(f"Adding scheduled task '{task_name}'")
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ class ScheduleMixin:
|
|||||||
repeats=task.get('repeats', -1),
|
repeats=task.get('repeats', -1),
|
||||||
)
|
)
|
||||||
|
|
||||||
except (ProgrammingError, OperationalError):
|
except (ProgrammingError, OperationalError): # pragma: no cover
|
||||||
# Database might not yet be ready
|
# Database might not yet be ready
|
||||||
logger.warning("register_tasks failed, database not ready")
|
logger.warning("register_tasks failed, database not ready")
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ class ScheduleMixin:
|
|||||||
scheduled_task.delete()
|
scheduled_task.delete()
|
||||||
except Schedule.DoesNotExist:
|
except Schedule.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
except (ProgrammingError, OperationalError):
|
except (ProgrammingError, OperationalError): # pragma: no cover
|
||||||
# Database might not yet be ready
|
# Database might not yet be ready
|
||||||
logger.warning("unregister_tasks failed, database not ready")
|
logger.warning("unregister_tasks failed, database not ready")
|
||||||
|
|
||||||
@ -408,7 +408,7 @@ class LabelPrintingMixin:
|
|||||||
"""
|
"""
|
||||||
MIXIN_NAME = 'Label printing'
|
MIXIN_NAME = 'Label printing'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self): # pragma: no cover
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.add_mixin('labels', True, __class__)
|
self.add_mixin('labels', True, __class__)
|
||||||
|
|
||||||
@ -426,7 +426,7 @@ class LabelPrintingMixin:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Unimplemented (to be implemented by the particular plugin class)
|
# Unimplemented (to be implemented by the particular plugin class)
|
||||||
...
|
... # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
class APICallMixin:
|
class APICallMixin:
|
||||||
|
@ -75,7 +75,7 @@ def handle_error(error, do_raise: bool = True, do_log: bool = True, log_name: st
|
|||||||
path_parts.remove('plugin')
|
path_parts.remove('plugin')
|
||||||
path_parts.pop(0)
|
path_parts.pop(0)
|
||||||
else:
|
else:
|
||||||
path_parts.remove('plugins')
|
path_parts.remove('plugins') # pragma: no cover
|
||||||
|
|
||||||
package_name = '.'.join(path_parts)
|
package_name = '.'.join(path_parts)
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ def handle_error(error, do_raise: bool = True, do_log: bool = True, log_name: st
|
|||||||
if do_raise:
|
if do_raise:
|
||||||
# do a straight raise if we are playing with enviroment variables at execution time, ignore the broken sample
|
# do a straight raise if we are playing with enviroment variables at execution time, ignore the broken sample
|
||||||
if settings.TESTING_ENV and package_name != 'integration.broken_sample' and isinstance(error, IntegrityError):
|
if settings.TESTING_ENV and package_name != 'integration.broken_sample' and isinstance(error, IntegrityError):
|
||||||
raise error
|
raise error # pragma: no cover
|
||||||
raise IntegrationPluginError(package_name, str(error))
|
raise IntegrationPluginError(package_name, str(error))
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ def check_git_version():
|
|||||||
except ValueError: # pragma: no cover
|
except ValueError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return False
|
return False # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
class GitStatus:
|
class GitStatus:
|
||||||
|
@ -191,7 +191,7 @@ class IntegrationPluginBase(MixinBase, plugin_base.InvenTreePluginBase):
|
|||||||
Path to the plugin
|
Path to the plugin
|
||||||
"""
|
"""
|
||||||
if self._is_package:
|
if self._is_package:
|
||||||
return self.__module__
|
return self.__module__ # pragma: no cover
|
||||||
return pathlib.Path(self.def_path).relative_to(settings.BASE_DIR)
|
return pathlib.Path(self.def_path).relative_to(settings.BASE_DIR)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -283,7 +283,7 @@ class PluginsRegistry:
|
|||||||
if not settings.PLUGIN_TESTING:
|
if not settings.PLUGIN_TESTING:
|
||||||
raise error # pragma: no cover
|
raise error # pragma: no cover
|
||||||
plugin_db_setting = None
|
plugin_db_setting = None
|
||||||
except (IntegrityError) as error:
|
except (IntegrityError) as error: # pragma: no cover
|
||||||
logger.error(f"Error initializing plugin: {error}")
|
logger.error(f"Error initializing plugin: {error}")
|
||||||
|
|
||||||
# Always activate if testing
|
# Always activate if testing
|
||||||
@ -322,7 +322,7 @@ class PluginsRegistry:
|
|||||||
self.plugins[plugin.slug] = plugin
|
self.plugins[plugin.slug] = plugin
|
||||||
else:
|
else:
|
||||||
# save for later reference
|
# save for later reference
|
||||||
self.plugins_inactive[plug_key] = plugin_db_setting
|
self.plugins_inactive[plug_key] = plugin_db_setting # pragma: no cover
|
||||||
|
|
||||||
def _activate_plugins(self, force_reload=False):
|
def _activate_plugins(self, force_reload=False):
|
||||||
"""
|
"""
|
||||||
@ -411,7 +411,7 @@ class PluginsRegistry:
|
|||||||
deleted_count += 1
|
deleted_count += 1
|
||||||
|
|
||||||
if deleted_count > 0:
|
if deleted_count > 0:
|
||||||
logger.info(f"Removed {deleted_count} old scheduled tasks")
|
logger.info(f"Removed {deleted_count} old scheduled tasks") # pragma: no cover
|
||||||
except (ProgrammingError, OperationalError):
|
except (ProgrammingError, OperationalError):
|
||||||
# Database might not yet be ready
|
# Database might not yet be ready
|
||||||
logger.warning("activate_integration_schedule failed, database not ready")
|
logger.warning("activate_integration_schedule failed, database not ready")
|
||||||
|
@ -8,11 +8,11 @@ from plugin.mixins import ScheduleMixin, SettingsMixin
|
|||||||
|
|
||||||
# Define some simple tasks to perform
|
# Define some simple tasks to perform
|
||||||
def print_hello():
|
def print_hello():
|
||||||
print("Hello")
|
print("Hello") # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def print_world():
|
def print_world():
|
||||||
print("World")
|
print("World") # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, IntegrationPluginBase):
|
class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, IntegrationPluginBase):
|
||||||
@ -36,7 +36,7 @@ class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, IntegrationPluginBase):
|
|||||||
'minutes': 45,
|
'minutes': 45,
|
||||||
},
|
},
|
||||||
'world': {
|
'world': {
|
||||||
'func': 'plugin.samples.integration.scheduled_task.print_hello',
|
'func': 'plugin.samples.integration.scheduled_task.print_world',
|
||||||
'schedule': 'H',
|
'schedule': 'H',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -58,3 +58,4 @@ class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, IntegrationPluginBase):
|
|||||||
t_or_f = self.get_setting('T_OR_F')
|
t_or_f = self.get_setting('T_OR_F')
|
||||||
|
|
||||||
print(f"Called member_func - value is {t_or_f}")
|
print(f"Called member_func - value is {t_or_f}")
|
||||||
|
return t_or_f
|
||||||
|
153
InvenTree/plugin/samples/integration/test_scheduled_task.py
Normal file
153
InvenTree/plugin/samples/integration/test_scheduled_task.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
""" Unit tests for scheduled tasks"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from plugin import registry, IntegrationPluginBase
|
||||||
|
from plugin.helpers import MixinImplementationError
|
||||||
|
from plugin.registry import call_function
|
||||||
|
from plugin.mixins import ScheduleMixin
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleScheduledTaskPluginTests(TestCase):
|
||||||
|
""" Tests for provided ScheduledTaskPlugin """
|
||||||
|
|
||||||
|
def test_function(self):
|
||||||
|
"""check if the scheduling works"""
|
||||||
|
# The plugin should be defined
|
||||||
|
self.assertIn('schedule', registry.plugins)
|
||||||
|
plg = registry.plugins['schedule']
|
||||||
|
self.assertTrue(plg)
|
||||||
|
|
||||||
|
# check that the built-in function is running
|
||||||
|
self.assertEqual(plg.member_func(), False)
|
||||||
|
|
||||||
|
# check that the tasks are defined
|
||||||
|
self.assertEqual(plg.get_task_names(), ['plugin.schedule.member', 'plugin.schedule.hello', 'plugin.schedule.world'])
|
||||||
|
|
||||||
|
# register
|
||||||
|
plg.register_tasks()
|
||||||
|
# check that schedule was registers
|
||||||
|
from django_q.models import Schedule
|
||||||
|
scheduled_plugin_tasks = Schedule.objects.filter(name__istartswith="plugin.")
|
||||||
|
self.assertEqual(len(scheduled_plugin_tasks), 3)
|
||||||
|
|
||||||
|
# delete middle task
|
||||||
|
# this is to check the system also deals with disappearing tasks
|
||||||
|
scheduled_plugin_tasks[1].delete()
|
||||||
|
# there should be one less now
|
||||||
|
scheduled_plugin_tasks = Schedule.objects.filter(name__istartswith="plugin.")
|
||||||
|
self.assertEqual(len(scheduled_plugin_tasks), 2)
|
||||||
|
|
||||||
|
# test unregistering
|
||||||
|
plg.unregister_tasks()
|
||||||
|
scheduled_plugin_tasks = Schedule.objects.filter(name__istartswith="plugin.")
|
||||||
|
self.assertEqual(len(scheduled_plugin_tasks), 0)
|
||||||
|
|
||||||
|
def test_calling(self):
|
||||||
|
"""check if a function can be called without errors"""
|
||||||
|
self.assertEqual(call_function('schedule', 'member_func'), False)
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduledTaskPluginTests(TestCase):
|
||||||
|
""" Tests for ScheduledTaskPluginTests mixin base """
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
"""Check that all MixinImplementationErrors raise"""
|
||||||
|
class Base(ScheduleMixin, IntegrationPluginBase):
|
||||||
|
PLUGIN_NAME = 'APlugin'
|
||||||
|
|
||||||
|
class NoSchedules(Base):
|
||||||
|
"""Plugin without schedules"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertRaises(MixinImplementationError):
|
||||||
|
NoSchedules()
|
||||||
|
|
||||||
|
class WrongFuncSchedules(Base):
|
||||||
|
"""
|
||||||
|
Plugin with broken functions
|
||||||
|
|
||||||
|
This plugin is missing a func
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCHEDULED_TASKS = {
|
||||||
|
'test': {
|
||||||
|
'schedule': 'I',
|
||||||
|
'minutes': 30,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
pass # pragma: no cover
|
||||||
|
|
||||||
|
with self.assertRaises(MixinImplementationError):
|
||||||
|
WrongFuncSchedules()
|
||||||
|
|
||||||
|
class WrongFuncSchedules1(WrongFuncSchedules):
|
||||||
|
"""
|
||||||
|
Plugin with broken functions
|
||||||
|
|
||||||
|
This plugin is missing a schedule
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCHEDULED_TASKS = {
|
||||||
|
'test': {
|
||||||
|
'func': 'test',
|
||||||
|
'minutes': 30,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(MixinImplementationError):
|
||||||
|
WrongFuncSchedules1()
|
||||||
|
|
||||||
|
class WrongFuncSchedules2(WrongFuncSchedules):
|
||||||
|
"""
|
||||||
|
Plugin with broken functions
|
||||||
|
|
||||||
|
This plugin is missing a schedule
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCHEDULED_TASKS = {
|
||||||
|
'test': {
|
||||||
|
'func': 'test',
|
||||||
|
'minutes': 30,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(MixinImplementationError):
|
||||||
|
WrongFuncSchedules2()
|
||||||
|
|
||||||
|
class WrongFuncSchedules3(WrongFuncSchedules):
|
||||||
|
"""
|
||||||
|
Plugin with broken functions
|
||||||
|
|
||||||
|
This plugin has a broken schedule
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCHEDULED_TASKS = {
|
||||||
|
'test': {
|
||||||
|
'func': 'test',
|
||||||
|
'schedule': 'XX',
|
||||||
|
'minutes': 30,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(MixinImplementationError):
|
||||||
|
WrongFuncSchedules3()
|
||||||
|
|
||||||
|
class WrongFuncSchedules4(WrongFuncSchedules):
|
||||||
|
"""
|
||||||
|
Plugin with broken functions
|
||||||
|
|
||||||
|
This plugin is missing a minute marker for its schedule
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCHEDULED_TASKS = {
|
||||||
|
'test': {
|
||||||
|
'func': 'test',
|
||||||
|
'schedule': 'I',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(MixinImplementationError):
|
||||||
|
WrongFuncSchedules4()
|
@ -11,6 +11,8 @@ from plugin import IntegrationPluginBase
|
|||||||
from plugin.mixins import AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, APICallMixin
|
from plugin.mixins import AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, APICallMixin
|
||||||
from plugin.urls import PLUGIN_BASE
|
from plugin.urls import PLUGIN_BASE
|
||||||
|
|
||||||
|
from plugin.samples.integration.sample import SampleIntegrationPlugin
|
||||||
|
|
||||||
|
|
||||||
class BaseMixinDefinition:
|
class BaseMixinDefinition:
|
||||||
def test_mixin_name(self):
|
def test_mixin_name(self):
|
||||||
@ -238,6 +240,7 @@ class IntegrationPluginBaseTests(TestCase):
|
|||||||
LICENSE = 'MIT'
|
LICENSE = 'MIT'
|
||||||
|
|
||||||
self.plugin_name = NameIntegrationPluginBase()
|
self.plugin_name = NameIntegrationPluginBase()
|
||||||
|
self.plugin_sample = SampleIntegrationPlugin()
|
||||||
|
|
||||||
def test_action_name(self):
|
def test_action_name(self):
|
||||||
"""check the name definition possibilities"""
|
"""check the name definition possibilities"""
|
||||||
@ -246,6 +249,10 @@ class IntegrationPluginBaseTests(TestCase):
|
|||||||
self.assertEqual(self.plugin_simple.plugin_name(), 'SimplePlugin')
|
self.assertEqual(self.plugin_simple.plugin_name(), 'SimplePlugin')
|
||||||
self.assertEqual(self.plugin_name.plugin_name(), 'Aplugin')
|
self.assertEqual(self.plugin_name.plugin_name(), 'Aplugin')
|
||||||
|
|
||||||
|
# is_sampe
|
||||||
|
self.assertEqual(self.plugin.is_sample, False)
|
||||||
|
self.assertEqual(self.plugin_sample.is_sample, True)
|
||||||
|
|
||||||
# slug
|
# slug
|
||||||
self.assertEqual(self.plugin.slug, '')
|
self.assertEqual(self.plugin.slug, '')
|
||||||
self.assertEqual(self.plugin_simple.slug, 'simpleplugin')
|
self.assertEqual(self.plugin_simple.slug, 'simpleplugin')
|
||||||
|
@ -31,6 +31,10 @@ class InvenTreePluginTests(TestCase):
|
|||||||
self.assertEqual(self.named_plugin.PLUGIN_NAME, 'abc123')
|
self.assertEqual(self.named_plugin.PLUGIN_NAME, 'abc123')
|
||||||
self.assertEqual(self.named_plugin.plugin_name(), 'abc123')
|
self.assertEqual(self.named_plugin.plugin_name(), 'abc123')
|
||||||
|
|
||||||
|
def test_basic_is_active(self):
|
||||||
|
"""check if a basic plugin is active"""
|
||||||
|
self.assertEqual(self.plugin.is_active(), False)
|
||||||
|
|
||||||
|
|
||||||
class PluginTagTests(TestCase):
|
class PluginTagTests(TestCase):
|
||||||
""" Tests for the plugin extras """
|
""" Tests for the plugin extras """
|
||||||
|
@ -564,14 +564,14 @@ class Owner(models.Model):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
owners.append(cls.objects.get(owner_id=user.pk, owner_type=user_type))
|
owners.append(cls.objects.get(owner_id=user.pk, owner_type=user_type))
|
||||||
except:
|
except: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for group in user.groups.all():
|
for group in user.groups.all():
|
||||||
try:
|
try:
|
||||||
owner = cls.objects.get(owner_id=group.pk, owner_type=group_type)
|
owner = cls.objects.get(owner_id=group.pk, owner_type=group_type)
|
||||||
owners.append(owner)
|
owners.append(owner)
|
||||||
except:
|
except: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return owners
|
return owners
|
||||||
|
@ -197,6 +197,10 @@ class OwnerModelTest(TestCase):
|
|||||||
self.assertTrue(user_as_owner in related_owners)
|
self.assertTrue(user_as_owner in related_owners)
|
||||||
self.assertTrue(group_as_owner in related_owners)
|
self.assertTrue(group_as_owner in related_owners)
|
||||||
|
|
||||||
|
# Check owner matching
|
||||||
|
owners = Owner.get_owners_matching_user(self.user)
|
||||||
|
self.assertEqual(owners, [user_as_owner, group_as_owner])
|
||||||
|
|
||||||
# Delete user and verify owner was deleted too
|
# Delete user and verify owner was deleted too
|
||||||
self.user.delete()
|
self.user.delete()
|
||||||
user_as_owner = Owner.get_owner(self.user)
|
user_as_owner = Owner.get_owner(self.user)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user