|
29 | 29 | import os |
30 | 30 | import shutil |
31 | 31 | import unittest |
| 32 | +import unittest.mock |
32 | 33 |
|
33 | 34 | import httpretty |
| 35 | +import requests |
34 | 36 |
|
35 | 37 | from perceval.backend import BackendCommandArgumentParser |
36 | 38 | from perceval.errors import BackendError |
@@ -128,6 +130,87 @@ def request_callback(method, uri, headers): |
128 | 130 | return http_requests |
129 | 131 |
|
130 | 132 |
|
| 133 | +def setup_http_server_with_initial_error(): |
| 134 | + """Like setup_http_server, but the first /rest/bug page returns HTTP 400. |
| 135 | +
|
| 136 | + Mimics a single BMO bug that 400s on include_fields=_all |
| 137 | + ("Failed to fetch key ... from network storage"), which otherwise |
| 138 | + wedges the whole collection. |
| 139 | + """ |
| 140 | + http_requests = [] |
| 141 | + |
| 142 | + bodies_bugs = [read_file('data/bugzilla/bugzilla_rest_bugs.json', mode='rb'), |
| 143 | + read_file('data/bugzilla/bugzilla_rest_bugs_next.json', mode='rb'), |
| 144 | + read_file('data/bugzilla/bugzilla_rest_bugs_empty.json', mode='rb')] |
| 145 | + body_comments = [read_file('data/bugzilla/bugzilla_rest_bugs_comments.json', mode='rb'), |
| 146 | + read_file('data/bugzilla/bugzilla_rest_bugs_comments_empty.json', mode='rb')] |
| 147 | + body_history = [read_file('data/bugzilla/bugzilla_rest_bugs_history.json', mode='rb'), |
| 148 | + read_file('data/bugzilla/bugzilla_rest_bugs_history_empty.json', mode='rb')] |
| 149 | + body_attachments = [read_file('data/bugzilla/bugzilla_rest_bugs_attachments.json', mode='rb'), |
| 150 | + read_file('data/bugzilla/bugzilla_rest_bugs_attachments_empty.json', mode='rb')] |
| 151 | + |
| 152 | + error_body = ('{"error":true,"code":68000,' |
| 153 | + '"message":"Failed to fetch key 9327669 from network storage: Not Found"}') |
| 154 | + |
| 155 | + def request_callback(method, uri, headers): |
| 156 | + if uri.startswith(BUGZILLA_VERSION_URL): |
| 157 | + body = '{"version":"5.1.2"}' |
| 158 | + elif uri.startswith(BUGZILLA_BUGS_COMMENTS_1273442_URL): |
| 159 | + body = body_comments[0] |
| 160 | + elif uri.startswith(BUGZILLA_BUGS_HISTORY_1273442_URL): |
| 161 | + body = body_history[0] |
| 162 | + elif uri.startswith(BUGZILLA_BUGS_ATTACHMENTS_1273442_URL): |
| 163 | + body = body_attachments[0] |
| 164 | + elif uri.startswith(BUGZILLA_BUG_947945_URL): |
| 165 | + if uri.find('comment') > 0: |
| 166 | + body = body_comments[1] |
| 167 | + elif uri.find('history') > 0: |
| 168 | + body = body_history[1] |
| 169 | + else: |
| 170 | + body = body_attachments[1] |
| 171 | + else: |
| 172 | + body = bodies_bugs.pop(0) |
| 173 | + |
| 174 | + http_requests.append(httpretty.last_request()) |
| 175 | + |
| 176 | + return (200, headers, body) |
| 177 | + |
| 178 | + httpretty.register_uri(httpretty.GET, |
| 179 | + BUGZILLA_VERSION_URL, |
| 180 | + responses=[ |
| 181 | + httpretty.Response(body=request_callback) |
| 182 | + for _ in range(3) |
| 183 | + ]) |
| 184 | + |
| 185 | + # First bug page 400s (the broken bug); the rest succeed. |
| 186 | + httpretty.register_uri(httpretty.GET, |
| 187 | + BUGZILLA_BUGS_URL, |
| 188 | + responses=[ |
| 189 | + httpretty.Response(body=error_body, status=400), |
| 190 | + *[httpretty.Response(body=request_callback) |
| 191 | + for _ in range(3)] |
| 192 | + ]) |
| 193 | + |
| 194 | + http_urls = [BUGZILLA_BUGS_COMMENTS_1273442_URL, |
| 195 | + BUGZILLA_BUGS_HISTORY_1273442_URL, |
| 196 | + BUGZILLA_BUGS_ATTACHMENTS_1273442_URL] |
| 197 | + |
| 198 | + suffixes = ['comment', 'history', 'attachment'] |
| 199 | + |
| 200 | + for http_url in [BUGZILLA_BUG_947945_URL]: |
| 201 | + for suffix in suffixes: |
| 202 | + http_urls.append(http_url + suffix) |
| 203 | + |
| 204 | + for req_url in http_urls: |
| 205 | + httpretty.register_uri(httpretty.GET, |
| 206 | + req_url, |
| 207 | + responses=[ |
| 208 | + httpretty.Response(body=request_callback) |
| 209 | + ]) |
| 210 | + |
| 211 | + return http_requests |
| 212 | + |
| 213 | + |
131 | 214 | class TestBugzillaRESTBackend(unittest.TestCase): |
132 | 215 | """Bugzilla REST backend tests""" |
133 | 216 |
|
@@ -257,6 +340,46 @@ def test_fetch(self): |
257 | 340 | for i in range(len(expected)): |
258 | 341 | self.assertDictEqual(http_requests[i].querystring, expected[i]) |
259 | 342 |
|
| 343 | + @httpretty.activate |
| 344 | + def test_fetch_skips_bug_page_on_http_400(self): |
| 345 | + """Test whether a bug page that returns HTTP 400 is skipped |
| 346 | +
|
| 347 | + A single bug can make Bugzilla return a 400 (e.g. a field whose |
| 348 | + data is missing from network storage). The whole collection must |
| 349 | + not abort: the failing page is skipped and the rest are fetched. |
| 350 | + """ |
| 351 | + setup_http_server_with_initial_error() |
| 352 | + |
| 353 | + bg = BugzillaREST(BUGZILLA_SERVER_URL, max_bugs=2) |
| 354 | + bugs = [bug for bug in bg.fetch(from_date=None)] |
| 355 | + |
| 356 | + # The 400 page was skipped; the remaining pages still yield all bugs. |
| 357 | + self.assertEqual(len(bugs), 3) |
| 358 | + self.assertEqual(bugs[0]['data']['id'], 1273442) |
| 359 | + self.assertEqual(bugs[1]['data']['id'], 1273439) |
| 360 | + self.assertEqual(bugs[2]['data']['id'], 947945) |
| 361 | + |
| 362 | + @httpretty.activate |
| 363 | + def test_fetch_aborts_after_too_many_consecutive_400s(self): |
| 364 | + """Test whether persistent HTTP 400s abort instead of looping forever""" |
| 365 | + |
| 366 | + httpretty.register_uri(httpretty.GET, |
| 367 | + BUGZILLA_VERSION_URL, |
| 368 | + body='{"version":"5.1.2"}') |
| 369 | + error_body = ('{"error":true,"code":68000,' |
| 370 | + '"message":"Failed to fetch key 9327669 from network storage: Not Found"}') |
| 371 | + httpretty.register_uri(httpretty.GET, |
| 372 | + BUGZILLA_BUGS_URL, |
| 373 | + body=error_body, |
| 374 | + status=400) |
| 375 | + |
| 376 | + bg = BugzillaREST(BUGZILLA_SERVER_URL, max_bugs=1) |
| 377 | + |
| 378 | + with unittest.mock.patch( |
| 379 | + 'perceval.backends.core.bugzillarest.MAX_CONSECUTIVE_SKIPS', 3): |
| 380 | + with self.assertRaises(requests.exceptions.HTTPError): |
| 381 | + _ = [bug for bug in bg.fetch(from_date=None)] |
| 382 | + |
260 | 383 | @httpretty.activate |
261 | 384 | def test_search_fields(self): |
262 | 385 | """Test whether the search_fields is properly set""" |
|
0 commit comments