Skip to content

Commit 2968cc1

Browse files
authored
Fix #575: check issue types in heartbeat (#576)
* Fix #575: check issue types in heartbeat * Reduce setup code to make tests more specific
1 parent e9a067f commit 2968cc1

File tree

2 files changed

+61
-30
lines changed

2 files changed

+61
-30
lines changed

jbi/services/jira.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def raise_for_status(self, *args, **kwargs):
8585
set_issue_status = instrumented_method(Jira.set_issue_status)
8686
issue_add_comment = instrumented_method(Jira.issue_add_comment)
8787
create_issue = instrumented_method(Jira.create_issue)
88+
issue_createmeta_issuetypes = instrumented_method(Jira.issue_createmeta_issuetypes)
8889

8990

9091
@lru_cache(maxsize=1)
@@ -116,6 +117,8 @@ def check_health(actions: Actions) -> ServiceHealth:
116117
"all_projects_have_permissions": _all_projects_permissions(actions),
117118
"all_projects_components_exist": is_up
118119
and _all_projects_components_exist(actions),
120+
"all_projects_issue_types_exist": is_up
121+
and _all_project_issue_types_exist(actions),
119122
}
120123
return health
121124

@@ -215,6 +218,27 @@ def _all_projects_components_exist(actions: Actions):
215218
return success
216219

217220

221+
def _all_project_issue_types_exist(actions: Actions):
222+
default_types = {"task": "Task", "defect": "Bug"}
223+
issue_types_by_project = {
224+
action.parameters["jira_project_key"]: set(
225+
action.parameters.get("issues_types_map", default_types).values()
226+
)
227+
for action in actions
228+
}
229+
success = True
230+
for project, specified_issue_types in issue_types_by_project.items():
231+
all_issue_types = get_client().issue_createmeta_issuetypes(project)
232+
all_issue_types_names = set(it["name"] for it in all_issue_types)
233+
unknown = set(specified_issue_types) - all_issue_types_names
234+
if unknown:
235+
logger.error(
236+
"Jira project %s does not have issue type %s", project, unknown
237+
)
238+
success = False
239+
return success
240+
241+
218242
def get_issue(context: ActionContext, issue_key):
219243
"""Return the Jira issue fields or `None` if not found."""
220244
try:

tests/unit/test_router.py

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def test_read_heartbeat_all_services_fail(anon_client, mocked_jira, mocked_bugzi
173173
"all_projects_are_visible": False,
174174
"all_projects_have_permissions": False,
175175
"all_projects_components_exist": False,
176+
"all_projects_issue_types_exist": False,
176177
},
177178
"bugzilla": {
178179
"up": False,
@@ -181,7 +182,7 @@ def test_read_heartbeat_all_services_fail(anon_client, mocked_jira, mocked_bugzi
181182
}
182183

183184

184-
def test_read_heartbeat_jira_services_fails(anon_client, mocked_jira, mocked_bugzilla):
185+
def test_read_heartbeat_jira_services_fails(anon_client, mocked_jira):
185186
"""/__heartbeat__ returns 503 when one service is unavailable."""
186187
mocked_jira.get_server_info.return_value = None
187188

@@ -193,12 +194,11 @@ def test_read_heartbeat_jira_services_fails(anon_client, mocked_jira, mocked_bug
193194
"all_projects_are_visible": False,
194195
"all_projects_have_permissions": False,
195196
"all_projects_components_exist": False,
197+
"all_projects_issue_types_exist": False,
196198
}
197199

198200

199-
def test_read_heartbeat_bugzilla_webhooks_fails(
200-
anon_client, mocked_jira, mocked_bugzilla
201-
):
201+
def test_read_heartbeat_bugzilla_webhooks_fails(anon_client, mocked_bugzilla):
202202
mocked_bugzilla.logged_in.return_value = True
203203
mocked_bugzilla.list_webhooks.return_value = [
204204
bugzilla_webhook_factory(enabled=False)
@@ -213,9 +213,7 @@ def test_read_heartbeat_bugzilla_webhooks_fails(
213213
}
214214

215215

216-
def test_heartbeat_bugzilla_reports_webhooks_errors(
217-
anon_client, mocked_jira, mocked_bugzilla
218-
):
216+
def test_heartbeat_bugzilla_reports_webhooks_errors(anon_client, mocked_bugzilla):
219217
mocked_bugzilla.logged_in.return_value = True
220218
mocked_bugzilla.list_webhooks.return_value = [
221219
bugzilla_webhook_factory(id=1, errors=0, product="Remote Settings"),
@@ -233,13 +231,9 @@ def test_heartbeat_bugzilla_reports_webhooks_errors(
233231
)
234232

235233

236-
def test_read_heartbeat_bugzilla_services_fails(
237-
anon_client, mocked_jira, mocked_bugzilla
238-
):
234+
def test_read_heartbeat_bugzilla_services_fails(anon_client, mocked_bugzilla):
239235
"""/__heartbeat__ returns 503 when one service is unavailable."""
240236
mocked_bugzilla.logged_in.return_value = False
241-
mocked_jira.get_server_info.return_value = {}
242-
mocked_jira.projects.return_value = [{"key": "DevTest"}]
243237

244238
resp = anon_client.get("/__heartbeat__")
245239

@@ -265,6 +259,10 @@ def test_read_heartbeat_success(anon_client, mocked_jira, mocked_bugzilla):
265259
"DELETE_ISSUES": {"havePermission": True},
266260
},
267261
}
262+
mocked_jira.issue_createmeta_issuetypes.return_value = [
263+
{"name": "Task"},
264+
{"name": "Bug"},
265+
]
268266

269267
resp = anon_client.get("/__heartbeat__")
270268

@@ -275,6 +273,7 @@ def test_read_heartbeat_success(anon_client, mocked_jira, mocked_bugzilla):
275273
"all_projects_are_visible": True,
276274
"all_projects_have_permissions": True,
277275
"all_projects_components_exist": True,
276+
"all_projects_issue_types_exist": True,
278277
},
279278
"bugzilla": {
280279
"up": True,
@@ -283,25 +282,20 @@ def test_read_heartbeat_success(anon_client, mocked_jira, mocked_bugzilla):
283282
}
284283

