Skip to content

Commit b776c5e

Browse files
author
Gauvain Pocentek
committed
Add tests for managers mixins
1 parent f2c4a6e commit b776c5e

File tree

2 files changed

+359
-2
lines changed

2 files changed

+359
-2
lines changed

gitlab/mixins.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import gitlab
1919
from gitlab import base
20+
from gitlab import exceptions
2021

2122

2223
class GetMixin(object):
@@ -93,13 +94,15 @@ def get(self, id, **kwargs):
9394
object: The generated RESTObject.
9495
9596
Raises:
96-
GitlabGetError: If the server cannot perform the request.
97+
AttributeError: If the object could not be found in the list
9798
"""
9899
gen = self.list()
99100
for obj in gen:
100101
if str(obj.get_id()) == str(id):
101102
return obj
102103

104+
raise exceptions.GitlabHttpError(404, "Not found")
105+
103106

104107
class RetrieveMixin(ListMixin, GetMixin):
105108
pass
@@ -141,7 +144,7 @@ def create(self, data, **kwargs):
141144
if hasattr(self, '_sanitize_data'):
142145
data = self._sanitize_data(data, 'create')
143146
# Handle specific URL for creation
144-
path = kwargs.get('path', self.path)
147+
path = kwargs.pop('path', self.path)
145148
server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
146149
return self._obj_cls(self, server_data)
147150

gitlab/tests/test_mixins.py

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright (C) 2014 Mika Mäenpää <mika.j.maenpaa@tut.fi>,
4+
# Tampere University of Technology
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Lesser General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
from __future__ import print_function
20+
21+
try:
22+
import unittest
23+
except ImportError:
24+
import unittest2 as unittest
25+
26+
from httmock import HTTMock # noqa
27+
from httmock import response # noqa
28+
from httmock import urlmatch # noqa
29+
30+
from gitlab import * # noqa
31+
from gitlab.base import * # noqa
32+
from gitlab.mixins import * # noqa
33+
34+
35+
class TestMetaMixins(unittest.TestCase):
36+
def test_retrieve_mixin(self):
37+
class M(RetrieveMixin):
38+
pass
39+
40+
obj = M()
41+
self.assertTrue(hasattr(obj, 'list'))
42+
self.assertTrue(hasattr(obj, 'get'))
43+
self.assertFalse(hasattr(obj, 'create'))
44+
self.assertFalse(hasattr(obj, 'update'))
45+
self.assertFalse(hasattr(obj, 'delete'))
46+
self.assertIsInstance(obj, ListMixin)
47+
self.assertIsInstance(obj, GetMixin)
48+
49+
def test_crud_mixin(self):
50+
class M(CRUDMixin):
51+
pass
52+
53+
obj = M()
54+
self.assertTrue(hasattr(obj, 'get'))
55+
self.assertTrue(hasattr(obj, 'list'))
56+
self.assertTrue(hasattr(obj, 'create'))
57+
self.assertTrue(hasattr(obj, 'update'))
58+
self.assertTrue(hasattr(obj, 'delete'))
59+
self.assertIsInstance(obj, ListMixin)
60+
self.assertIsInstance(obj, GetMixin)
61+
self.assertIsInstance(obj, CreateMixin)
62+
self.assertIsInstance(obj, UpdateMixin)
63+
self.assertIsInstance(obj, DeleteMixin)
64+
65+
def test_no_update_mixin(self):
66+
class M(NoUpdateMixin):
67+
pass
68+
69+
obj = M()
70+
self.assertTrue(hasattr(obj, 'get'))
71+
self.assertTrue(hasattr(obj, 'list'))
72+
self.assertTrue(hasattr(obj, 'create'))
73+
self.assertFalse(hasattr(obj, 'update'))
74+
self.assertTrue(hasattr(obj, 'delete'))
75+
self.assertIsInstance(obj, ListMixin)
76+
self.assertIsInstance(obj, GetMixin)
77+
self.assertIsInstance(obj, CreateMixin)
78+
self.assertNotIsInstance(obj, UpdateMixin)
79+
self.assertIsInstance(obj, DeleteMixin)
80+
81+
82+
class FakeObject(base.RESTObject):
83+
pass
84+
85+
86+
class FakeManager(base.RESTManager):
87+
_path = '/tests'
88+
_obj_cls = FakeObject
89+
90+
91+
class TestMixinMethods(unittest.TestCase):
92+
def setUp(self):
93+
self.gl = Gitlab("http://localhost", private_token="private_token",
94+
api_version=4)
95+
96+
def test_get_mixin(self):
97+
class M(GetMixin, FakeManager):
98+
pass
99+
100+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
101+
method="get")
102+
def resp_cont(url, request):
103+
headers = {'Content-Type': 'application/json'}
104+
content = '{"id": 42, "foo": "bar"}'
105+
return response(200, content, headers, None, 5, request)
106+
107+
with HTTMock(resp_cont):
108+
mgr = M(self.gl)
109+
obj = mgr.get(42)
110+
self.assertIsInstance(obj, FakeObject)
111+
self.assertEqual(obj.foo, 'bar')
112+
self.assertEqual(obj.id, 42)
113+
114+
def test_get_without_id_mixin(self):
115+
class M(GetWithoutIdMixin, FakeManager):
116+
pass
117+
118+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
119+
method="get")
120+
def resp_cont(url, request):
121+
headers = {'Content-Type': 'application/json'}
122+
content = '{"foo": "bar"}'
123+
return response(200, content, headers, None, 5, request)
124+
125+
with HTTMock(resp_cont):
126+
mgr = M(self.gl)
127+
obj = mgr.get()
128+
self.assertIsInstance(obj, FakeObject)
129+
self.assertEqual(obj.foo, 'bar')
130+
self.assertFalse(hasattr(obj, 'id'))
131+
132+
def test_list_mixin(self):
133+
class M(ListMixin, FakeManager):
134+
pass
135+
136+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
137+
method="get")
138+
def resp_cont(url, request):
139+
headers = {'Content-Type': 'application/json'}
140+
content = '[{"id": 42, "foo": "bar"},{"id": 43, "foo": "baz"}]'
141+
return response(200, content, headers, None, 5, request)
142+
143+
with HTTMock(resp_cont):
144+
# test RESTObjectList
145+
mgr = M(self.gl)
146+
obj_list = mgr.list()
147+
self.assertIsInstance(obj_list, base.RESTObjectList)
148+
for obj in obj_list:
149+
self.assertIsInstance(obj, FakeObject)
150+
self.assertIn(obj.id, (42, 43))
151+
152+
# test list()
153+
obj_list = mgr.list(all=True)
154+
self.assertIsInstance(obj_list, list)
155+
self.assertEqual(obj_list[0].id, 42)
156+
self.assertEqual(obj_list[1].id, 43)
157+
self.assertIsInstance(obj_list[0], FakeObject)
158+
self.assertEqual(len(obj_list), 2)
159+
160+
def test_list_other_url(self):
161+
class M(ListMixin, FakeManager):
162+
pass
163+
164+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/others',
165+
method="get")
166+
def resp_cont(url, request):
167+
headers = {'Content-Type': 'application/json'}
168+
content = '[{"id": 42, "foo": "bar"}]'
169+
return response(200, content, headers, None, 5, request)
170+
171+
with HTTMock(resp_cont):
172+
mgr = M(self.gl)
173+
obj_list = mgr.list(path='/others')
174+
self.assertIsInstance(obj_list, base.RESTObjectList)
175+
obj = obj_list.next()
176+
self.assertEqual(obj.id, 42)
177+
self.assertEqual(obj.foo, 'bar')
178+
self.assertRaises(StopIteration, obj_list.next)
179+
180+
def test_get_from_list_mixin(self):
181+
class M(GetFromListMixin, FakeManager):
182+
pass
183+
184+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
185+
method="get")
186+
def resp_cont(url, request):
187+
headers = {'Content-Type': 'application/json'}
188+
content = '[{"id": 42, "foo": "bar"},{"id": 43, "foo": "baz"}]'
189+
return response(200, content, headers, None, 5, request)
190+
191+
with HTTMock(resp_cont):
192+
mgr = M(self.gl)
193+
obj = mgr.get(42)
194+
self.assertIsInstance(obj, FakeObject)
195+
self.assertEqual(obj.foo, 'bar')
196+
self.assertEqual(obj.id, 42)
197+
198+
self.assertRaises(GitlabHttpError, mgr.get, 44)
199+
200+
def test_create_mixin_get_attrs(self):
201+
class M1(CreateMixin, FakeManager):
202+
pass
203+
204+
class M2(CreateMixin, FakeManager):
205+
_create_attrs = (('foo',), ('bar', 'baz'))
206+
_update_attrs = (('foo',), ('bam', ))
207+
208+
mgr = M1(self.gl)
209+
required, optional = mgr.get_create_attrs()
210+
self.assertEqual(len(required), 0)
211+
self.assertEqual(len(optional), 0)
212+
213+
mgr = M2(self.gl)
214+
required, optional = mgr.get_create_attrs()
215+
self.assertIn('foo', required)
216+
self.assertIn('bar', optional)
217+
self.assertIn('baz', optional)
218+
self.assertNotIn('bam', optional)
219+
220+
def test_create_mixin_missing_attrs(self):
221+
class M(CreateMixin, FakeManager):
222+
_create_attrs = (('foo',), ('bar', 'baz'))
223+
224+
mgr = M(self.gl)
225+
data = {'foo': 'bar', 'baz': 'blah'}
226+
mgr._check_missing_create_attrs(data)
227+
228+
data = {'baz': 'blah'}
229+
with self.assertRaises(AttributeError) as error:
230+
mgr._check_missing_create_attrs(data)
231+
self.assertIn('foo', str(error.exception))
232+
233+
def test_create_mixin(self):
234+
class M(CreateMixin, FakeManager):
235+
_create_attrs = (('foo',), ('bar', 'baz'))
236+
_update_attrs = (('foo',), ('bam', ))
237+
238+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
239+
method="post")
240+
def resp_cont(url, request):
241+
headers = {'Content-Type': 'application/json'}
242+
content = '{"id": 42, "foo": "bar"}'
243+
return response(200, content, headers, None, 5, request)
244+
245+
with HTTMock(resp_cont):
246+
mgr = M(self.gl)
247+
obj = mgr.create({'foo': 'bar'})
248+
self.assertIsInstance(obj, FakeObject)
249+
self.assertEqual(obj.id, 42)
250+
self.assertEqual(obj.foo, 'bar')
251+
252+
def test_create_mixin_custom_path(self):
253+
class M(CreateMixin, FakeManager):
254+
_create_attrs = (('foo',), ('bar', 'baz'))
255+
_update_attrs = (('foo',), ('bam', ))
256+
257+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/others',
258+
method="post")
259+
def resp_cont(url, request):
260+
headers = {'Content-Type': 'application/json'}
261+
content = '{"id": 42, "foo": "bar"}'
262+
return response(200, content, headers, None, 5, request)
263+
264+
with HTTMock(resp_cont):
265+
mgr = M(self.gl)
266+
obj = mgr.create({'foo': 'bar'}, path='/others')
267+
self.assertIsInstance(obj, FakeObject)
268+
self.assertEqual(obj.id, 42)
269+
self.assertEqual(obj.foo, 'bar')
270+
271+
def test_update_mixin_get_attrs(self):
272+
class M1(UpdateMixin, FakeManager):
273+
pass
274+
275+
class M2(UpdateMixin, FakeManager):
276+
_create_attrs = (('foo',), ('bar', 'baz'))
277+
_update_attrs = (('foo',), ('bam', ))
278+
279+
mgr = M1(self.gl)
280+
required, optional = mgr.get_update_attrs()
281+
self.assertEqual(len(required), 0)
282+
self.assertEqual(len(optional), 0)
283+
284+
mgr = M2(self.gl)
285+
required, optional = mgr.get_update_attrs()
286+
self.assertIn('foo', required)
287+
self.assertIn('bam', optional)
288+
self.assertNotIn('bar', optional)
289+
self.assertNotIn('baz', optional)
290+
291+
def test_update_mixin_missing_attrs(self):
292+
class M(UpdateMixin, FakeManager):
293+
_update_attrs = (('foo',), ('bar', 'baz'))
294+
295+
mgr = M(self.gl)
296+
data = {'foo': 'bar', 'baz': 'blah'}
297+
mgr._check_missing_update_attrs(data)
298+
299+
data = {'baz': 'blah'}
300+
with self.assertRaises(AttributeError) as error:
301+
mgr._check_missing_update_attrs(data)
302+
self.assertIn('foo', str(error.exception))
303+
304+
def test_update_mixin(self):
305+
class M(UpdateMixin, FakeManager):
306+
_create_attrs = (('foo',), ('bar', 'baz'))
307+
_update_attrs = (('foo',), ('bam', ))
308+
309+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
310+
method="put")
311+
def resp_cont(url, request):
312+
headers = {'Content-Type': 'application/json'}
313+
content = '{"id": 42, "foo": "baz"}'
314+
return response(200, content, headers, None, 5, request)
315+
316+
with HTTMock(resp_cont):
317+
mgr = M(self.gl)
318+
server_data = mgr.update(42, {'foo': 'baz'})
319+
self.assertIsInstance(server_data, dict)
320+
self.assertEqual(server_data['id'], 42)
321+
self.assertEqual(server_data['foo'], 'baz')
322+
323+
def test_update_mixin_no_id(self):
324+
class M(UpdateMixin, FakeManager):
325+
_create_attrs = (('foo',), ('bar', 'baz'))
326+
_update_attrs = (('foo',), ('bam', ))
327+
328+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
329+
method="put")
330+
def resp_cont(url, request):
331+
headers = {'Content-Type': 'application/json'}
332+
content = '{"foo": "baz"}'
333+
return response(200, content, headers, None, 5, request)
334+
335+
with HTTMock(resp_cont):
336+
mgr = M(self.gl)
337+
server_data = mgr.update(new_data={'foo': 'baz'})
338+
self.assertIsInstance(server_data, dict)
339+
self.assertEqual(server_data['foo'], 'baz')
340+
341+
def test_delete_mixin(self):
342+
class M(DeleteMixin, FakeManager):
343+
pass
344+
345+
@urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
346+
method="delete")
347+
def resp_cont(url, request):
348+
headers = {'Content-Type': 'application/json'}
349+
content = ''
350+
return response(200, content, headers, None, 5, request)
351+
352+
with HTTMock(resp_cont):
353+
mgr = M(self.gl)
354+
mgr.delete(42)

0 commit comments

Comments
 (0)