← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~allenap/maas/use-rabbit-fixture into lp:maas

 

Gavin Panella has proposed merging lp:~allenap/maas/use-rabbit-fixture into lp:maas.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~allenap/maas/use-rabbit-fixture/+merge/97932
-- 
https://code.launchpad.net/~allenap/maas/use-rabbit-fixture/+merge/97932
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/maas/use-rabbit-fixture into lp:maas.
=== modified file 'src/maasserver/tests/__init__.py'
--- src/maasserver/tests/__init__.py	2012-01-23 12:42:42 +0000
+++ src/maasserver/tests/__init__.py	2012-03-16 17:23:36 +0000
@@ -1,18 +0,0 @@
-# Copyright 2012 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-from __future__ import (
-    print_function,
-    unicode_literals,
-    )
-
-__metaclass__ = type
-__all__ = []
-
-from os.path import dirname
-
-from django.utils.unittest import TestLoader
-
-
-def suite():
-    return TestLoader().discover(dirname(__file__))

=== modified file 'src/maasserver/tests/test_views.py'
--- src/maasserver/tests/test_views.py	2012-03-16 16:16:11 +0000
+++ src/maasserver/tests/test_views.py	2012-03-16 17:23:36 +0000
@@ -43,6 +43,7 @@
     get_yui_location,
     proxy_to_longpoll,
     )
+from maastesting.rabbit import uses_rabbit
 
 
 def get_prefixed_form_data(prefix, data):
@@ -237,6 +238,7 @@
         self.patch(views, 'messaging', get_messaging())
         self.assertEqual({}, get_longpoll_context())
 
+    @uses_rabbit
     def test_get_longpoll_context(self):
         longpoll = factory.getRandomString()
         self.patch(settings, 'LONGPOLL_PATH', longpoll)

=== modified file 'src/maastesting/rabbit.py'
--- src/maastesting/rabbit.py	2012-01-24 12:44:48 +0000
+++ src/maastesting/rabbit.py	2012-03-16 17:23:36 +0000
@@ -10,56 +10,87 @@
 
 __metaclass__ = type
 __all__ = [
-    "RabbitServerResource",
+    "get_rabbit",
+    "RabbitServerSettings",
+    "start_rabbit",
+    "stop_rabbit",
+    "use_rabbit",
+    "uses_rabbit",
     ]
 
+from functools import wraps
+
+from fixtures import Fixture
 from rabbitfixture.server import RabbitServer