285284

286-
def test_jira_heartbeat_visible_projects(anon_client, mocked_jira, mocked_bugzilla):
285+
def test_jira_heartbeat_visible_projects(anon_client, mocked_jira):
287286
"""/__heartbeat__ fails if configured projects don't match."""
288287
mocked_jira.get_server_info.return_value = {}
289288

290289
resp = anon_client.get("/__heartbeat__")
291290

292291
assert resp.status_code == 503
293-
assert resp.json()["jira"] == {
294-
"up": True,
295-
"all_projects_are_visible": False,
296-
"all_projects_have_permissions": False,
297-
"all_projects_components_exist": False,
298-
}
292+
assert resp.json()["jira"]["up"]
293+
assert not resp.json()["jira"]["all_projects_are_visible"]
299294

300295

301-
def test_jira_heartbeat_missing_permissions(anon_client, mocked_jira, mocked_bugzilla):
296+
def test_jira_heartbeat_missing_permissions(anon_client, mocked_jira):
302297
"""/__heartbeat__ fails if configured projects don't match."""
303298
mocked_jira.get_server_info.return_value = {}
304-
mocked_jira.get_project_components.return_value = [{"name": "Main"}]
305299
mocked_jira.get_project_permission_scheme.return_value = {
306300
"permissions": {
307301
"ADD_COMMENTS": {"havePermission": True},
@@ -314,25 +308,34 @@ def test_jira_heartbeat_missing_permissions(anon_client, mocked_jira, mocked_bug
314308
resp = anon_client.get("/__heartbeat__")
315309

316310
assert resp.status_code == 503
317-
assert resp.json()["jira"] == {
318-
"up": True,
319-
"all_projects_are_visible": False,
320-
"all_projects_have_permissions": False,
321-
"all_projects_components_exist": True,
322-
}
311+
assert resp.json()["jira"]["up"]
312+
assert not resp.json()["jira"]["all_projects_have_permissions"]
323313

324314

325-
def test_jira_heartbeat_unknown_components(anon_client, mocked_jira, mocked_bugzilla):
326-
mocked_bugzilla.logged_in.return_value = True
327-
mocked_bugzilla.list_webhooks.return_value = [bugzilla_webhook_factory()]
315+
def test_jira_heartbeat_unknown_components(anon_client, mocked_jira):
328316
mocked_jira.get_server_info.return_value = {}
329317

330318
resp = anon_client.get("/__heartbeat__")
331319

332320
assert resp.status_code == 503
321+
assert resp.json()["jira"]["up"]
333322
assert not resp.json()["jira"]["all_projects_components_exist"]
334323

335324

325+
def test_jira_heartbeat_unknown_issue_types(anon_client, mocked_jira):
326+
mocked_jira.get_server_info.return_value = {}
327+
mocked_jira.issue_createmeta_issuetypes.return_value = [
328+
{"name": "Task"},
329+
# missing Bug
330+
]
331+
332+
resp = anon_client.get("/__heartbeat__")
333+
334+
assert resp.status_code == 503
335+
assert resp.json()["jira"]["up"]
336+
assert not resp.json()["jira"]["all_projects_issue_types_exist"]
337+
338+
336339
def test_head_heartbeat_success(anon_client, mocked_jira, mocked_bugzilla):
337340
"""/__heartbeat__ support head requests"""
338341
mocked_bugzilla.logged_in.return_value = True
@@ -348,6 +351,10 @@ def test_head_heartbeat_success(anon_client, mocked_jira, mocked_bugzilla):
348351
"DELETE_ISSUES": {"havePermission": True},
349352
},
350353
}
354+
mocked_jira.issue_createmeta_issuetypes.return_value = [
355+
{"name": "Task"},
356+
{"name": "Bug"},
357+
]
351358

352359
resp = anon_client.head("/__heartbeat__")
353360

0 commit comments

Comments
 (0)