Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / plankton / tests.py @ f6ff4b40

History | View | Annotate | Download (11.6 kB)

1 37f87d98 Christos Stavrakakis
# Copyright 2012 GRNET S.A. All rights reserved.
2 37f87d98 Christos Stavrakakis
#
3 37f87d98 Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 37f87d98 Christos Stavrakakis
# without modification, are permitted provided that the following
5 37f87d98 Christos Stavrakakis
# conditions are met:
6 37f87d98 Christos Stavrakakis
#
7 37f87d98 Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 37f87d98 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 37f87d98 Christos Stavrakakis
#      disclaimer.
10 37f87d98 Christos Stavrakakis
#
11 37f87d98 Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 37f87d98 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 37f87d98 Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 37f87d98 Christos Stavrakakis
#      provided with the distribution.
15 37f87d98 Christos Stavrakakis
#
16 37f87d98 Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 37f87d98 Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 37f87d98 Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 37f87d98 Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 37f87d98 Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 37f87d98 Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 37f87d98 Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 37f87d98 Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 37f87d98 Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 37f87d98 Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 37f87d98 Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 37f87d98 Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 37f87d98 Christos Stavrakakis
#
29 37f87d98 Christos Stavrakakis
# The views and conclusions contained in the software and
30 37f87d98 Christos Stavrakakis
# documentation are those of the authors and should not be
31 37f87d98 Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 37f87d98 Christos Stavrakakis
# or implied, of GRNET S.A.
33 37f87d98 Christos Stavrakakis
34 37f87d98 Christos Stavrakakis
import json
35 37f87d98 Christos Stavrakakis
36 37f87d98 Christos Stavrakakis
from django.test import TestCase
37 37f87d98 Christos Stavrakakis
38 37f87d98 Christos Stavrakakis
from contextlib import contextmanager
39 37f87d98 Christos Stavrakakis
from mock import patch
40 37f87d98 Christos Stavrakakis
from functools import wraps
41 37f87d98 Christos Stavrakakis
from copy import deepcopy
42 37f87d98 Christos Stavrakakis
43 37f87d98 Christos Stavrakakis
44 37f87d98 Christos Stavrakakis
FILTERS = ('name', 'container_format', 'disk_format', 'status', 'size_min',
45 37f87d98 Christos Stavrakakis
           'size_max')
46 37f87d98 Christos Stavrakakis
PARAMS = ('sort_key', 'sort_dir')
47 37f87d98 Christos Stavrakakis
SORT_KEY_OPTIONS = ('id', 'name', 'status', 'size', 'disk_format',
48 37f87d98 Christos Stavrakakis
                    'container_format', 'created_at', 'updated_at')
49 37f87d98 Christos Stavrakakis
SORT_DIR_OPTIONS = ('asc', 'desc')
50 37f87d98 Christos Stavrakakis
LIST_FIELDS = ('status', 'name', 'disk_format', 'container_format', 'size',
51 37f87d98 Christos Stavrakakis
               'id')
52 37f87d98 Christos Stavrakakis
DETAIL_FIELDS = ('name', 'disk_format', 'container_format', 'size', 'checksum',
53 37f87d98 Christos Stavrakakis
                 'location', 'created_at', 'updated_at', 'deleted_at',
54 37f87d98 Christos Stavrakakis
                 'status', 'is_public', 'owner', 'properties', 'id')
55 37f87d98 Christos Stavrakakis
ADD_FIELDS = ('name', 'id', 'store', 'disk_format', 'container_format', 'size',
56 37f87d98 Christos Stavrakakis
              'checksum', 'is_public', 'owner', 'properties', 'location')
57 37f87d98 Christos Stavrakakis
UPDATE_FIELDS = ('name', 'disk_format', 'container_format', 'is_public',
58 37f87d98 Christos Stavrakakis
                 'owner', 'properties', 'status')
