# HG changeset patch # User Denis Laxalde <denis.laxalde@logilab.fr> # Date 1535986109 -7200 # Mon Sep 03 16:48:29 2018 +0200 # Node ID 34801bb72594d53f5e142aac98edbd249d8bc4f2 # Parent 8382e8632a2af504ded5912a01335717df3c73f0 [test] Setup S3Storage in a hook Instead of doing storage attribution in test setup, we implement a test hook (in test/data/hook.py) as would be done in real application. As a consequence, we change the way s3 requests are mocked (using moto). Namely, we introduce a "_s3_client" class method in S3Storage that is mocked in test setup so that mocking operates in the test hook. In migration tests, instead of instantiating a new storage object, we reuse the one bound to Image's data attribute so as to benefit from the active mock. diff --git a/cubicweb_s3storage/storages.py b/cubicweb_s3storage/storages.py --- a/cubicweb_s3storage/storages.py +++ b/cubicweb_s3storage/storages.py @@ -33,9 +33,13 @@ is_source_callback = True def __init__(self, bucket): - self.s3cnx = boto3.client('s3') + self.s3cnx = self._s3_client() self.bucket = bucket + @classmethod + def _s3_client(cls): + return boto3.client('s3') + def callback(self, source, cnx, value): """see docstring for prototype, which vary according to is_source_callback """ diff --git a/cubicweb_s3storage/testing.py b/cubicweb_s3storage/testing.py --- a/cubicweb_s3storage/testing.py +++ b/cubicweb_s3storage/testing.py @@ -1,22 +1,29 @@ import boto3 +from mock import patch from moto import mock_s3 -from cubicweb_s3storage.storages import S3Storage - class S3StorageTestMixin(object): - bucket = 'test-bucket' + s3_bucket = 'test-bucket' def setUp(self): - self.s3_mock = mock_s3() - self.s3_mock.start() + s3_mock = mock_s3() + s3_mock.start() resource = boto3.resource('s3', region_name='somewhere') - self.s3_bucket = resource.create_bucket(Bucket=self.bucket) - self.s3_storage = S3Storage(self.bucket) + self.s3_bucket = resource.create_bucket(Bucket=self.s3_bucket) + patched_storage_s3_client = patch( + 'cubicweb_s3storage.storages.S3Storage._s3_client', + return_value=boto3.client('s3'), + ) + patched_storage_s3_client.start() + self._mocks = [ + s3_mock, + patched_storage_s3_client, + ] super(S3StorageTestMixin, self).setUp() def tearDown(self): super(S3StorageTestMixin, self).tearDown() - del self.s3_storage - self.s3_mock.stop() + while self._mocks: + self._mocks.pop().stop() diff --git a/test/data/hooks.py b/test/data/hooks.py new file mode 100644 --- /dev/null +++ b/test/data/hooks.py @@ -0,0 +1,12 @@ + +from cubicweb.server.hook import Hook +from cubicweb_s3storage.storages import S3Storage + + +class S3StorageStartupHook(Hook): + __regid__ = 's3tests.server-startup-hook' + events = ('server_startup', 'server_maintenance') + + def __call__(self): + storage = S3Storage('test-bucket') + self.repo.system_source.set_storage('Image', 'data', storage) diff --git a/test/test_s3storage.py b/test/test_s3storage.py --- a/test/test_s3storage.py +++ b/test/test_s3storage.py @@ -1,15 +1,12 @@ import re from contextlib import contextmanager -import boto3 -from moto import mock_s3 from six import PY3 from cubicweb.server.sources import storages from cubicweb.devtools.testlib import CubicWebTC from cubicweb import Binary from cubicweb.server.migractions import ServerMigrationHelper -from cubicweb_s3storage.storages import S3Storage from cubicweb_s3storage import testing @@ -19,24 +16,16 @@ class S3StorageTC(testing.S3StorageTestMixin, CubicWebTC): - def setUp(self): - super(S3StorageTC, self).setUp() - storages.set_attribute_storage( - self.repo, 'Image', 'data', self.s3_storage) - - def tearDown(self): - super(S3StorageTC, self).tearDown() - storages.unset_attribute_storage(self.repo, 'Image', 'data') - def test_s3key_gen(self): + s3storage = self.repo.system_source.storage('Image', 'data') with self.admin_access.client_cnx() as cnx: fobj = create_image(cnx, b'some content') cnx.commit() eid = fobj.eid - k1 = self.s3_storage.get_s3_key(fobj, 'data') + k1 = s3storage.get_s3_key(fobj, 'data') with self.admin_access.client_cnx() as cnx: fobj = cnx.find('Image', eid=eid).one() - k2 = self.s3_storage.get_s3_key(fobj, 'data') + k2 = s3storage.get_s3_key(fobj, 'data') self.assertEqual(k1, k2) def test_entity_create(self): @@ -100,8 +89,7 @@ self.assertNotIn(key, keys) -class S3StorageMigrationTC(CubicWebTC): - bucket = 'test-bucket' +class S3StorageMigrationTC(testing.S3StorageTestMixin, CubicWebTC): @contextmanager def mh(self): @@ -116,12 +104,10 @@ create_image(cnx, thumbnail=Binary(b'some content')).eid cnx.commit() + # Re-use storage instance of "data" attribute as it already has s3 + # mock activated. + s3_storage = self.repo.system_source.storage('Image', 'data') with self.mh() as (cnx, mh): - mock = mock_s3() - mock.start() - resource = boto3.resource('s3', region_name='somewhere') - resource.create_bucket(Bucket=self.bucket) - s3_storage = S3Storage(self.bucket) storages.set_attribute_storage( self.repo, 'Image', 'thumbnail', s3_storage)