-from testresources import TestResource
-
-
-class RabbitServerResource(TestResource):
-    """A `TestResource` that wraps a `RabbitServer`.
-
-    :ivar server: A `RabbitServer`.
-    """
-
-    def __init__(self, config=None):
-        """See `TestResource.__init__`.
-
-        :param config: An optional instance of
-            `rabbitfixture.server.RabbitServerResources`.
-        """
-        super(RabbitServerResource, self).__init__()
-        self.server = RabbitServer(config)
-
-    def clean(self, resource):
-        """See `TestResource.clean`."""
-        resource.cleanUp()
-
-    def make(self, dependency_resources):
-        """See `TestResource.make`."""
-        self.server.setUp()
-        return self.server
-
-    def isDirty(self):
-        """See `TestResource.isDirty`.
-
-        Always returns ``True`` because it's difficult to figure out if an
-        `RabbitMQ` server has been used, and it will be very quick to reset
-        once we have the management plugin.
-
-        Also, somewhat confusingly, `testresources` uses `self._dirty` to
-        figure out whether or not to recreate the resource in `self.reset`.
-        That's only set by calling `self.dirtied`, which is fiddly from a
-        test. For now we assume that it doesn't matter if it's dirty or not;
-        tests need to ensure they're using uniquely named queues and/or
-        exchanges, or explicity purge things during set-up.
-        """
-        return True
-
-    def reset(self, old_resource, result=None):
-        """See `TestResource.reset`."""
-        # XXX: GavinPanella 2011-01-20 bug=???: When it becomes possible to
-        # install rabbitmq-management on Precise this could be changed to
-        # properly reset the running server.
-        return super(RabbitServerResource, self).reset(old_resource, result)
+from testtools.monkey import MonkeyPatcher
+
+
+class RabbitServerSettings(Fixture):
+    """
+    This patches the active Django settings to point the application at the
+    ephemeral RabbitMQ server specified by the given configuration.
+    """
+
+    def __init__(self, config):
+        super(RabbitServerSettings, self).__init__()
+        self.config = config
+
+    def setUp(self):
+        super(RabbitServerSettings, self).setUp()
+        from django.conf import settings
+        patcher = MonkeyPatcher()
+        patcher.add_patch(
+            settings, "RABBITMQ_HOST", "%s:%d" % (
+                self.config.hostname, self.config.port))
+        patcher.add_patch(settings, "RABBITMQ_USERID", "guest")
+        patcher.add_patch(settings, "RABBITMQ_PASSWORD", "guest")
+        patcher.add_patch(settings, "RABBITMQ_VIRTUAL_HOST", "/")
+        patcher.add_patch(settings, "RABBITMQ_PUBLISH", True)
+        self.addCleanup(patcher.restore)
+        patcher.patch()
+
+
+# See {start,stop,get}_rabbit().
+rabbit = None
+
+
+def start_rabbit():
+    """Start a shared :class:`RabbitServer`."""
+    global rabbit
+    if rabbit is None:
+        rabbit = RabbitServer()
+        rabbit.setUp()
+
+
+def stop_rabbit():
+    """Stop a shared :class:`RabbitServer`, if any."""
+    global rabbit
+    if rabbit is not None:
+        rabbit.cleanUp()
+        rabbit = None
+
+
+def get_rabbit():
+    """Start and return a shared :class:`RabbitServer`."""
+    global rabbit
+    start_rabbit()
+    return rabbit
+
+
+def use_rabbit(test):
+    """Ensure that a :class:`RabbitServer` is started, and Django's setting
+    updated to point to it, and that Django's settings are returned to their
+    original values at the end.
+    """
+    config = get_rabbit().config
+    fixture = RabbitServerSettings(config)
+    test.useFixture(fixture)
+
+
+def uses_rabbit(func):
+    """Decorate a test function with `use_rabbit`."""
+    @wraps(func)
+    def wrapper(self):
+        use_rabbit(self)
+        return func(self)
+    return wrapper

=== modified file 'src/maastesting/tests/test_rabbit.py'
--- src/maastesting/tests/test_rabbit.py	2012-03-13 05:34:38 +0000
+++ src/maastesting/tests/test_rabbit.py	2012-03-16 17:23:36 +0000
@@ -11,50 +11,27 @@
 __metaclass__ = type
 __all__ = []
 
-from maastesting.rabbit import RabbitServerResource
+from random import randint
+
+from django.conf import settings
+from maastesting.factory import factory
+from maastesting.rabbit import RabbitServerSettings
 from maastesting.testcase import TestCase
-from rabbitfixture.server import RabbitServer
-
-
-class TestRabbitServerResourceBasics(TestCase):
-
-    def test_cycle(self):
-        """
-        A RabbitMQ server can be successfully brought up and shut-down.
-        """
-        resource = RabbitServerResource()
-        server = resource.make({})
-        try:
-            self.assertIs(resource.server, server)
-            self.assertIsInstance(server, RabbitServer)
-        finally:
-            resource.clean(server)
-
-    def test_reset(self):
-        """
-        Resetting a RabbitMQ server resource when it has not explicitly been
-        marked as dirty - via `RabbitServerResource.dirtied` - is a no-op; the
-        same server is returned.
-        """
-        resource = RabbitServerResource()
-        server = resource.make({})
-        try:
-            server2 = resource.reset(server)
-            self.assertIs(server, server2)
-        finally:
-            resource.clean(server)
-
-
-class TestRabbitServerResource(TestCase):
-
-    resources = [
-        ("rabbit", RabbitServerResource()),
-        ]
-
-    def test_one(self):
-        """The `self.rabbit` resource is made available here."""
-        self.assertIsInstance(self.rabbit, RabbitServer)
-
-    def test_two(self):
-        """The `self.rabbit resource is also made available here."""
-        self.assertIsInstance(self.rabbit, RabbitServer)
+from rabbitfixture.server import RabbitServerResources
+
+
+class TestRabbitServerSettings(TestCase):
+
+    def test_patch(self):
+        config = RabbitServerResources(
+            hostname=factory.getRandomString(),
+            port=randint(1025, 2**16))
+        self.useFixture(config)
+        self.useFixture(RabbitServerSettings(config))
+        self.assertEqual(
+            "%s:%d" % (config.hostname, config.port),
+            settings.RABBITMQ_HOST)
+        self.assertEqual("guest", settings.RABBITMQ_PASSWORD)
+        self.assertEqual("guest", settings.RABBITMQ_USERID)
+        self.assertEqual("/", settings.RABBITMQ_VIRTUAL_HOST)
+        self.assertTrue(settings.RABBITMQ_PUBLISH)