59 37f87d98 Christos Stavrakakis
60 37f87d98 Christos Stavrakakis
61 37f87d98 Christos Stavrakakis
@contextmanager
62 37f87d98 Christos Stavrakakis
def astakos_user(user):
63 37f87d98 Christos Stavrakakis
    """
64 37f87d98 Christos Stavrakakis
    Context manager to mock astakos response.
65 37f87d98 Christos Stavrakakis

66 37f87d98 Christos Stavrakakis
    usage:
67 37f87d98 Christos Stavrakakis
    with astakos_user("user@user.com"):
68 37f87d98 Christos Stavrakakis
        .... make api calls ....
69 37f87d98 Christos Stavrakakis

70 37f87d98 Christos Stavrakakis
    """
71 37f87d98 Christos Stavrakakis
    def dummy_get_user(request, *args, **kwargs):
72 37f87d98 Christos Stavrakakis
        request.user = {'username': user, 'groups': []}
73 37f87d98 Christos Stavrakakis
        request.user_uniq = user
74 52194c77 Christos Stavrakakis
75 52194c77 Christos Stavrakakis
    with patch('synnefo.plankton.util.get_user') as m:
76 52194c77 Christos Stavrakakis
        m.side_effect = dummy_get_user
77 52194c77 Christos Stavrakakis
        yield
78 37f87d98 Christos Stavrakakis
79 37f87d98 Christos Stavrakakis
80 52194c77 Christos Stavrakakis
class BaseAPITest(TestCase):
81 37f87d98 Christos Stavrakakis
    def get(self, url, user='user', *args, **kwargs):
82 37f87d98 Christos Stavrakakis
        with astakos_user(user):
83 37f87d98 Christos Stavrakakis
            response = self.client.get(url, *args, **kwargs)
84 37f87d98 Christos Stavrakakis
        return response
85 37f87d98 Christos Stavrakakis
86 37f87d98 Christos Stavrakakis
    def delete(self, url, user='user'):
87 37f87d98 Christos Stavrakakis
        with astakos_user(user):
88 37f87d98 Christos Stavrakakis
            response = self.client.delete(url)
89 37f87d98 Christos Stavrakakis
        return response
90 37f87d98 Christos Stavrakakis
91 37f87d98 Christos Stavrakakis
    def post(self, url, user='user', params={}, ctype='json', *args, **kwargs):
92 37f87d98 Christos Stavrakakis
        if ctype == 'json':
93 37f87d98 Christos Stavrakakis
            content_type = 'application/json'
94 37f87d98 Christos Stavrakakis
        with astakos_user(user):
95 37f87d98 Christos Stavrakakis
            response = self.client.post(url, params, content_type=content_type,
96 37f87d98 Christos Stavrakakis
                                        *args, **kwargs)
97 37f87d98 Christos Stavrakakis
        return response
98 37f87d98 Christos Stavrakakis
99 37f87d98 Christos Stavrakakis
    def put(self, url, user='user', params={}, ctype='json', *args, **kwargs):
100 37f87d98 Christos Stavrakakis
        if ctype == 'json':
101 37f87d98 Christos Stavrakakis
            content_type = 'application/json'
102 37f87d98 Christos Stavrakakis
        with astakos_user(user):
103 37f87d98 Christos Stavrakakis
            response = self.client.put(url, params, content_type=content_type,
104 cc92b70f Christos Stavrakakis
                                       *args, **kwargs)
105 37f87d98 Christos Stavrakakis
        return response
106 37f87d98 Christos Stavrakakis
107 37f87d98 Christos Stavrakakis
    def assertSuccess(self, response):
108 37f87d98 Christos Stavrakakis
        self.assertTrue(response.status_code in [200, 203, 204])
109 37f87d98 Christos Stavrakakis
110 37f87d98 Christos Stavrakakis
    def assertFault(self, response, status_code, name):
111 37f87d98 Christos Stavrakakis
        self.assertEqual(response.status_code, status_code)
112 450a6d2b Christos Stavrakakis
        fault = response.content
