##// END OF EJS Templates
tests: added few generated tests
super-admin -
r1087:cde6d2b1 python3
parent child Browse files
Show More
@@ -1,245 +1,286 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2020 RhodeCode GmbH
2 # Copyright (C) 2014-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import threading
18 import threading
19 import msgpack
19 import msgpack
20
20
21 from http.server import BaseHTTPRequestHandler
21 from http.server import BaseHTTPRequestHandler
22 from socketserver import TCPServer
22 from socketserver import TCPServer
23
23
24 import mercurial.ui
24 import mercurial.ui
25 import mock
25 import mock
26 import pytest
26 import pytest
27
27
28 from vcsserver.hooks import HooksHttpClient
28 from vcsserver.lib.rc_json import json
29 from vcsserver.lib.rc_json import json
29 from vcsserver import hooks
30 from vcsserver import hooks
30
31
31
32
32 def get_hg_ui(extras=None):
33 def get_hg_ui(extras=None):
33 """Create a Config object with a valid RC_SCM_DATA entry."""
34 """Create a Config object with a valid RC_SCM_DATA entry."""
34 extras = extras or {}
35 extras = extras or {}
35 required_extras = {
36 required_extras = {
36 'username': '',
37 'username': '',
37 'repository': '',
38 'repository': '',
38 'locked_by': '',
39 'locked_by': '',
39 'scm': '',
40 'scm': '',
40 'make_lock': '',
41 'make_lock': '',
41 'action': '',
42 'action': '',
42 'ip': '',
43 'ip': '',
43 'hooks_uri': 'fake_hooks_uri',
44 'hooks_uri': 'fake_hooks_uri',
44 }
45 }
45 required_extras.update(extras)
46 required_extras.update(extras)
46 hg_ui = mercurial.ui.ui()
47 hg_ui = mercurial.ui.ui()
47 hg_ui.setconfig(b'rhodecode', b'RC_SCM_DATA', json.dumps(required_extras))
48 hg_ui.setconfig(b'rhodecode', b'RC_SCM_DATA', json.dumps(required_extras))
48
49
49 return hg_ui
50 return hg_ui
50
51
51
52
52 def test_git_pre_receive_is_disabled():
53 def test_git_pre_receive_is_disabled():
53 extras = {'hooks': ['pull']}
54 extras = {'hooks': ['pull']}
54 response = hooks.git_pre_receive(None, None,
55 response = hooks.git_pre_receive(None, None,
55 {'RC_SCM_DATA': json.dumps(extras)})
56 {'RC_SCM_DATA': json.dumps(extras)})
56
57
57 assert response == 0
58 assert response == 0
58
59
59
60
60 def test_git_post_receive_is_disabled():
61 def test_git_post_receive_is_disabled():
61 extras = {'hooks': ['pull']}
62 extras = {'hooks': ['pull']}
62 response = hooks.git_post_receive(None, '',
63 response = hooks.git_post_receive(None, '',
63 {'RC_SCM_DATA': json.dumps(extras)})
64 {'RC_SCM_DATA': json.dumps(extras)})
64
65
65 assert response == 0
66 assert response == 0
66
67
67
68
68 def test_git_post_receive_calls_repo_size():
69 def test_git_post_receive_calls_repo_size():
69 extras = {'hooks': ['push', 'repo_size']}
70 extras = {'hooks': ['push', 'repo_size']}
70
71
71 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
72 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
72 hooks.git_post_receive(
73 hooks.git_post_receive(
73 None, '', {'RC_SCM_DATA': json.dumps(extras)})
74 None, '', {'RC_SCM_DATA': json.dumps(extras)})
74 extras.update({'commit_ids': [], 'hook_type': 'post_receive',
75 extras.update({'commit_ids': [], 'hook_type': 'post_receive',
75 'new_refs': {'bookmarks': [], 'branches': [], 'tags': []}})
76 'new_refs': {'bookmarks': [], 'branches': [], 'tags': []}})
76 expected_calls = [
77 expected_calls = [
77 mock.call('repo_size', extras, mock.ANY),
78 mock.call('repo_size', extras, mock.ANY),
78 mock.call('post_push', extras, mock.ANY),
79 mock.call('post_push', extras, mock.ANY),
79 ]
80 ]
80 assert call_hook_mock.call_args_list == expected_calls
81 assert call_hook_mock.call_args_list == expected_calls
81
82
82
83
83 def test_git_post_receive_does_not_call_disabled_repo_size():
84 def test_git_post_receive_does_not_call_disabled_repo_size():
84 extras = {'hooks': ['push']}
85 extras = {'hooks': ['push']}
85
86
86 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
87 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
87 hooks.git_post_receive(
88 hooks.git_post_receive(
88 None, '', {'RC_SCM_DATA': json.dumps(extras)})
89 None, '', {'RC_SCM_DATA': json.dumps(extras)})
89 extras.update({'commit_ids': [], 'hook_type': 'post_receive',
90 extras.update({'commit_ids': [], 'hook_type': 'post_receive',
90 'new_refs': {'bookmarks': [], 'branches': [], 'tags': []}})
91 'new_refs': {'bookmarks': [], 'branches': [], 'tags': []}})
91 expected_calls = [
92 expected_calls = [
92 mock.call('post_push', extras, mock.ANY)
93 mock.call('post_push', extras, mock.ANY)
93 ]
94 ]
94 assert call_hook_mock.call_args_list == expected_calls
95 assert call_hook_mock.call_args_list == expected_calls
95
96
96
97
97 def test_repo_size_exception_does_not_affect_git_post_receive():
98 def test_repo_size_exception_does_not_affect_git_post_receive():
98 extras = {'hooks': ['push', 'repo_size']}
99 extras = {'hooks': ['push', 'repo_size']}
99 status = 0
100 status = 0
100
101
101 def side_effect(name, *args, **kwargs):
102 def side_effect(name, *args, **kwargs):
102 if name == 'repo_size':
103 if name == 'repo_size':
103 raise Exception('Fake exception')
104 raise Exception('Fake exception')
104 else:
105 else:
105 return status
106 return status
106
107
107 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
108 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
108 call_hook_mock.side_effect = side_effect
109 call_hook_mock.side_effect = side_effect
109 result = hooks.git_post_receive(
110 result = hooks.git_post_receive(
110 None, '', {'RC_SCM_DATA': json.dumps(extras)})
111 None, '', {'RC_SCM_DATA': json.dumps(extras)})
111 assert result == status
112 assert result == status
112
113
113
114
114 def test_git_pre_pull_is_disabled():
115 def test_git_pre_pull_is_disabled():
115 assert hooks.git_pre_pull({'hooks': ['push']}) == hooks.HookResponse(0, '')
116 assert hooks.git_pre_pull({'hooks': ['push']}) == hooks.HookResponse(0, '')
116
117
117
118
118 def test_git_post_pull_is_disabled():
119 def test_git_post_pull_is_disabled():
119 assert (
120 assert (
120 hooks.git_post_pull({'hooks': ['push']}) == hooks.HookResponse(0, ''))
121 hooks.git_post_pull({'hooks': ['push']}) == hooks.HookResponse(0, ''))
121
122
122
123
123 class TestGetHooksClient(object):
124 class TestGetHooksClient(object):
124
125
125 def test_returns_http_client_when_protocol_matches(self):
126 def test_returns_http_client_when_protocol_matches(self):
126 hooks_uri = 'localhost:8000'
127 hooks_uri = 'localhost:8000'
127 result = hooks._get_hooks_client({
128 result = hooks._get_hooks_client({
128 'hooks_uri': hooks_uri,
129 'hooks_uri': hooks_uri,
129 'hooks_protocol': 'http'
130 'hooks_protocol': 'http'
130 })
131 })
131 assert isinstance(result, hooks.HooksHttpClient)
132 assert isinstance(result, hooks.HooksHttpClient)
132 assert result.hooks_uri == hooks_uri
133 assert result.hooks_uri == hooks_uri
133
134
134 def test_returns_dummy_client_when_hooks_uri_not_specified(self):
135 def test_returns_dummy_client_when_hooks_uri_not_specified(self):
135 fake_module = mock.Mock()
136 fake_module = mock.Mock()
136 import_patcher = mock.patch.object(
137 import_patcher = mock.patch.object(
137 hooks.importlib, 'import_module', return_value=fake_module)
138 hooks.importlib, 'import_module', return_value=fake_module)
138 fake_module_name = 'fake.module'
139 fake_module_name = 'fake.module'
139 with import_patcher as import_mock:
140 with import_patcher as import_mock:
140 result = hooks._get_hooks_client(
141 result = hooks._get_hooks_client(
141 {'hooks_module': fake_module_name})
142 {'hooks_module': fake_module_name})
142
143
143 import_mock.assert_called_once_with(fake_module_name)
144 import_mock.assert_called_once_with(fake_module_name)
144 assert isinstance(result, hooks.HooksDummyClient)
145 assert isinstance(result, hooks.HooksDummyClient)
145 assert result._hooks_module == fake_module
146 assert result._hooks_module == fake_module
146
147
147
148
148 class TestHooksHttpClient(object):
149 class TestHooksHttpClient(object):
149 def test_init_sets_hooks_uri(self):
150 def test_init_sets_hooks_uri(self):
150 uri = 'localhost:3000'
151 uri = 'localhost:3000'
151 client = hooks.HooksHttpClient(uri)
152 client = hooks.HooksHttpClient(uri)
152 assert client.hooks_uri == uri
153 assert client.hooks_uri == uri
153
154
154 def test_serialize_returns_serialized_string(self):
155 def test_serialize_returns_serialized_string(self):
155 client = hooks.HooksHttpClient('localhost:3000')
156 client = hooks.HooksHttpClient('localhost:3000')
156 hook_name = 'test'
157 hook_name = 'test'
157 extras = {
158 extras = {
158 'first': 1,
159 'first': 1,
159 'second': 'two'
160 'second': 'two'
160 }
161 }
161 hooks_proto, result = client._serialize(hook_name, extras)
162 hooks_proto, result = client._serialize(hook_name, extras)
162 expected_result = msgpack.packb({
163 expected_result = msgpack.packb({
163 'method': hook_name,
164 'method': hook_name,
164 'extras': extras,
165 'extras': extras,
165 })
166 })
166 assert hooks_proto == {'rc-hooks-protocol': 'msgpack.v1'}
167 assert hooks_proto == {'rc-hooks-protocol': 'msgpack.v1'}
167 assert result == expected_result
168 assert result == expected_result
168
169
169 def test_call_queries_http_server(self, http_mirror):
170 def test_call_queries_http_server(self, http_mirror):
170 client = hooks.HooksHttpClient(http_mirror.uri)
171 client = hooks.HooksHttpClient(http_mirror.uri)
171 hook_name = 'test'
172 hook_name = 'test'
172 extras = {
173 extras = {
173 'first': 1,
174 'first': 1,
174 'second': 'two'
175 'second': 'two'
175 }
176 }
176 result = client(hook_name, extras)
177 result = client(hook_name, extras)
177 expected_result = msgpack.unpackb(msgpack.packb({
178 expected_result = msgpack.unpackb(msgpack.packb({
178 'method': hook_name,
179 'method': hook_name,
179 'extras': extras
180 'extras': extras
180 }), raw=False)
181 }), raw=False)
181 assert result == expected_result
182 assert result == expected_result
182
183
183
184
184 class TestHooksDummyClient(object):
185 class TestHooksDummyClient(object):
185 def test_init_imports_hooks_module(self):
186 def test_init_imports_hooks_module(self):
186 hooks_module_name = 'rhodecode.fake.module'
187 hooks_module_name = 'rhodecode.fake.module'
187 hooks_module = mock.MagicMock()
188 hooks_module = mock.MagicMock()
188
189
189 import_patcher = mock.patch.object(
190 import_patcher = mock.patch.object(
190 hooks.importlib, 'import_module', return_value=hooks_module)
191 hooks.importlib, 'import_module', return_value=hooks_module)
191 with import_patcher as import_mock:
192 with import_patcher as import_mock:
192 client = hooks.HooksDummyClient(hooks_module_name)
193 client = hooks.HooksDummyClient(hooks_module_name)
193 import_mock.assert_called_once_with(hooks_module_name)
194 import_mock.assert_called_once_with(hooks_module_name)
194 assert client._hooks_module == hooks_module
195 assert client._hooks_module == hooks_module
195
196
196 def test_call_returns_hook_result(self):
197 def test_call_returns_hook_result(self):
197 hooks_module_name = 'rhodecode.fake.module'
198 hooks_module_name = 'rhodecode.fake.module'
198 hooks_module = mock.MagicMock()
199 hooks_module = mock.MagicMock()
199 import_patcher = mock.patch.object(
200 import_patcher = mock.patch.object(
200 hooks.importlib, 'import_module', return_value=hooks_module)
201 hooks.importlib, 'import_module', return_value=hooks_module)
201 with import_patcher:
202 with import_patcher:
202 client = hooks.HooksDummyClient(hooks_module_name)
203 client = hooks.HooksDummyClient(hooks_module_name)
203
204
204 result = client('post_push', {})
205 result = client('post_push', {})
205 hooks_module.Hooks.assert_called_once_with()
206 hooks_module.Hooks.assert_called_once_with()
206 assert result == hooks_module.Hooks().__enter__().post_push()
207 assert result == hooks_module.Hooks().__enter__().post_push()
207
208
208
209
209 @pytest.fixture
210 @pytest.fixture
210 def http_mirror(request):
211 def http_mirror(request):
211 server = MirrorHttpServer()
212 server = MirrorHttpServer()
212 request.addfinalizer(server.stop)
213 request.addfinalizer(server.stop)
213 return server
214 return server
214
215
215
216
216 class MirrorHttpHandler(BaseHTTPRequestHandler):
217 class MirrorHttpHandler(BaseHTTPRequestHandler):
217
218
218 def do_POST(self):
219 def do_POST(self):
219 length = int(self.headers['Content-Length'])
220 length = int(self.headers['Content-Length'])
220 body = self.rfile.read(length)
221 body = self.rfile.read(length)
221 self.send_response(200)
222 self.send_response(200)
222 self.end_headers()
223 self.end_headers()
223 self.wfile.write(body)
224 self.wfile.write(body)
224
225
225
226
226 class MirrorHttpServer(object):
227 class MirrorHttpServer(object):
227 ip_address = '127.0.0.1'
228 ip_address = '127.0.0.1'
228 port = 0
229 port = 0
229
230
230 def __init__(self):
231 def __init__(self):
231 self._daemon = TCPServer((self.ip_address, 0), MirrorHttpHandler)
232 self._daemon = TCPServer((self.ip_address, 0), MirrorHttpHandler)
232 _, self.port = self._daemon.server_address
233 _, self.port = self._daemon.server_address
233 self._thread = threading.Thread(target=self._daemon.serve_forever)
234 self._thread = threading.Thread(target=self._daemon.serve_forever)
234 self._thread.daemon = True
235 self._thread.daemon = True
235 self._thread.start()
236 self._thread.start()
236
237
237 def stop(self):
238 def stop(self):
238 self._daemon.shutdown()
239 self._daemon.shutdown()
239 self._thread.join()
240 self._thread.join()
240 self._daemon = None
241 self._daemon = None
241 self._thread = None
242 self._thread = None
242
243
243 @property
244 @property
244 def uri(self):
245 def uri(self):
245 return '{}:{}'.format(self.ip_address, self.port)
246 return '{}:{}'.format(self.ip_address, self.port)
247
248
249 def test_hooks_http_client_init():
250 hooks_uri = 'http://localhost:8000'
251 client = HooksHttpClient(hooks_uri)
252 assert client.hooks_uri == hooks_uri
253
254
255 def test_hooks_http_client_call():
256 hooks_uri = 'http://localhost:8000'
257
258 method = 'test_method'
259 extras = {'key': 'value'}
260
261 with \
262 mock.patch('vcsserver.hooks.HTTPConnection') as mock_connection,\
263 mock.patch('msgpack.load') as mock_load:
264
265 client = HooksHttpClient(hooks_uri)
266
267 mock_load.return_value = {'result': 'success'}
268 response = mock.MagicMock()
269 response.status = 200
270 mock_connection.request.side_effect = None
271 mock_connection.getresponse.return_value = response
272
273 result = client(method, extras)
274
275 mock_connection.assert_called_with(hooks_uri)
276 mock_connection.return_value.request.assert_called_once()
277 assert result == {'result': 'success'}
278
279
280 def test_hooks_http_client_serialize():
281 method = 'test_method'
282 extras = {'key': 'value'}
283 headers, body = HooksHttpClient._serialize(method, extras)
284
285 assert headers == {'rc-hooks-protocol': HooksHttpClient.proto}
286 assert msgpack.unpackb(body) == {'method': method, 'extras': extras}
General Comments 0
You need to be logged in to leave comments. Login now