=== modified file 'src/metadataserver/tests/__init__.py'
--- src/metadataserver/tests/__init__.py	2012-02-14 14:34:29 +0000
+++ src/metadataserver/tests/__init__.py	2012-03-16 17:23:36 +0000
@@ -0,0 +1,17 @@
+# Copyright 2005-2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for `metadataserver`."""
+
+from __future__ import (
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+from maastesting.rabbit import stop_rabbit
+
+
+tearDown = stop_rabbit

=== modified file 'src/metadataserver/tests/test_api.py'
--- src/metadataserver/tests/test_api.py	2012-03-13 05:34:38 +0000
+++ src/metadataserver/tests/test_api.py	2012-03-16 17:23:36 +0000
@@ -19,6 +19,7 @@
 from maasserver.exceptions import Unauthorized
 from maasserver.testing.factory import factory
 from maasserver.testing.oauthclient import OAuthAuthenticatedClient
+from maastesting.rabbit import uses_rabbit
 from maastesting.testcase import TestCase
 from metadataserver.api import (
     check_version,
@@ -94,6 +95,7 @@
     def test_extract_oauth_key_rejects_auth_without_oauth_key(self):
         self.assertRaises(Unauthorized, extract_oauth_key, '')
 
+    @uses_rabbit
     def test_get_node_for_request_finds_node(self):
         node = factory.make_node()
         token = NodeKey.objects.get_token_for_node(node)
@@ -137,10 +139,12 @@
     def test_no_anonymous_access(self):
         self.assertEqual(httplib.UNAUTHORIZED, self.get('/').status_code)
 
+    @uses_rabbit
     def test_metadata_index_shows_latest(self):
         client = self.make_node_client()
         self.assertIn('latest', self.get('/', client).content)
 
+    @uses_rabbit
     def test_metadata_index_shows_only_known_versions(self):
         client = self.make_node_client()
         for item in self.get('/', client).content.splitlines():
@@ -148,16 +152,19 @@
         # The test is that we get here without exception.
         pass
 
+    @uses_rabbit
     def test_version_index_shows_meta_data(self):
         client = self.make_node_client()
         items = self.get('/latest/', client).content.splitlines()
         self.assertIn('meta-data', items)
 
+    @uses_rabbit
     def test_version_index_does_not_show_user_data_if_not_available(self):
         client = self.make_node_client()
         items = self.get('/latest/', client).content.splitlines()
         self.assertNotIn('user-data', items)
 
+    @uses_rabbit
     def test_version_index_shows_user_data_if_available(self):
         node = factory.make_node()
         NodeUserData.objects.set_user_data(node, b"User data for node")
@@ -165,6 +172,7 @@
         items = self.get('/latest/', client).content.splitlines()
         self.assertIn('user-data', items)
 
+    @uses_rabbit
     def test_meta_data_view_lists_fields(self):
         client = self.make_node_client()
         response = self.get('/latest/meta-data/', client)
@@ -172,12 +180,14 @@
         self.assertItemsEqual(
             MetaDataHandler.fields, response.content.split())
 
+    @uses_rabbit
     def test_meta_data_view_is_sorted(self):
         client = self.make_node_client()
         response = self.get('/latest/meta-data/', client)
         attributes = response.content.split()
         self.assertEqual(sorted(attributes), attributes)
 
+    @uses_rabbit
     def test_meta_data_unknown_item_is_not_found(self):
         client = self.make_node_client()
         response = self.get('/latest/meta-data/UNKNOWN-ITEM-HA-HA-HA', client)
@@ -188,6 +198,7 @@
         producers = map(handler.get_attribute_producer, handler.fields)
         self.assertNotIn(None, producers)
 
+    @uses_rabbit
     def test_meta_data_local_hostname_returns_hostname(self):
         hostname = factory.getRandomString()
         client = self.make_node_client(factory.make_node(hostname=hostname))
@@ -197,6 +208,7 @@
             (response.status_code, response.content.decode('ascii')))
         self.assertIn('text/plain', response['Content-Type'])
 
+    @uses_rabbit
     def test_meta_data_instance_id_returns_system_id(self):
         node = factory.make_node()
         client = self.make_node_client(node)
@@ -206,6 +218,7 @@
             (response.status_code, response.content.decode('ascii')))
         self.assertIn('text/plain', response['Content-Type'])
 