113 450a6d2b Christos Stavrakakis
        self.assertEqual(fault, name)
114 37f87d98 Christos Stavrakakis
115 37f87d98 Christos Stavrakakis
    def assertBadRequest(self, response):
116 450a6d2b Christos Stavrakakis
        self.assertFault(response, 400, '400 Bad Request')
117 37f87d98 Christos Stavrakakis
118 37f87d98 Christos Stavrakakis
    def assertItemNotFound(self, response):
119 37f87d98 Christos Stavrakakis
        self.assertFault(response, 404, 'itemNotFound')
120 37f87d98 Christos Stavrakakis
121 37f87d98 Christos Stavrakakis
122 37f87d98 Christos Stavrakakis
DummyImages = {
123 37f87d98 Christos Stavrakakis
 '0786a349-9725-48ec-8b86-8598eefc4043':
124 37f87d98 Christos Stavrakakis
 {'checksum': u'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
125 37f87d98 Christos Stavrakakis
  u'container_format': u'bare',
126 37f87d98 Christos Stavrakakis
  'created_at': '2012-12-04 09:50:20',
127 37f87d98 Christos Stavrakakis
  'deleted_at': '',
128 37f87d98 Christos Stavrakakis
  u'disk_format': u'diskdump',
129 37f87d98 Christos Stavrakakis
  'id': u'0786a349-9725-48ec-8b86-8598eefc4043',
130 37f87d98 Christos Stavrakakis
  'is_public': True,
131 37f87d98 Christos Stavrakakis
  'location': u'pithos://foo@example.com/container/foo3',
132 37f87d98 Christos Stavrakakis
  u'name': u'dummyname',
133 37f87d98 Christos Stavrakakis
  'owner': u'foo@example.com',
134 37f87d98 Christos Stavrakakis
  'properties': {},
135 37f87d98 Christos Stavrakakis
  'size': 500L,
136 37f87d98 Christos Stavrakakis
  u'status': u'available',
137 37f87d98 Christos Stavrakakis
  'store': 'pithos',
138 37f87d98 Christos Stavrakakis
  'updated_at': '2012-12-04 09:50:54'},
139 37f87d98 Christos Stavrakakis
140 37f87d98 Christos Stavrakakis
 'd8aa85b8-410b-4550-953d-6797572534e6':
141 37f87d98 Christos Stavrakakis
 {'checksum': u'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
142 37f87d98 Christos Stavrakakis
  u'container_format': u'bare',
143 37f87d98 Christos Stavrakakis
  'created_at': '2012-11-26 11:56:42',
144 37f87d98 Christos Stavrakakis
  'deleted_at': '',
145 37f87d98 Christos Stavrakakis
  u'disk_format': u'diskdump',
146 37f87d98 Christos Stavrakakis
  'id': u'd8aa85b8-410b-4550-953d-6797572534e6',
147 37f87d98 Christos Stavrakakis
  'is_public': False,
148 37f87d98 Christos Stavrakakis
  'location': u'pithos://foo@example.com/container/private',
149 37f87d98 Christos Stavrakakis
  u'name': u'dummyname2',
150 37f87d98 Christos Stavrakakis
  'owner': u'foo@example.com',
151 37f87d98 Christos Stavrakakis
  'properties': {},
152 37f87d98 Christos Stavrakakis
  'size': 10000L,
153 37f87d98 Christos Stavrakakis
  u'status': u'available',
154 37f87d98 Christos Stavrakakis
  'store': 'pithos',
155 37f87d98 Christos Stavrakakis
  'updated_at': '2012-11-26 11:57:09'},
156 37f87d98 Christos Stavrakakis
157 37f87d98 Christos Stavrakakis
 '264fb9ac-2458-421c-b460-6a765a92825c':
158 37f87d98 Christos Stavrakakis
 {'checksum': u'0c6d0586744781218672fff2d7ed94cc32efb02a6a8eb589a0628f0e22bd5a7f',
159 37f87d98 Christos Stavrakakis
  u'container_format': u'bare',
160 37f87d98 Christos Stavrakakis
  'created_at': '2012-11-26 11:52:54',
161 37f87d98 Christos Stavrakakis
  'deleted_at': '',
162 37f87d98 Christos Stavrakakis
  u'disk_format': u'diskdump',
163 37f87d98 Christos Stavrakakis
  'id': u'264fb9ac-2458-421c-b460-6a765a92825c',
164 37f87d98 Christos Stavrakakis
  'is_public': True,
165 37f87d98 Christos Stavrakakis
  'location': u'pithos://foo@example.com/container/baz.diskdump',
166 37f87d98 Christos Stavrakakis
  u'name': u'"dummyname3"',
167 37f87d98 Christos Stavrakakis
  'owner': u'foo@example.com',
168 37f87d98 Christos Stavrakakis
  'properties': {u'description': u'Debian Squeeze Base System',
169 37f87d98 Christos Stavrakakis
                 u'gui': u'No GUI',
170 37f87d98 Christos Stavrakakis
                 u'kernel': u'2.6.32',
171 37f87d98 Christos Stavrakakis
                 u'os': u'debian',
172 37f87d98 Christos Stavrakakis
                 u'osfamily': u'linux',
173 37f87d98 Christos Stavrakakis
                 u'root_partition': u'1',
174 37f87d98 Christos Stavrakakis
                 u'size': u'451',
175 37f87d98 Christos Stavrakakis
                 u'sortorder': u'1',
176 37f87d98 Christos Stavrakakis
                 u'users': u'root'},
177 37f87d98 Christos Stavrakakis
  'size': 473772032L,
178 37f87d98 Christos Stavrakakis
  u'status': u'available',
179 37f87d98 Christos Stavrakakis
  'store': 'pithos',
180 37f87d98 Christos Stavrakakis
  'updated_at': '2012-11-26 11:55:40'}}
