1# Copyright (C) 2017-2020 by the Free Software Foundation, Inc. 2# 3# This file is part of GNU Mailman. 4# 5# GNU Mailman is free software: you can redistribute it and/or modify it under 6# the terms of the GNU General Public License as published by the Free 7# Software Foundation, either version 3 of the License, or (at your option) 8# any later version. 9# 10# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT 11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13# more details. 14# 15# You should have received a copy of the GNU General Public License along with 16# GNU Mailman. If not, see <https://www.gnu.org/licenses/>. 17 18"""Test some additional plugin stuff.""" 19 20import sys 21import unittest 22 23from contextlib import ExitStack 24from mailman.interfaces.plugin import IPlugin 25from mailman.plugins.initialize import initialize 26from mailman.plugins.testing.layer import PluginRESTLayer 27from mailman.testing.helpers import call_api 28from mailman.testing.layers import ConfigLayer 29from tempfile import TemporaryDirectory 30from types import SimpleNamespace 31from unittest.mock import patch 32from urllib.error import HTTPError 33from zope.interface import implementer 34 35 36class TestRESTPlugin(unittest.TestCase): 37 layer = PluginRESTLayer 38 39 def test_plugin_raises_exception(self): 40 with self.assertRaises(HTTPError) as cm: 41 call_api('http://localhost:9001/3.1/plugins/example/no') 42 self.assertEqual(cm.exception.code, 400) 43 44 45@implementer(IPlugin) 46class TestablePlugin: 47 def pre_hook(self): 48 pass 49 50 def post_hook(self): 51 pass 52 53 resource = None 54 55 56class TestInitializePlugins(unittest.TestCase): 57 layer = ConfigLayer 58 59 def test_duplicate_plugin_name(self): 60 with ExitStack() as resources: 61 system_path = resources.enter_context(TemporaryDirectory()) 62 fake_plugin_config = { 63 'path': system_path, 64 'enabled': 'yes', 65 'class': 'ExamplePlugin', 66 } 67 log_mock = resources.enter_context( 68 patch('mailman.plugins.initialize.log')) 69 fake_mailman_config = SimpleNamespace( 70 plugin_configs=[('example', fake_plugin_config)], 71 plugins=['example'], 72 ) 73 resources.enter_context(patch( 74 'mailman.plugins.initialize.config', fake_mailman_config)) 75 initialize() 76 log_mock.error.assert_called_once_with( 77 'Duplicate plugin name: example') 78 79 def test_does_not_implement(self): 80 with ExitStack() as resources: 81 system_path = resources.enter_context(TemporaryDirectory()) 82 fake_plugin_config = { 83 'path': system_path, 84 'enabled': 'yes', 85 'class': 'ExamplePlugin', 86 } 87 log_mock = resources.enter_context( 88 patch('mailman.plugins.initialize.log')) 89 fake_mailman_config = SimpleNamespace( 90 plugin_configs=[('example', fake_plugin_config)], 91 plugins=[], 92 ) 93 resources.enter_context(patch( 94 'mailman.plugins.initialize.config', fake_mailman_config)) 95 resources.enter_context(patch( 96 'mailman.plugins.initialize.call_name', 97 # object() does not implement IPlugin. 98 return_value=object())) 99 initialize() 100 log_mock.error.assert_called_once_with( 101 'Plugin class does not implement IPlugin: ExamplePlugin') 102 self.assertNotIn(system_path, sys.path) 103 104 def test_adds_plugins_to_config(self): 105 with ExitStack() as resources: 106 system_path = resources.enter_context(TemporaryDirectory()) 107 fake_plugin_config = { 108 'path': system_path, 109 'enabled': 'yes', 110 'class': 'ExamplePlugin', 111 } 112 fake_mailman_config = SimpleNamespace( 113 plugin_configs=[('example', fake_plugin_config)], 114 plugins={}, 115 ) 116 resources.enter_context(patch( 117 'mailman.plugins.initialize.config', fake_mailman_config)) 118 testable_plugin = TestablePlugin() 119 resources.enter_context(patch( 120 'mailman.plugins.initialize.call_name', 121 # object() does not implement IPlugin. 122 return_value=testable_plugin)) 123 initialize() 124 self.assertIn('example', fake_mailman_config.plugins) 125 self.assertEqual( 126 fake_mailman_config.plugins['example'], 127 testable_plugin) 128 129 def test_not_enabled(self): 130 with ExitStack() as resources: 131 fake_plugin_config = { 132 'path': '/does/not/exist', 133 'enabled': 'no', 134 'class': 'ExamplePlugin', 135 } 136 log_mock = resources.enter_context( 137 patch('mailman.plugins.initialize.log')) 138 fake_mailman_config = SimpleNamespace( 139 plugin_configs=[('example', fake_plugin_config)], 140 plugins={}, 141 ) 142 resources.enter_context(patch( 143 'mailman.plugins.initialize.config', fake_mailman_config)) 144 initialize() 145 log_mock.info.assert_called_once_with( 146 'Plugin not enabled, or empty class path: example') 147