From d7fa4054621a3002708063218922d1c13573156b Mon Sep 17 00:00:00 2001
From: Daira Hopwood <david-sarah@jacaranda.org>
Date: Sat, 18 May 2013 02:35:01 +0100
Subject: [PATCH] Add tests for 'tahoe admin create-container'. refs #1971

Signed-off-by: Daira Hopwood <david-sarah@jacaranda.org>
---
 .../storage/backends/cloud/mock_cloud.py      | 11 ++-
 src/allmydata/test/test_storage.py            | 76 +++++++++++++++++++
 2 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/src/allmydata/storage/backends/cloud/mock_cloud.py b/src/allmydata/storage/backends/cloud/mock_cloud.py
index e6c333c7..c2b834d7 100644
--- a/src/allmydata/storage/backends/cloud/mock_cloud.py
+++ b/src/allmydata/storage/backends/cloud/mock_cloud.py
@@ -25,6 +25,13 @@ def configure_mock_cloud_backend(storedir, config):
     return CloudBackend(container)
 
 
+def _not_implemented():
+    raise NotImplementedError()
+
+def hook_create_container():
+    return defer.execute(_not_implemented)
+
+
 class MockContainer(ContainerRetryMixin, ContainerListMixin):
     implements(IContainer)
     """
@@ -45,10 +52,10 @@ class MockContainer(ContainerRetryMixin, ContainerListMixin):
         return ("<%s at %r>" % (self.__class__.__name__, self._storagedir,))
 
     def _create(self):
-        return defer.execute(self._not_implemented)
+        return hook_create_container()
 
     def _delete(self):
-        return defer.execute(self._not_implemented)
+        return defer.execute(_not_implemented)
 
     def _iterate_dirs(self):
         shares_dir = os.path.join(self._storagedir, "shares")
diff --git a/src/allmydata/test/test_storage.py b/src/allmydata/test/test_storage.py
index edfc5f7a..a0c42612 100644
--- a/src/allmydata/test/test_storage.py
+++ b/src/allmydata/test/test_storage.py
@@ -57,6 +57,8 @@ from allmydata.test.common import LoggingServiceParent, ShouldFailMixin, Crawler
 from allmydata.test.common_util import ReallyEqualMixin
 from allmydata.test.common_web import WebRenderingMixin
 from allmydata.test.no_network import NoNetworkServer
+from allmydata.test.test_cli import parse_options
+from allmydata.scripts.admin import do_create_container
 from allmydata.web.storage import StorageStatus, remove_prefix
 
 
@@ -1534,6 +1536,80 @@ class MSAzureStorageBackendTests(unittest.TestCase, CloudStorageBackendMixin):
         self.failUnless(done)
 
 
+class Namespace(object):
+    pass
+
+
+class CreateContainer(unittest.TestCase, WorkdirMixin):
+    def test_create_container(self):
+        # We'll use the mock cloud backend to test this.
+        basedir = self.workdir("test_create_container")
+        os.makedirs(basedir)
+        fileutil.write(os.path.join(basedir, "tahoe.cfg"),
+                                    "[client]\n"
+                                    "introducer.furl = \n"
+                                    "[storage]\n"
+                                    "enabled = true\n"
+                                    "backend = mock_cloud\n")
+
+        ns = Namespace()
+        ns.called = 0
+        def call_hook_create_container():
+            ns.called += 1
+            return defer.execute(ns.result_callback)
+        self.patch(mock_cloud, "hook_create_container", call_hook_create_container)
+        self.patch(cloud_common, 'BACKOFF_SECONDS_BEFORE_RETRY', (0, 0.1, 0.2))
+
+        def _run_create_container(result_callback):
+            # We're really only testing do_create_container (to avoid problems with
+            # restarting the reactor or exiting), but that should be sufficient.
+
+            ns.result_callback = result_callback
+            options = parse_options(basedir, "admin", ["create-container"])
+            options.stdout = StringIO()
+            options.stderr = StringIO()
+            d = defer.maybeDeferred(do_create_container, options)
+            d.addCallbacks(lambda ign: 0, lambda ign: 1)
+            d.addCallback(lambda rc: (options.stdout.getvalue(), options.stderr.getvalue(), rc))
+            return d
+
+        d = _run_create_container(lambda: None)
+        def _check_create(res):
+            (out, err, rc) = res
+            self.failUnlessEqual(ns.called, 1, str(res))
+            self.failUnlessIn("The container was successfully created.", out, str(res))
+            self.failUnlessEqual(err, "", str(res))
+            self.failUnlessEqual(rc, 0, str(res))
+        d.addCallback(_check_create)
+
+        def _already(ign):
+            def _already_exists(): raise CloudServiceError("", 409, "The specified container already exists.")
+            return _run_create_container(_already_exists)
+        d.addCallback(_already)
+        def _check_already(res):
+            (out, err, rc) = res
+            self.failUnlessEqual(ns.called, 2, str(res))
+            self.failUnlessEqual(out, "", str(res))
+            self.failUnlessIn("The specified container already exists.", err, str(res))
+            self.failUnlessEqual(rc, 1, str(res))
+        d.addCallback(_check_already)
+
+        def _failure(ign):
+            def _failed(): raise CloudServiceError("", 500, "<shrug>")
+            return _run_create_container(_failed)
+        d.addCallback(_failure)
+        def _check_failure(res):
+            (out, err, rc) = res
+            # 4 more calls, because there are three retries.
+            self.failUnlessEqual(ns.called, 6, str(res))
+            self.failUnlessEqual(out, "", str(res))
+            self.failUnlessIn("CloudError:", err, str(res))
+            self.failUnlessIn("<shrug>", err, str(res))
+            self.failUnlessEqual(rc, 1, str(res))
+        d.addCallback(_check_failure)
+        return d
+
+
 class ServerMixin:
     def allocate(self, account, storage_index, sharenums, size, canary=None):
         # These secrets are not used, but clients still provide them.
-- 
2.45.2