181 37f87d98 Christos Stavrakakis
182 37f87d98 Christos Stavrakakis
183 37f87d98 Christos Stavrakakis
def assert_backend_closed(func):
184 37f87d98 Christos Stavrakakis
    @wraps(func)
185 37f87d98 Christos Stavrakakis
    def wrapper(self, backend):
186 37f87d98 Christos Stavrakakis
        result = func(self, backend)
187 37f87d98 Christos Stavrakakis
        if backend.called is True:
188 37f87d98 Christos Stavrakakis
            backend.return_value.close.assert_called_once_with()
189 37f87d98 Christos Stavrakakis
        return result
190 37f87d98 Christos Stavrakakis
    return wrapper
191 37f87d98 Christos Stavrakakis
192 37f87d98 Christos Stavrakakis
193 37f87d98 Christos Stavrakakis
@patch("synnefo.plankton.util.ImageBackend")
194 52194c77 Christos Stavrakakis
class PlanktonTest(BaseAPITest):
195 37f87d98 Christos Stavrakakis
    @assert_backend_closed
196 37f87d98 Christos Stavrakakis
    def test_list_images(self, backend):
197 37f87d98 Christos Stavrakakis
        backend.return_value.list.return_value =\
198 37f87d98 Christos Stavrakakis
                deepcopy(DummyImages).values()
199 37f87d98 Christos Stavrakakis
        response = self.get("/plankton/images/")
200 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
201 37f87d98 Christos Stavrakakis
        images = json.loads(response.content)
202 37f87d98 Christos Stavrakakis
        for api_image in images:
203 37f87d98 Christos Stavrakakis
            id = api_image['id']
204 37f87d98 Christos Stavrakakis
            pithos_image = dict([(key, val)\
205 37f87d98 Christos Stavrakakis
                                for key, val in DummyImages[id].items()\
206 37f87d98 Christos Stavrakakis
                                if key in LIST_FIELDS])
