##// END OF EJS Templates
tests: test fixes after py311 compat run
super-admin -
r1115:508a6554 python3
parent child Browse files
Show More
@@ -1,103 +1,103 b''
1 1 #SHELL = /bin/bash
2 2
3 3 # set by: PATH_TO_OUTDATED_PACKAGES=/some/path/outdated_packages.py
4 4 OUTDATED_PACKAGES = ${PATH_TO_OUTDATED_PACKAGES}
5 5
6 6 .PHONY: clean
7 7 ## Cleanup compiled and cache py files
8 8 clean:
9 9 make test-clean
10 10 find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' -o -iname '*.orig' \) -exec rm '{}' ';'
11 11
12 12
13 13 .PHONY: test
14 14 ## run test-clean and tests
15 15 test:
16 16 make test-clean
17 17 make test-only
18 18
19 19
20 20 .PHONY: test-clean
21 21 ## run test-clean and tests
22 22 test-clean:
23 23 rm -rf coverage.xml htmlcov junit.xml pylint.log result
24 24 find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';'
25 25 find . -type f \( -iname '.coverage.*' \) -exec rm '{}' ';'
26 26
27 27
28 28 .PHONY: test-only
29 29 ## Run tests only without cleanup
30 30 test-only:
31 31 PYTHONHASHSEED=random \
32 32 py.test -x -vv -r xw -p no:sugar \
33 33 --cov-report=term-missing --cov-report=html --cov=vcsserver vcsserver
34 34
35 35
36 36 .PHONY: pip-packages
37 37 ## Show outdated packages
38 38 pip-packages:
39 39 python ${OUTDATED_PACKAGES}
40 40
41 41
42 42 .PHONY: build
43 43 ## Build sdist/egg
44 44 build:
45 45 python -m build
46 46
47 47
48 48 .PHONY: dev-env
49 49 ## make dev-env based on the requirements files and install develop of packages
50 50 dev-env:
51 pip install build virtualenv pipdeptree
51 pip install build virtualenv
52 52 pip wheel --wheel-dir=/home/rhodecode/.cache/pip/wheels -r requirements.txt -r requirements_test.txt -r requirements_debug.txt
53 53 pip install --no-index --find-links=/home/rhodecode/.cache/pip/wheels -r requirements.txt -r requirements_test.txt -r requirements_debug.txt
54 54 pip install -e .
55 55
56 56
57 57 .PHONY: dev-srv
58 58 ## run develop server instance
59 59 dev-srv:
60 60 pserve --reload .dev/dev.ini
61 61
62 62
63 63 # Default command on calling make
64 64 .DEFAULT_GOAL := show-help
65 65
66 66 .PHONY: show-help
67 67 show-help:
68 68 @echo "$$(tput bold)Available rules:$$(tput sgr0)"
69 69 @echo
70 70 @sed -n -e "/^## / { \
71 71 h; \
72 72 s/.*//; \
73 73 :doc" \
74 74 -e "H; \
75 75 n; \
76 76 s/^## //; \
77 77 t doc" \
78 78 -e "s/:.*//; \
79 79 G; \
80 80 s/\\n## /---/; \
81 81 s/\\n/ /g; \
82 82 p; \
83 83 }" ${MAKEFILE_LIST} \
84 84 | LC_ALL='C' sort --ignore-case \
85 85 | awk -F '---' \
86 86 -v ncol=$$(tput cols) \
87 87 -v indent=19 \
88 88 -v col_on="$$(tput setaf 6)" \
89 89 -v col_off="$$(tput sgr0)" \
90 90 '{ \
91 91 printf "%s%*s%s ", col_on, -indent, $$1, col_off; \
92 92 n = split($$2, words, " "); \
93 93 line_length = ncol - indent; \
94 94 for (i = 1; i <= n; i++) { \
95 95 line_length -= length(words[i]) + 1; \
96 96 if (line_length <= 0) { \
97 97 line_length = ncol - indent - length(words[i]) - 1; \
98 98 printf "\n%*s ", -indent, " "; \
99 99 } \
100 100 printf "%s ", words[i]; \
101 101 } \
102 102 printf "\n"; \
103 103 }'
@@ -1,12 +1,22 b''
1 1 ## special libraries we could extend the requirements.txt file with to add some
2 ## custom libraries useful for debug and memory tracing
3
4 ## uncomment inclusion of this file in requirements.txt run make generate-pkgs and nix-shell
2 ## custom libraries usefull for debug and memory tracing
5 3
6 4 objgraph
7 5 memory-profiler
8 6 pympler
7
8 ## debug
9 9 ipdb
10 10 ipython
11 rich
12
13 # format
11 14 flake8
12 rich
15 ruff
16
17 pipdeptree==2.7.1
18 invoke==2.0.0
19 bumpversion==0.6.0
20 bump2version==1.0.1
21
22 docutils-stubs
@@ -1,286 +1,286 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2020 RhodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import threading
19 19 import msgpack
20 20
21 21 from http.server import BaseHTTPRequestHandler
22 22 from socketserver import TCPServer
23 23
24 24 import mercurial.ui
25 25 import mock
26 26 import pytest
27 27
28 28 from vcsserver.hooks import HooksHttpClient
29 29 from vcsserver.lib.rc_json import json
30 30 from vcsserver import hooks
31 31
32 32
33 33 def get_hg_ui(extras=None):
34 34 """Create a Config object with a valid RC_SCM_DATA entry."""
35 35 extras = extras or {}
36 36 required_extras = {
37 37 'username': '',
38 38 'repository': '',
39 39 'locked_by': '',
40 40 'scm': '',
41 41 'make_lock': '',
42 42 'action': '',
43 43 'ip': '',
44 44 'hooks_uri': 'fake_hooks_uri',
45 45 }
46 46 required_extras.update(extras)
47 47 hg_ui = mercurial.ui.ui()
48 48 hg_ui.setconfig(b'rhodecode', b'RC_SCM_DATA', json.dumps(required_extras))
49 49
50 50 return hg_ui
51 51
52 52
53 53 def test_git_pre_receive_is_disabled():
54 54 extras = {'hooks': ['pull']}
55 55 response = hooks.git_pre_receive(None, None,
56 56 {'RC_SCM_DATA': json.dumps(extras)})
57 57
58 58 assert response == 0
59 59
60 60
61 61 def test_git_post_receive_is_disabled():
62 62 extras = {'hooks': ['pull']}
63 63 response = hooks.git_post_receive(None, '',
64 64 {'RC_SCM_DATA': json.dumps(extras)})
65 65
66 66 assert response == 0
67 67
68 68
69 69 def test_git_post_receive_calls_repo_size():
70 70 extras = {'hooks': ['push', 'repo_size']}
71 71
72 72 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
73 73 hooks.git_post_receive(
74 74 None, '', {'RC_SCM_DATA': json.dumps(extras)})
75 75 extras.update({'commit_ids': [], 'hook_type': 'post_receive',
76 76 'new_refs': {'bookmarks': [], 'branches': [], 'tags': []}})
77 77 expected_calls = [
78 78 mock.call('repo_size', extras, mock.ANY),
79 79 mock.call('post_push', extras, mock.ANY),
80 80 ]
81 81 assert call_hook_mock.call_args_list == expected_calls
82 82
83 83
84 84 def test_git_post_receive_does_not_call_disabled_repo_size():
85 85 extras = {'hooks': ['push']}
86 86
87 87 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
88 88 hooks.git_post_receive(
89 89 None, '', {'RC_SCM_DATA': json.dumps(extras)})
90 90 extras.update({'commit_ids': [], 'hook_type': 'post_receive',
91 91 'new_refs': {'bookmarks': [], 'branches': [], 'tags': []}})
92 92 expected_calls = [
93 93 mock.call('post_push', extras, mock.ANY)
94 94 ]
95 95 assert call_hook_mock.call_args_list == expected_calls
96 96
97 97
98 98 def test_repo_size_exception_does_not_affect_git_post_receive():
99 99 extras = {'hooks': ['push', 'repo_size']}
100 100 status = 0
101 101
102 102 def side_effect(name, *args, **kwargs):
103 103 if name == 'repo_size':
104 104 raise Exception('Fake exception')
105 105 else:
106 106 return status
107 107
108 108 with mock.patch.object(hooks, '_call_hook') as call_hook_mock:
109 109 call_hook_mock.side_effect = side_effect
110 110 result = hooks.git_post_receive(
111 111 None, '', {'RC_SCM_DATA': json.dumps(extras)})
112 112 assert result == status
113 113
114 114
115 115 def test_git_pre_pull_is_disabled():
116 116 assert hooks.git_pre_pull({'hooks': ['push']}) == hooks.HookResponse(0, '')
117 117
118 118
119 119 def test_git_post_pull_is_disabled():
120 120 assert (
121 121 hooks.git_post_pull({'hooks': ['push']}) == hooks.HookResponse(0, ''))
122 122
123 123
124 124 class TestGetHooksClient(object):
125 125
126 126 def test_returns_http_client_when_protocol_matches(self):
127 127 hooks_uri = 'localhost:8000'
128 128 result = hooks._get_hooks_client({
129 129 'hooks_uri': hooks_uri,
130 130 'hooks_protocol': 'http'
131 131 })
132 132 assert isinstance(result, hooks.HooksHttpClient)
133 133 assert result.hooks_uri == hooks_uri
134 134
135 135 def test_returns_dummy_client_when_hooks_uri_not_specified(self):
136 136 fake_module = mock.Mock()
137 137 import_patcher = mock.patch.object(
138 138 hooks.importlib, 'import_module', return_value=fake_module)
139 139 fake_module_name = 'fake.module'
140 140 with import_patcher as import_mock:
141 141 result = hooks._get_hooks_client(
142 142 {'hooks_module': fake_module_name})
143 143
144 144 import_mock.assert_called_once_with(fake_module_name)
145 145 assert isinstance(result, hooks.HooksDummyClient)
146 146 assert result._hooks_module == fake_module
147 147
148 148
149 149 class TestHooksHttpClient(object):
150 150 def test_init_sets_hooks_uri(self):
151 151 uri = 'localhost:3000'
152 152 client = hooks.HooksHttpClient(uri)
153 153 assert client.hooks_uri == uri
154 154
155 155 def test_serialize_returns_serialized_string(self):
156 156 client = hooks.HooksHttpClient('localhost:3000')
157 157 hook_name = 'test'
158 158 extras = {
159 159 'first': 1,
160 160 'second': 'two'
161 161 }
162 162 hooks_proto, result = client._serialize(hook_name, extras)
163 163 expected_result = msgpack.packb({
164 164 'method': hook_name,
165 165 'extras': extras,
166 166 })
167 assert hooks_proto == {'rc-hooks-protocol': 'msgpack.v1'}
167 assert hooks_proto == {'rc-hooks-protocol': 'msgpack.v1', 'Connection': 'keep-alive'}
168 168 assert result == expected_result
169 169
170 170 def test_call_queries_http_server(self, http_mirror):
171 171 client = hooks.HooksHttpClient(http_mirror.uri)
172 172 hook_name = 'test'
173 173 extras = {
174 174 'first': 1,
175 175 'second': 'two'
176 176 }
177 177 result = client(hook_name, extras)
178 178 expected_result = msgpack.unpackb(msgpack.packb({
179 179 'method': hook_name,
180 180 'extras': extras
181 181 }), raw=False)
182 182 assert result == expected_result
183 183
184 184
185 185 class TestHooksDummyClient(object):
186 186 def test_init_imports_hooks_module(self):
187 187 hooks_module_name = 'rhodecode.fake.module'
188 188 hooks_module = mock.MagicMock()
189 189
190 190 import_patcher = mock.patch.object(
191 191 hooks.importlib, 'import_module', return_value=hooks_module)
192 192 with import_patcher as import_mock:
193 193 client = hooks.HooksDummyClient(hooks_module_name)
194 194 import_mock.assert_called_once_with(hooks_module_name)
195 195 assert client._hooks_module == hooks_module
196 196
197 197 def test_call_returns_hook_result(self):
198 198 hooks_module_name = 'rhodecode.fake.module'
199 199 hooks_module = mock.MagicMock()
200 200 import_patcher = mock.patch.object(
201 201 hooks.importlib, 'import_module', return_value=hooks_module)
202 202 with import_patcher:
203 203 client = hooks.HooksDummyClient(hooks_module_name)
204 204
205 205 result = client('post_push', {})
206 206 hooks_module.Hooks.assert_called_once_with()
207 207 assert result == hooks_module.Hooks().__enter__().post_push()
208 208
209 209
210 210 @pytest.fixture
211 211 def http_mirror(request):
212 212 server = MirrorHttpServer()
213 213 request.addfinalizer(server.stop)
214 214 return server
215 215
216 216
217 217 class MirrorHttpHandler(BaseHTTPRequestHandler):
218 218
219 219 def do_POST(self):
220 220 length = int(self.headers['Content-Length'])
221 221 body = self.rfile.read(length)
222 222 self.send_response(200)
223 223 self.end_headers()
224 224 self.wfile.write(body)
225 225
226 226
227 227 class MirrorHttpServer(object):
228 228 ip_address = '127.0.0.1'
229 229 port = 0
230 230
231 231 def __init__(self):
232 232 self._daemon = TCPServer((self.ip_address, 0), MirrorHttpHandler)
233 233 _, self.port = self._daemon.server_address
234 234 self._thread = threading.Thread(target=self._daemon.serve_forever)
235 235 self._thread.daemon = True
236 236 self._thread.start()
237 237
238 238 def stop(self):
239 239 self._daemon.shutdown()
240 240 self._thread.join()
241 241 self._daemon = None
242 242 self._thread = None
243 243
244 244 @property
245 245 def uri(self):
246 246 return '{}:{}'.format(self.ip_address, self.port)
247 247
248 248
249 249 def test_hooks_http_client_init():
250 250 hooks_uri = 'http://localhost:8000'
251 251 client = HooksHttpClient(hooks_uri)
252 252 assert client.hooks_uri == hooks_uri
253 253
254 254
255 255 def test_hooks_http_client_call():
256 256 hooks_uri = 'http://localhost:8000'
257 257
258 258 method = 'test_method'
259 259 extras = {'key': 'value'}
260 260
261 261 with \
262 mock.patch('vcsserver.hooks.HTTPConnection') as mock_connection,\
262 mock.patch('http.client.HTTPConnection') as mock_connection,\
263 263 mock.patch('msgpack.load') as mock_load:
264 264
265 265 client = HooksHttpClient(hooks_uri)
266 266
267 267 mock_load.return_value = {'result': 'success'}
268 268 response = mock.MagicMock()
269 269 response.status = 200
270 270 mock_connection.request.side_effect = None
271 271 mock_connection.getresponse.return_value = response
272 272
273 273 result = client(method, extras)
274 274
275 275 mock_connection.assert_called_with(hooks_uri)
276 276 mock_connection.return_value.request.assert_called_once()
277 277 assert result == {'result': 'success'}
278 278
279 279
280 280 def test_hooks_http_client_serialize():
281 281 method = 'test_method'
282 282 extras = {'key': 'value'}
283 283 headers, body = HooksHttpClient._serialize(method, extras)
284 284
285 assert headers == {'rc-hooks-protocol': HooksHttpClient.proto}
285 assert headers == {'rc-hooks-protocol': HooksHttpClient.proto, 'Connection': 'keep-alive'}
286 286 assert msgpack.unpackb(body) == {'method': method, 'extras': extras}
General Comments 0
You need to be logged in to leave comments. Login now