# pylint: disable=redefined-outer-name from pathlib import Path import pytest from shepherd.agent import plugin @pytest.fixture def simple_plugin(request): # Load a simple plugin as a custom plugin, using `./assets` as the plugin dir interface = plugin.load_plugin("simpletestplugin", Path(request.fspath.dirname)/'assets') return interface def test_simple_plugin_load(simple_plugin: plugin.PluginInterface): assert simple_plugin._plugin_name == "simpletestplugin" def test_simple_interface_function_load(simple_plugin: plugin.PluginInterface): # Check register_function() assert "my_interface_function" in simple_plugin._functions @pytest.fixture def simple_initialised_plugin(request): plugin.unload_plugin("simpletestplugin") interface = plugin.load_plugin("simpletestplugin", Path(request.fspath.dirname)/'assets') # The plugin system is _not_ responsible for making sure the config passed to it is valid. It # stores and provides the conf-spec, but it's up to Core.Agent to actually validate the # config - otherwise we wouldn't be able to do the whole multiple-layer-fallback thing before # actually initialising the plugin. # Therefore, part of the promise we make with the plugin interface is that the config we pass # in _will_ fit the config-spec template_config = interface.confspec.get_template() plugin.init_plugins({"simpletestplugin": template_config}) return interface def test_simple_plugin_init(simple_initialised_plugin): assert simple_initialised_plugin._plugin_name == "simpletestplugin" # Check registered init function has run assert simple_initialised_plugin.init_func_called is True def test_simple_interface_functions(simple_initialised_plugin): # Check module level function dict assert simple_initialised_plugin._functions["my_interface_function"]() == 42 # Check functions handed back to plugin assert simple_initialised_plugin.plugins["simpletestplugin"].my_interface_function() == 42 def test_simple_hook_attachments(simple_initialised_plugin): assert "basic_hook" in simple_initialised_plugin._hooks assert simple_initialised_plugin._hooks['basic_hook']( ) == {'simpletestplugin': "basic attachment"} assert simple_initialised_plugin.hooks.hook_with_args( 3, 7) == {'simpletestplugin': "attachment with args: 3, 7"} assert simple_initialised_plugin.hooks.hook_with_fancy_args( 2, 4) == {'simpletestplugin': "attachment with fancy args: 2, 4, True"} with pytest.raises(TypeError, match="takes 2 positional arguments but 3 were"): simple_initialised_plugin.hooks.hook_with_args(3, 7, 5) def test_dirty_plugin_load(request): """ Corner cases in plugin load """ interface = plugin.load_plugin("dirtytestplugin", Path(request.fspath.dirname)/'assets') # Should prefer the confspec actually registered, even if declared after assert "spec2" in interface.confspec.spec_dict @pytest.fixture def running_class_plugin(request): plugin.unload_plugin("classtestplugin") interface = plugin.load_plugin("classtestplugin", Path(request.fspath.dirname)/'assets') template_config = interface.confspec.get_template() plugin.init_plugins({"classtestplugin": template_config}) return interface def test_class_plugin_init(running_class_plugin): assert running_class_plugin._plugin_name == "classtestplugin" # Check plugin object init method has run assert running_class_plugin.init_method_called is True # Check registered init method has run assert running_class_plugin.init2_method_called is True def test_class_interface_functions(running_class_plugin): ifuncs = running_class_plugin.plugins["classtestplugin"] assert ifuncs.module_function(1) == "module func 1" assert ifuncs.instance_method(2) == "instance method 2" assert ifuncs.class_method(3) == "class method 3" assert ifuncs.static_method(4) == "static method 4" def test_class_hook_attachments(running_class_plugin): assert running_class_plugin._hooks.keys() == {"module_hook", "instance_hook", "static_hook", "static_hook2"} # Internal hooks dict assert running_class_plugin._hooks['module_hook'](1, 2) == {'classtestplugin': "module attachment 1 2"} # Interface hooks namespace assert running_class_plugin.hooks.instance_hook(3, 4) == {'classtestplugin': "instance attachment 3 4"} # Replaced attr in plugin object assert running_class_plugin._plugin_obj.static_hook(5, 6) == {'classtestplugin': "static attachment 5 6"} assert running_class_plugin.hooks.static_hook2(7, 8) == {'classtestplugin': "class attachment 7 8"} with pytest.raises(TypeError, match="takes 2 positional arguments but 3 were"): running_class_plugin.hooks.static_hook(3, 7, 5)