207 37f87d98 Christos Stavrakakis
            self.assertEqual(api_image, pithos_image)
208 37f87d98 Christos Stavrakakis
        backend.return_value\
209 37f87d98 Christos Stavrakakis
                .list.assert_called_once_with({}, {'sort_key': 'created_at',
210 37f87d98 Christos Stavrakakis
                                                   'sort_dir': 'desc'})
211 37f87d98 Christos Stavrakakis
212 37f87d98 Christos Stavrakakis
    @assert_backend_closed
213 37f87d98 Christos Stavrakakis
    def test_list_images_detail(self, backend):
214 37f87d98 Christos Stavrakakis
        backend.return_value.list.return_value =\
215 37f87d98 Christos Stavrakakis
                deepcopy(DummyImages).values()
216 37f87d98 Christos Stavrakakis
        response = self.get("/plankton/images/detail")
217 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
218 37f87d98 Christos Stavrakakis
        images = json.loads(response.content)
219 37f87d98 Christos Stavrakakis
        for api_image in images:
220 37f87d98 Christos Stavrakakis
            id = api_image['id']
221 37f87d98 Christos Stavrakakis
            pithos_image = dict([(key, val)\
222 37f87d98 Christos Stavrakakis
                                for key, val in DummyImages[id].items()\
223 37f87d98 Christos Stavrakakis
                                if key in DETAIL_FIELDS])
224 37f87d98 Christos Stavrakakis
            self.assertEqual(api_image, pithos_image)
225 37f87d98 Christos Stavrakakis
        backend.return_value\
226 37f87d98 Christos Stavrakakis
                .list.assert_called_once_with({}, {'sort_key': 'created_at',
227 37f87d98 Christos Stavrakakis
                                                   'sort_dir': 'desc'})
228 37f87d98 Christos Stavrakakis
229 37f87d98 Christos Stavrakakis
    @assert_backend_closed
230 37f87d98 Christos Stavrakakis
    def test_list_images_filters(self, backend):
231 37f87d98 Christos Stavrakakis
        backend.return_value.list.return_value =\
232 37f87d98 Christos Stavrakakis
                deepcopy(DummyImages).values()
233 37f87d98 Christos Stavrakakis
        response = self.get("/plankton/images/?size_max=1000")
234 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
235 37f87d98 Christos Stavrakakis
        backend.return_value\
236 450a6d2b Christos Stavrakakis
                .list.assert_called_once_with({'size_max': 1000},
237 37f87d98 Christos Stavrakakis
                                              {'sort_key': 'created_at',
238 37f87d98 Christos Stavrakakis
                                               'sort_dir': 'desc'})
239 37f87d98 Christos Stavrakakis
240 37f87d98 Christos Stavrakakis
    @assert_backend_closed
241 450a6d2b Christos Stavrakakis
    def test_list_images_filters_error_1(self, backend):
242 450a6d2b Christos Stavrakakis
        response = self.get("/plankton/images/?size_max=")
243 450a6d2b Christos Stavrakakis
        self.assertBadRequest(response)
244 450a6d2b Christos Stavrakakis
245 450a6d2b Christos Stavrakakis
    @assert_backend_closed
246 450a6d2b Christos Stavrakakis
    def test_list_images_filters_error_2(self, backend):
247 450a6d2b Christos Stavrakakis
        response = self.get("/plankton/images/?size_min=foo")
248 450a6d2b Christos Stavrakakis
        self.assertBadRequest(response)
249 450a6d2b Christos Stavrakakis
250 450a6d2b Christos Stavrakakis
    @assert_backend_closed
251 37f87d98 Christos Stavrakakis
    def test_update_image(self, backend):
252 37f87d98 Christos Stavrakakis
        db_image = DummyImages.values()[0]