+    @uses_rabbit
     def test_user_data_view_returns_binary_data(self):
         data = b"\x00\xff\xff\xfe\xff"
         node = factory.make_node()
@@ -217,6 +230,7 @@
         self.assertEqual(
             (httplib.OK, data), (response.status_code, response.content))
 
+    @uses_rabbit
     def test_user_data_for_node_without_user_data_returns_not_found(self):
         response = self.get('/latest/user-data', self.make_node_client())
         self.assertEqual(httplib.NOT_FOUND, response.status_code)

=== modified file 'src/metadataserver/tests/test_models.py'
--- src/metadataserver/tests/test_models.py	2012-03-13 05:34:38 +0000
+++ src/metadataserver/tests/test_models.py	2012-03-16 17:23:36 +0000
@@ -12,6 +12,7 @@
 __all__ = []
 
 from maasserver.testing.factory import factory
+from maastesting.rabbit import use_rabbit
 from maastesting.testcase import TestCase
 from metadataserver.models import (
     NodeKey,
@@ -22,6 +23,10 @@
 class TestNodeKeyManager(TestCase):
     """Test NodeKeyManager."""
 
+    def setUp(self):
+        super(TestNodeKeyManager, self).setUp()
+        use_rabbit(self)
+
     def test_get_token_for_node_registers_node_key(self):
         node = factory.make_node()
         token = NodeKey.objects.get_token_for_node(node)
@@ -68,6 +73,10 @@
 class TestNodeUserDataManager(TestCase):
     """Test NodeUserDataManager."""
 
+    def setUp(self):
+        super(TestNodeUserDataManager, self).setUp()
+        use_rabbit(self)
+
     def test_set_user_data_creates_new_nodeuserdata_if_needed(self):
         node = factory.make_node()
         data = b'foo'

=== modified file 'src/provisioningserver/testing/amqpclient.py'
--- src/provisioningserver/testing/amqpclient.py	2012-03-15 13:58:32 +0000
+++ src/provisioningserver/testing/amqpclient.py	2012-03-16 17:23:36 +0000
@@ -67,12 +67,12 @@
           http://readthedocs.org/docs/nose/en/latest/writing_tests.html
 
         """
-        from provisioningserver import tests
-        return tests.get_rabbit()
+        from maastesting import rabbit
+        return rabbit.get_rabbit()
 
     @skip(
         "RabbitMQ is not yet a required component "
-        "of a running MAAS installation.")
+        "of a running MAAS pserv instance.")
     def setUp(self):
         """
         At each run, we delete the test vhost and recreate it, to be sure to be

=== modified file 'src/provisioningserver/tests/__init__.py'
--- src/provisioningserver/tests/__init__.py	2012-02-15 14:23:44 +0000
+++ src/provisioningserver/tests/__init__.py	2012-03-16 17:23:36 +0000
@@ -9,28 +9,9 @@
     )
 
 __metaclass__ = type
-__all__ = [
-    "get_rabbit",
-    ]
-
-from rabbitfixture.server import RabbitServer
-
-# Set get_rabbit() and tearDown().
-rabbit = None
-
-
-def get_rabbit():
-    """Return a running `RabbitServer` fixture."""
-    global rabbit
-    if rabbit is None:
-        rabbit = RabbitServer()
-        rabbit.setUp()
-    return rabbit
-
-
-def tearDown():
-    """Package-level fixture hook, recognized by nose."""
-    global rabbit
-    if rabbit is not None:
-        rabbit.cleanUp()
-        rabbit = None
+__all__ = []
+
+from maastesting.rabbit import stop_rabbit
+
+
+tearDown = stop_rabbit