/ letsencrypt / plugins / disco_test.py
disco_test.py
  1  """Tests for letsencrypt.plugins.disco."""
  2  import pkg_resources
  3  import unittest
  4  
  5  import mock
  6  import zope.interface
  7  
  8  from letsencrypt import errors
  9  from letsencrypt import interfaces
 10  
 11  from letsencrypt.plugins import standalone
 12  
 13  EP_SA = pkg_resources.EntryPoint(
 14      "sa", "letsencrypt.plugins.standalone",
 15      attrs=("Authenticator",),
 16      dist=mock.MagicMock(key="letsencrypt"))
 17  
 18  
 19  class PluginEntryPointTest(unittest.TestCase):
 20      """Tests for letsencrypt.plugins.disco.PluginEntryPoint."""
 21  
 22      def setUp(self):
 23          self.ep1 = pkg_resources.EntryPoint(
 24              "ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
 25          self.ep1prim = pkg_resources.EntryPoint(
 26              "ep1", "p2.ep2", dist=mock.MagicMock(key="p2"))
 27          # nested
 28          self.ep2 = pkg_resources.EntryPoint(
 29              "ep2", "p2.foo.ep2", dist=mock.MagicMock(key="p2"))
 30          # project name != top-level package name
 31          self.ep3 = pkg_resources.EntryPoint(
 32              "ep3", "a.ep3", dist=mock.MagicMock(key="p3"))
 33  
 34          from letsencrypt.plugins.disco import PluginEntryPoint
 35          self.plugin_ep = PluginEntryPoint(EP_SA)
 36  
 37      def test_entry_point_to_plugin_name(self):
 38          from letsencrypt.plugins.disco import PluginEntryPoint
 39  
 40          names = {
 41              self.ep1: "p1:ep1",
 42              self.ep1prim: "p2:ep1",
 43              self.ep2: "p2:ep2",
 44              self.ep3: "p3:ep3",
 45              EP_SA: "sa",
 46          }
 47  
 48          for entry_point, name in names.iteritems():
 49              self.assertEqual(
 50                  name, PluginEntryPoint.entry_point_to_plugin_name(entry_point))
 51  
 52      def test_description(self):
 53          self.assertEqual("Standalone Authenticator", self.plugin_ep.description)
 54  
 55      def test_description_with_name(self):
 56          self.plugin_ep.plugin_cls = mock.MagicMock(description="Desc")
 57          self.assertEqual(
 58              "Desc (sa)", self.plugin_ep.description_with_name)
 59  
 60      def test_ifaces(self):
 61          self.assertTrue(self.plugin_ep.ifaces((interfaces.IAuthenticator,)))
 62          self.assertFalse(self.plugin_ep.ifaces((interfaces.IInstaller,)))
 63          self.assertFalse(self.plugin_ep.ifaces((
 64              interfaces.IInstaller, interfaces.IAuthenticator)))
 65  
 66      def test__init__(self):
 67          self.assertFalse(self.plugin_ep.initialized)
 68          self.assertFalse(self.plugin_ep.prepared)
 69          self.assertFalse(self.plugin_ep.misconfigured)
 70          self.assertFalse(self.plugin_ep.available)
 71          self.assertTrue(self.plugin_ep.problem is None)
 72          self.assertTrue(self.plugin_ep.entry_point is EP_SA)
 73          self.assertEqual("sa", self.plugin_ep.name)
 74  
 75          self.assertTrue(self.plugin_ep.plugin_cls is standalone.Authenticator)
 76  
 77      def test_init(self):
 78          config = mock.MagicMock()
 79          plugin = self.plugin_ep.init(config=config)
 80          self.assertTrue(self.plugin_ep.initialized)
 81          self.assertTrue(plugin.config is config)
 82          # memoize!
 83          self.assertTrue(self.plugin_ep.init() is plugin)
 84          self.assertTrue(plugin.config is config)
 85          # try to give different config
 86          self.assertTrue(self.plugin_ep.init(123) is plugin)
 87          self.assertTrue(plugin.config is config)
 88  
 89          self.assertFalse(self.plugin_ep.prepared)
 90          self.assertFalse(self.plugin_ep.misconfigured)
 91          self.assertFalse(self.plugin_ep.available)
 92  
 93      def test_verify(self):
 94          iface1 = mock.MagicMock(__name__="iface1")
 95          iface2 = mock.MagicMock(__name__="iface2")
 96          iface3 = mock.MagicMock(__name__="iface3")
 97          # pylint: disable=protected-access
 98          self.plugin_ep._initialized = plugin = mock.MagicMock()
 99  
100          exceptions = zope.interface.exceptions
101          with mock.patch("letsencrypt.plugins."
102                          "disco.zope.interface") as mock_zope:
103              mock_zope.exceptions = exceptions
104  
105              def verify_object(iface, obj):  # pylint: disable=missing-docstring
106                  assert obj is plugin
107                  assert iface is iface1 or iface is iface2 or iface is iface3
108                  if iface is iface3:
109                      raise mock_zope.exceptions.BrokenImplementation(None, None)
110              mock_zope.verify.verifyObject.side_effect = verify_object
111              self.assertTrue(self.plugin_ep.verify((iface1,)))
112              self.assertTrue(self.plugin_ep.verify((iface1, iface2)))
113              self.assertFalse(self.plugin_ep.verify((iface3,)))
114              self.assertFalse(self.plugin_ep.verify((iface1, iface3)))
115  
116      def test_prepare(self):
117          config = mock.MagicMock()
118          self.plugin_ep.init(config=config)
119          self.plugin_ep.prepare()
120          self.assertTrue(self.plugin_ep.prepared)
121          self.assertFalse(self.plugin_ep.misconfigured)
122  
123          # output doesn't matter that much, just test if it runs
124          str(self.plugin_ep)
125  
126      def test_prepare_misconfigured(self):
127          plugin = mock.MagicMock()
128          plugin.prepare.side_effect = errors.MisconfigurationError
129          # pylint: disable=protected-access
130          self.plugin_ep._initialized = plugin
131          self.assertTrue(isinstance(self.plugin_ep.prepare(),
132                                     errors.MisconfigurationError))
133          self.assertTrue(self.plugin_ep.prepared)
134          self.assertTrue(self.plugin_ep.misconfigured)
135          self.assertTrue(isinstance(self.plugin_ep.problem,
136                                     errors.MisconfigurationError))
137          self.assertTrue(self.plugin_ep.available)
138  
139      def test_prepare_no_installation(self):
140          plugin = mock.MagicMock()
141          plugin.prepare.side_effect = errors.NoInstallationError
142          # pylint: disable=protected-access
143          self.plugin_ep._initialized = plugin
144          self.assertTrue(isinstance(self.plugin_ep.prepare(),
145                                     errors.NoInstallationError))
146          self.assertTrue(self.plugin_ep.prepared)
147          self.assertFalse(self.plugin_ep.misconfigured)
148          self.assertFalse(self.plugin_ep.available)
149  
150      def test_prepare_generic_plugin_error(self):
151          plugin = mock.MagicMock()
152          plugin.prepare.side_effect = errors.PluginError
153          # pylint: disable=protected-access
154          self.plugin_ep._initialized = plugin
155          self.assertTrue(isinstance(self.plugin_ep.prepare(), errors.PluginError))
156          self.assertTrue(self.plugin_ep.prepared)
157          self.assertFalse(self.plugin_ep.misconfigured)
158          self.assertFalse(self.plugin_ep.available)
159  
160      def test_repr(self):
161          self.assertEqual("PluginEntryPoint#sa", repr(self.plugin_ep))
162  
163  
164  class PluginsRegistryTest(unittest.TestCase):
165      """Tests for letsencrypt.plugins.disco.PluginsRegistry."""
166  
167      def setUp(self):
168          from letsencrypt.plugins.disco import PluginsRegistry
169          self.plugin_ep = mock.MagicMock(name="mock")
170          self.plugin_ep.__hash__.side_effect = TypeError
171          self.plugins = {"mock": self.plugin_ep}
172          self.reg = PluginsRegistry(self.plugins)
173  
174      def test_find_all(self):
175          from letsencrypt.plugins.disco import PluginsRegistry
176          with mock.patch("letsencrypt.plugins.disco.pkg_resources") as mock_pkg:
177              mock_pkg.iter_entry_points.return_value = iter([EP_SA])
178              plugins = PluginsRegistry.find_all()
179          self.assertTrue(plugins["sa"].plugin_cls is standalone.Authenticator)
180          self.assertTrue(plugins["sa"].entry_point is EP_SA)
181  
182      def test_getitem(self):
183          self.assertEqual(self.plugin_ep, self.reg["mock"])
184  
185      def test_iter(self):
186          self.assertEqual(["mock"], list(self.reg))
187  
188      def test_len(self):
189          self.assertEqual(1, len(self.reg))
190          self.plugins.clear()
191          self.assertEqual(0, len(self.reg))
192  
193      def test_init(self):
194          self.plugin_ep.init.return_value = "baz"
195          self.assertEqual(["baz"], self.reg.init("bar"))
196          self.plugin_ep.init.assert_called_once_with("bar")
197  
198      def test_filter(self):
199          self.plugins.update({
200              "foo": "bar",
201              "bar": "foo",
202              "baz": "boo",
203          })
204          self.assertEqual(
205              {"foo": "bar", "baz": "boo"},
206              self.reg.filter(lambda p_ep: str(p_ep).startswith("b")))
207  
208      def test_ifaces(self):
209          self.plugin_ep.ifaces.return_value = True
210          # pylint: disable=protected-access
211          self.assertEqual(self.plugins, self.reg.ifaces()._plugins)
212          self.plugin_ep.ifaces.return_value = False
213          self.assertEqual({}, self.reg.ifaces()._plugins)
214  
215      def test_verify(self):
216          self.plugin_ep.verify.return_value = True
217          # pylint: disable=protected-access
218          self.assertEqual(
219              self.plugins, self.reg.verify(mock.MagicMock())._plugins)
220          self.plugin_ep.verify.return_value = False
221          self.assertEqual({}, self.reg.verify(mock.MagicMock())._plugins)
222  
223      def test_prepare(self):
224          self.plugin_ep.prepare.return_value = "baz"
225          self.assertEqual(["baz"], self.reg.prepare())
226          self.plugin_ep.prepare.assert_called_once_with()
227  
228      def test_available(self):
229          self.plugin_ep.available = True
230          # pylint: disable=protected-access
231          self.assertEqual(self.plugins, self.reg.available()._plugins)
232          self.plugin_ep.available = False
233          self.assertEqual({}, self.reg.available()._plugins)
234  
235      def test_find_init(self):
236          self.assertTrue(self.reg.find_init(mock.Mock()) is None)
237          self.plugin_ep.initalized = True
238          self.assertTrue(
239              self.reg.find_init(self.plugin_ep.init()) is self.plugin_ep)
240  
241      def test_repr(self):
242          self.plugin_ep.__repr__ = lambda _: "PluginEntryPoint#mock"
243          self.assertEqual("PluginsRegistry(PluginEntryPoint#mock)",
244                           repr(self.reg))
245  
246      def test_str(self):
247          self.plugin_ep.__str__ = lambda _: "Mock"
248          self.plugins["foo"] = "Mock"
249          self.assertEqual("Mock\n\nMock", str(self.reg))
250          self.plugins.clear()
251          self.assertEqual("No plugins", str(self.reg))
252  
253  
254  if __name__ == "__main__":
255      unittest.main()  # pragma: no cover