253 37f87d98 Christos Stavrakakis
        response = self.put("/plankton/images/%s" % db_image['id'],
254 37f87d98 Christos Stavrakakis
                            json.dumps({}),
255 37f87d98 Christos Stavrakakis
                            'json', HTTP_X_IMAGE_META_OWNER='user2')
256 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
257 37f87d98 Christos Stavrakakis
        backend.return_value.update.assert_called_once_with(db_image['id'],
258 37f87d98 Christos Stavrakakis
                                                            {"owner": "user2"})
259 37f87d98 Christos Stavrakakis
260 37f87d98 Christos Stavrakakis
    @assert_backend_closed
261 37f87d98 Christos Stavrakakis
    def test_add_image_member(self, backend):
262 37f87d98 Christos Stavrakakis
        image_id = DummyImages.values()[0]['id']
263 37f87d98 Christos Stavrakakis
        response = self.put("/plankton/images/%s/members/user3" % image_id,
264 37f87d98 Christos Stavrakakis
                            json.dumps({}), 'json')
265 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
266 37f87d98 Christos Stavrakakis
        backend.return_value.add_user.assert_called_once_with(image_id,
267 37f87d98 Christos Stavrakakis
                                                             'user3')
268 37f87d98 Christos Stavrakakis
269 37f87d98 Christos Stavrakakis
    @assert_backend_closed
270 37f87d98 Christos Stavrakakis
    def test_remove_image_member(self, backend):
271 37f87d98 Christos Stavrakakis
        image_id = DummyImages.values()[0]['id']
272 37f87d98 Christos Stavrakakis
        response = self.delete("/plankton/images/%s/members/user3" % image_id)
273 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
274 37f87d98 Christos Stavrakakis
        backend.return_value.remove_user.assert_called_once_with(image_id,
275 37f87d98 Christos Stavrakakis
                                                                'user3')
276 37f87d98 Christos Stavrakakis
277 37f87d98 Christos Stavrakakis
    @assert_backend_closed
278 37f87d98 Christos Stavrakakis
    def test_add_image(self, backend):
279 37f87d98 Christos Stavrakakis
        response = self.post("/plankton/images/",
280 37f87d98 Christos Stavrakakis
                             json.dumps({}),
281 37f87d98 Christos Stavrakakis
                             'json',
282 37f87d98 Christos Stavrakakis
                             HTTP_X_IMAGE_META_NAME='dummy_name',
283 37f87d98 Christos Stavrakakis
                             HTTP_X_IMAGE_META_OWNER='dummy_owner',
284 37f87d98 Christos Stavrakakis
                             HTTP_X_IMAGE_META_LOCATION='dummy_location')
285 37f87d98 Christos Stavrakakis
        self.assertSuccess(response)
286 37f87d98 Christos Stavrakakis
        backend.return_value.register.assert_called_once_with('dummy_name',
287 37f87d98 Christos Stavrakakis
                                                              'dummy_location',
288 52194c77 Christos Stavrakakis
                                                      {'owner': 'dummy_owner'})
289 37f87d98 Christos Stavrakakis
290 37f87d98 Christos Stavrakakis
    @assert_backend_closed
291 37f87d98 Christos Stavrakakis
    def test_get_image(self, backend):
292 37f87d98 Christos Stavrakakis
        response = self.get("/plankton/images/123")
293 37f87d98 Christos Stavrakakis
        self.assertEqual(response.status_code, 501)
294 f13aab5d Christos Stavrakakis
295 f13aab5d Christos Stavrakakis
    @assert_backend_closed
296 f13aab5d Christos Stavrakakis
    def test_delete_image(self, backend):
297 f13aab5d Christos Stavrakakis
        response = self.delete("/plankton/images/123")
298 f13aab5d Christos Stavrakakis
        self.assertEqual(response.status_code, 204)
299 f13aab5d Christos Stavrakakis
        backend.return_value.unregister.assert_called_once_with('123')
300 f13aab5d Christos Stavrakakis
        backend.return_value._delete.assert_not_called()