firefighter-incident 0.0.22__py3-none-any.whl → 0.0.23__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. firefighter/_version.py +2 -2
  2. firefighter/api/serializers.py +18 -0
  3. firefighter/api/views/incidents.py +3 -0
  4. firefighter/confluence/models.py +66 -6
  5. firefighter/confluence/signals/incident_updated.py +8 -26
  6. firefighter/firefighter/settings/components/jira_app.py +33 -0
  7. firefighter/incidents/admin.py +3 -0
  8. firefighter/incidents/models/impact.py +3 -5
  9. firefighter/incidents/models/incident.py +24 -9
  10. firefighter/incidents/views/views.py +2 -0
  11. firefighter/jira_app/admin.py +15 -1
  12. firefighter/jira_app/apps.py +3 -0
  13. firefighter/jira_app/client.py +151 -3
  14. firefighter/jira_app/management/__init__.py +1 -0
  15. firefighter/jira_app/management/commands/__init__.py +1 -0
  16. firefighter/jira_app/migrations/0002_add_jira_postmortem_model.py +71 -0
  17. firefighter/jira_app/models.py +50 -0
  18. firefighter/jira_app/service_postmortem.py +292 -0
  19. firefighter/jira_app/signals/__init__.py +10 -0
  20. firefighter/jira_app/signals/incident_key_events_updated.py +88 -0
  21. firefighter/jira_app/signals/postmortem_created.py +155 -0
  22. firefighter/jira_app/templates/jira/postmortem/impact.txt +12 -0
  23. firefighter/jira_app/templates/jira/postmortem/incident_summary.txt +17 -0
  24. firefighter/jira_app/templates/jira/postmortem/mitigation_actions.txt +9 -0
  25. firefighter/jira_app/templates/jira/postmortem/root_causes.txt +12 -0
  26. firefighter/jira_app/templates/jira/postmortem/timeline.txt +7 -0
  27. firefighter/raid/signals/incident_updated.py +31 -11
  28. firefighter/slack/messages/slack_messages.py +39 -3
  29. firefighter/slack/signals/postmortem_created.py +51 -3
  30. firefighter/slack/views/modals/closure_reason.py +15 -0
  31. firefighter/slack/views/modals/key_event_message.py +9 -0
  32. firefighter/slack/views/modals/postmortem.py +32 -40
  33. firefighter/slack/views/modals/update_status.py +7 -1
  34. {firefighter_incident-0.0.22.dist-info → firefighter_incident-0.0.23.dist-info}/METADATA +1 -1
  35. {firefighter_incident-0.0.22.dist-info → firefighter_incident-0.0.23.dist-info}/RECORD +50 -31
  36. {firefighter_incident-0.0.22.dist-info → firefighter_incident-0.0.23.dist-info}/WHEEL +1 -1
  37. firefighter_tests/test_api/test_renderer.py +41 -0
  38. firefighter_tests/test_incidents/test_models/test_incident_model.py +29 -0
  39. firefighter_tests/test_jira_app/test_incident_key_events_sync.py +112 -0
  40. firefighter_tests/test_jira_app/test_models.py +138 -0
  41. firefighter_tests/test_jira_app/test_postmortem_issue_link.py +201 -0
  42. firefighter_tests/test_jira_app/test_postmortem_service.py +416 -0
  43. firefighter_tests/test_jira_app/test_timeline_template.py +135 -0
  44. firefighter_tests/test_raid/test_raid_signals.py +50 -8
  45. firefighter_tests/test_slack/messages/test_slack_messages.py +112 -23
  46. firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py +18 -2
  47. firefighter_tests/test_slack/views/modals/test_key_event_message.py +30 -0
  48. firefighter_tests/test_slack/views/modals/test_update_status.py +161 -129
  49. {firefighter_incident-0.0.22.dist-info → firefighter_incident-0.0.23.dist-info}/entry_points.txt +0 -0
  50. {firefighter_incident-0.0.22.dist-info → firefighter_incident-0.0.23.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ gunicorn.conf.py,sha256=vHsTGjaKOr8FDMp6fTKYTX4AtokmPgYvvt5Mr0Q6APc,273
6
6
  main.py,sha256=CsbprHoOYhjCLpTJmq9Z_aRYFoFgWxoz2pDLuwm8Eqg,1558
7
7
  manage.py,sha256=5ivHGD13C6nJ8QvltKsJ9T9akA5he8da70HLWaEP3k8,689
8
8
  firefighter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- firefighter/_version.py,sha256=ppqXAvMQkDCf5uPK5J4O6hozIheDimMJFhFAaY9euV0,706
9
+ firefighter/_version.py,sha256=ibKwfdtUKJ8ftEJ-ANHeQVfItYjoZhYBzigrJx5AdJU,706
10
10
  firefighter/api/__init__.py,sha256=JQW0Bv6xwGqy7ioxx3h6UGMzkkJ4DntDpbvV1Ncgi8k,136
11
11
  firefighter/api/admin.py,sha256=x9Ysy-GiYjb0rynmFdS9g56e6n24fkN0ouGy5QD9Yrc,4629
12
12
  firefighter/api/apps.py,sha256=P5uU1_gMrDfzurdMbfqw1Bnb2uNKKcMq17WBPg2sLhc,204
@@ -14,7 +14,7 @@ firefighter/api/authentication.py,sha256=1OEeTF0NU54ZLgq2Vy3GxrR-7skj6HmobVp4UGP
14
14
  firefighter/api/models.py,sha256=HJh0PtlrUX8HVCdcdQlClwA6kIFVYnivnKmiQ30D0nQ,1533
15
15
  firefighter/api/permissions.py,sha256=1uxMg5ylFkmvIIonJVlQKL0RJAcgAY79a0y5ROiNrTg,608
16
16
  firefighter/api/renderer.py,sha256=RCQFnjZBIq9fF3dapu3kid2B1s0in8XOEBJmbh5cPlU,4057
17
- firefighter/api/serializers.py,sha256=DcPA22PuEyWrtdmYovuB4pi_RF1oSqc2d3a16pxJ-50,10586
17
+ firefighter/api/serializers.py,sha256=Dbixi7HIVnqzv9zuB6NU9syakdkz9mF8SS1XoNaA3O8,11266
18
18
  firefighter/api/urls.py,sha256=kjL6TwO836laVMn8e4fF4GiULyCXQkZiTDqXiVxrGtY,2103
19
19
  firefighter/api/migrations/0001_initial.py,sha256=8Q3Q_OUjAOsB_NzjYunAefC9WPk1FUwL3PA7N_gMylc,1496
20
20
  firefighter/api/migrations/0002_alter_apitokenproxy_options.py,sha256=BRuXscZS1gmcbF9mFlUyYy_Mwf-5xnUHcuFL2VA89eY,933
@@ -27,7 +27,7 @@ firefighter/api/views/environments.py,sha256=B9j5qFnFHzXw20ZVzJIJJSKjlNXINNeXqJ8
27
27
  firefighter/api/views/groups.py,sha256=mu75XdBNBCMXaVCbiFSLNd0YtpmneNa5u-tgXadAsCE,331
28
28
  firefighter/api/views/incident_cost_types.py,sha256=cNYqk3ez5ud_BA45aS2ySLEIJotHdCu_fLc4YRiULk4,410
29
29
  firefighter/api/views/incident_costs.py,sha256=aZYEGlT8nF09He4hV_90QJToKFRgi-cJdrsKHR_8HUM,409
30
- firefighter/api/views/incidents.py,sha256=yx_ORI-8GUSWMC8aKC0BEc-dRSmudQIxcaD-MjPZGxA,10306
30
+ firefighter/api/views/incidents.py,sha256=UvPUUXWOR8kOYQXWjmDH25nGQLcSRwtXePYiiY8XPHc,10387
31
31
  firefighter/api/views/severities.py,sha256=mdkR4GjZibydC1Dx-Sglm-f35GZxWbjmqStAx_kRv7U,352
32
32
  firefighter/components/__init__.py,sha256=Vd_Uk5Uq7Mqp6NOFp5QiniWZAyzmYLqNSFEEw1x7COk,167
33
33
  firefighter/components/avatar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -55,7 +55,7 @@ firefighter/confluence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
55
55
  firefighter/confluence/admin.py,sha256=aDXghuuLc7G_TLt-655M31smx-H6vkIgLtEmmNCA3lg,1490
56
56
  firefighter/confluence/apps.py,sha256=vKswBwQL7L9e2JQwvRb7xy3myyE_GRldYX78jSY3XCM,406
57
57
  firefighter/confluence/client.py,sha256=xjSsrsGPF75JANNvam2YgiUkztuXhOcs9pMmPbb7ymk,6361
58
- firefighter/confluence/models.py,sha256=-jOb1Mxs-1N-LzZTHHXSOoIoQcCLjFtILc037X_5Rns,6465
58
+ firefighter/confluence/models.py,sha256=ExFip6Tw199jI5LVLqC8t4pOPsvQxfEhtQeWCqTijFw,8769
59
59
  firefighter/confluence/serializers.py,sha256=CzuHVXIJNS47NCAJLXSTDOevtg5sf309XXEcWKQ1sAQ,258
60
60
  firefighter/confluence/service.py,sha256=MU_wHGxgLmfR7t4wLJbfldwivlBfyaeXFKjOfFJzEqk,12307
61
61
  firefighter/confluence/tables.py,sha256=ANEtFXzXyPK6E5FIrBC5XoQt5R3ZUY1DME_RbD1h_NE,732
@@ -70,7 +70,7 @@ firefighter/confluence/management/commands/sync_runbooks.py,sha256=EgapI-TO9NXoH
70
70
  firefighter/confluence/migrations/0001_initial_oss.py,sha256=FdR7hie-0vwx_tcuYUziBZMSiIuQ-yNO5LRhizJ7CQg,3860
71
71
  firefighter/confluence/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  firefighter/confluence/signals/__init__.py,sha256=nmuEEoawaYFVOlK880DT5-VOkXVIKYVLzrJIEnrXku0,121
73
- firefighter/confluence/signals/incident_updated.py,sha256=2VQGNVW7DutQCDhfaBnxDQLhqlI8yQ_QzuzYIvsIUSU,1805
73
+ firefighter/confluence/signals/incident_updated.py,sha256=DNx-nZvss98H-1_4GwrQPxainRNo2nyC6GuaCVdEj8k,1169
74
74
  firefighter/confluence/tasks/__init__.py,sha256=-NrthX2GIVmqjgiGAFg0r72Djc54Jl05k6Zb_UHUlf4,427
75
75
  firefighter/confluence/tasks/archive_postmortems.py,sha256=k2kgRaTC56SRzV9neGUZfvl8BpRZ_pKHoHr_sETykms,8098
76
76
  firefighter/confluence/tasks/sort_runbooks.py,sha256=rac-AjP14sYYulHHBbhuBbh9PK16Y3zBZ-7ywIdF5lo,2958
@@ -115,7 +115,7 @@ firefighter/firefighter/settings/components/caches.py,sha256=C7WULBbKwU3tjtzg6vU
115
115
  firefighter/firefighter/settings/components/celery.py,sha256=AjWlIqAdwaZOh9QK-31Qtw60QjETy4Q-VjadyNf7pCM,1974
116
116
  firefighter/firefighter/settings/components/common.py,sha256=sKflgempBG3wWVP53IG-kx64Kb6qhS4zhMQBd0JvnF4,9827
117
117
  firefighter/firefighter/settings/components/confluence.py,sha256=DPpP9FYyXZM1S3JGDL46UIlu518oSLzLi6mSfgsHKIM,1789
118
- firefighter/firefighter/settings/components/jira_app.py,sha256=yax_NLQsK_Fws-GC6_-QYxJDJQiLAhp56gzRFV-d0TU,843
118
+ firefighter/firefighter/settings/components/jira_app.py,sha256=2Ei9NCp4TSVOKqgn7FNFOoIgccx8ZBLjGShXLY5ddgI,2184
119
119
  firefighter/firefighter/settings/components/logging.py,sha256=PzaDYr2BxspK2tXaTecUAKu7BlVZOLQYizQMAa959KA,3336
120
120
  firefighter/firefighter/settings/components/pagerduty.py,sha256=_LT0_RPhN2E4qW2XQiyywky_iGAQK_eSaabXJyAYMRc,698
121
121
  firefighter/firefighter/settings/components/raid.py,sha256=i16Gtgmbye-GpUqHYX0HwzC2CipQhLjGDeeOuquB7y8,1004
@@ -128,7 +128,7 @@ firefighter/firefighter/templates/admin/base.html,sha256=JZbBsYmdr77PMeMMGHss1lY
128
128
  firefighter/firefighter/templates/admin/login.html,sha256=EY9AvQe8Y9GNQAAMr9g9Wl1hBfbYC-hLIDzY0rR5SAc,536
129
129
  firefighter/firefighter/templates/admin/send_message_conversation.html,sha256=LavOB38P3GFcDzzzdsvUhwMr5jzE4H1wyFTGITucAmE,2058
130
130
  firefighter/incidents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
- firefighter/incidents/admin.py,sha256=ZbGmAkR9xCIBvS2h18WaV21z4pNc516P98Z4JFBF9rk,22018
131
+ firefighter/incidents/admin.py,sha256=-uhKuutkMNFuDEq4bJEQqt1dUHjTFrljrs2cZcJnAxg,22171
132
132
  firefighter/incidents/apps.py,sha256=GySoDZsz-BH-nsIZWTOd0H1hgL9s9il1BEuLC4gKTXs,378
133
133
  firefighter/incidents/enums.py,sha256=FsoxsZCBB_19MmxnxKu-hb4CzOJQjJVPtv1AwKjUAaI,1840
134
134
  firefighter/incidents/factories.py,sha256=EXrJ83H28QrD7J0SpzmO8rq2RgkQ8iRj8hmil4QD0Rs,3723
@@ -180,8 +180,8 @@ firefighter/incidents/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
180
180
  firefighter/incidents/models/__init__.py,sha256=FLVyBwIdyxLdgSvXRAKC3fry9YwwqlqhitTIuG0vWrk,877
181
181
  firefighter/incidents/models/environment.py,sha256=51txwua3dCrWZ1iSG3ZA8rbDn9c00pyMAZujl9gwE5c,827
182
182
  firefighter/incidents/models/group.py,sha256=VrVL315VFUvKW69AZuRUBg1h0jZJvn8zWeMxMOWec1Y,700
183
- firefighter/incidents/models/impact.py,sha256=URoMsQBJQnyfaNRpVl1o4A1IllM7dg94sd6tf5Ssr-k,4868
184
- firefighter/incidents/models/incident.py,sha256=PoeNq7NDn_9sP65YURQ1_DQDBXW8q6wltxy6BblR3Ag,27301
183
+ firefighter/incidents/models/impact.py,sha256=bzTTW79FmclLcAKN0wmk739cXBiYIY_YHpkkASGn5Rw,4776
184
+ firefighter/incidents/models/incident.py,sha256=UdL9bc-3Ou-OS8P-nHO4YF6-Hk0mDkGNq1zjOq-TNFk,28022
185
185
  firefighter/incidents/models/incident_category.py,sha256=g4OHv_XQhWcH6dvkqkyCgjlruo_1eih_CdtAPgPhaW4,7744
186
186
  firefighter/incidents/models/incident_cost.py,sha256=juwOfJKRaNQpOHkRUCHShDDba0FU98YjRPkU4I0ofAU,1346
187
187
  firefighter/incidents/models/incident_cost_type.py,sha256=wm8diry_VySJzIjC9M3Yavv2tYbvJgpN9UDb2gFRuH4,845
@@ -259,7 +259,7 @@ firefighter/incidents/views/date_filter.py,sha256=fUhTjkBulMokI5tAHuqNDVv1dyspjm
259
259
  firefighter/incidents/views/date_utils.py,sha256=tiRTlh7PmRv4eAH0asiSX3Gn7ajsal9egm4S1d7s3_s,5759
260
260
  firefighter/incidents/views/errors.py,sha256=yDuH0YOdGf-voVNEC51yR9Ie3OU-az7g2EqWs_uV1Kk,7855
261
261
  firefighter/incidents/views/reports.py,sha256=1Iegx04w-oHw4cj7u9w2_s7T_e9FH5I6RRPTwDZwZhg,20973
262
- firefighter/incidents/views/views.py,sha256=-z-cfYJ8lIwORAkAxXRMCAg34RmwcPO3l0-VuDpEGyA,10543
262
+ firefighter/incidents/views/views.py,sha256=TnmPeVYXNDU6MSUHqqnbTLYUBpzFsng_fy6qVcxEwVU,10655
263
263
  firefighter/incidents/views/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
264
264
  firefighter/incidents/views/components/details.py,sha256=gFEezmL1TcVYnM_ryLNNMaynuIdjYV31Qzx_GfzrQiA,1040
265
265
  firefighter/incidents/views/components/list.py,sha256=u8HfXetmdL59h_4AZIhiHmKcmrPRZXgekPfnucB4Rek,2207
@@ -269,16 +269,28 @@ firefighter/incidents/views/docs/role_types.py,sha256=88dGBAL7NMC24lyqLDN0jGnh5-
269
269
  firefighter/incidents/views/users/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
270
270
  firefighter/incidents/views/users/details.py,sha256=xqDUEMorplAdSJNQ2vcNzntKKn6UsMDb_dXjVBN2GeY,1853
271
271
  firefighter/jira_app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
272
- firefighter/jira_app/admin.py,sha256=YywHfi1dlpyyW8XPYvjtOW3fbTZGm5bKXvHbDkr0D_8,702
273
- firefighter/jira_app/apps.py,sha256=W6iM_e2X-dQ4YSeDPuFPM4wp-zIgpWsEiehtng0fk48,348
274
- firefighter/jira_app/client.py,sha256=vT0Oj8W1QJabUCA_Gkgg2ZarxgAjBL_krdP2EkaJN84,16535
275
- firefighter/jira_app/models.py,sha256=m-ay74zeI9zMyKuTBfN7W8DPDc9zPmiDESC8mnGBjR4,1741
272
+ firefighter/jira_app/admin.py,sha256=ZHAAbhy0hm_DcklK59KMmid_ZiPn8n5V6g7cZCSNrpc,1299
273
+ firefighter/jira_app/apps.py,sha256=T6vHrQuMZHJoTth-xjy3CbNfPv6DyXgcR3PSMju2JS4,504
274
+ firefighter/jira_app/client.py,sha256=qpMqNTjJUq5OqAxmwvVOE20uJe7kp737HSdsiqUu1G4,21982
275
+ firefighter/jira_app/models.py,sha256=2zKy5VaKkhiHYA8Dukz8g0NTG82Qy5UHAHY9eMv67NE,3097
276
+ firefighter/jira_app/service_postmortem.py,sha256=b76f1uv-zUo4jUWXTul4nv8UnaqQXHzuoTYEDrEpcMA,10620
276
277
  firefighter/jira_app/types.py,sha256=Ukak1U1EhcH2jQPN-UoEL6AMZ-kzPsQ8c7FUr7GmahE,956
277
278
  firefighter/jira_app/utils.py,sha256=3xuzr8viZCBm6j2J9oFzA4bUvVW8TN1DOdlpbruJ_TE,3443
279
+ firefighter/jira_app/management/__init__.py,sha256=wy4qMZb7_K-INwwGGEhMtEeI0XTLqgUw4P8_-VEnrEw,40
280
+ firefighter/jira_app/management/commands/__init__.py,sha256=wy4qMZb7_K-INwwGGEhMtEeI0XTLqgUw4P8_-VEnrEw,40
278
281
  firefighter/jira_app/migrations/0001_initial_oss.py,sha256=AR5UKfKi4vUGSmckeSxQ2qMnsjdhZZqr5Q-RoXw1WKM,2992
282
+ firefighter/jira_app/migrations/0002_add_jira_postmortem_model.py,sha256=oFSbYNcLIdHAoZ99SPRgL8n4ceoAd2YpBGxAoEtcBYQ,2442
279
283
  firefighter/jira_app/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
284
+ firefighter/jira_app/signals/__init__.py,sha256=OpXFlbRgIrh73DGlUzQ6WUTedKsD5wYW9bxGMq_DnIs,325
285
+ firefighter/jira_app/signals/incident_key_events_updated.py,sha256=uaV3MON1QzeOZizzAwSdyktBwe2mWxHJeNSvy9MYc3k,3204
286
+ firefighter/jira_app/signals/postmortem_created.py,sha256=eOTQKcjspL-JHvXiCu-U1FzrqIr6OJ20GOK6gJ4dojw,5904
280
287
  firefighter/jira_app/tasks/__init__.py,sha256=XLCPkolM6LwIUGv0MNbk_0lCuBHyzgRFHsE3vTRD5ds,86
281
288
  firefighter/jira_app/tasks/sync_users_jira.py,sha256=sSSLsVCdzkPNRS6Gt8j0YwCTuoRqkJAJLxDBu7IElmM,1437
289
+ firefighter/jira_app/templates/jira/postmortem/impact.txt,sha256=eYlX0rytUiKZZIrWxMN23QXkLZ8JwlwPe5S2oiqZyFA,259
290
+ firefighter/jira_app/templates/jira/postmortem/incident_summary.txt,sha256=Bnias41O8TR2v0CAWpOoyRVVBAyx6vk2iICeQsLOuCs,392
291
+ firefighter/jira_app/templates/jira/postmortem/mitigation_actions.txt,sha256=7DWOcMhU0NiJugiUmEvSg1Z3ajW7IDZejprxt8urue0,242
292
+ firefighter/jira_app/templates/jira/postmortem/root_causes.txt,sha256=27DgQdrtHvHcju1llyYqU1jufegeHjJN_qvVkyECINM,291
293
+ firefighter/jira_app/templates/jira/postmortem/timeline.txt,sha256=NpUkBNknF4AUj2w20YqTjQ-o_EmGOYcoTZIhc8iWe3k,530
282
294
  firefighter/logging/__init__.py,sha256=SSTapLdwQsMkBy8FDFOjLkzJlutfT152mmPEGErirK4,60
283
295
  firefighter/logging/custom_json_formatter.py,sha256=dckOtllNTfeDLc1J4FWJ-_PVeT_CqfQIyTs9FKfZsqE,5601
284
296
  firefighter/logging/pretty_formatter.py,sha256=6FxNVpQczcVhmLbLCz6HWxOQOvbqgH12-y8t1d7drfs,2411
@@ -326,7 +338,7 @@ firefighter/raid/migrations/0002_featureteam_remove_qualifierrotation_jira_user_
326
338
  firefighter/raid/migrations/0003_delete_raidarea.py,sha256=M_XkKCu73ib2H09co2L-ssLQakJJFNOfqJpKrzOYP2Y,332
327
339
  firefighter/raid/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
340
  firefighter/raid/signals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
329
- firefighter/raid/signals/incident_updated.py,sha256=5LYeqvgTrfINu_SOwkZa3hD6rvTsl8BL8Py-immqK3I,1374
341
+ firefighter/raid/signals/incident_updated.py,sha256=Al1S1BOWAd6s97ITG0Yg181hOOLR5Byx8OO9hM5kP9E,2231
330
342
  firefighter/raid/tasks/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
331
343
  firefighter/raid/views/__init__.py,sha256=C3WhAJfEoUasi2afHPuLpKiuRYixK-tc3j0-2Rw_g3E,5210
332
344
  firefighter/slack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -347,7 +359,7 @@ firefighter/slack/management/commands/generate_manifest.py,sha256=zFWHAC7ioozcDd
347
359
  firefighter/slack/management/commands/switch_test_users.py,sha256=2KTSvCBxsEvZa61J8p0r3huPNhwuytcj2J7IawwZWpQ,11064
348
360
  firefighter/slack/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
349
361
  firefighter/slack/messages/base.py,sha256=biH-YEAaldJ-OLHEs5ZjW-gtUYUbjOqxrAEflqV2XS0,4593
350
- firefighter/slack/messages/slack_messages.py,sha256=AI6t5dnJKZKa1ZCQ1l_zmf_SBjT-aTlykBS9Wz8x7NA,34092
362
+ firefighter/slack/messages/slack_messages.py,sha256=xvkV_sEbBRkyqGigR_oQeC6Cr9KLWqqwxDXmPywyEok,35553
351
363
  firefighter/slack/migrations/0001_initial_oss.py,sha256=XmTPgq7zCME2xDwzRFoVi4OegSIG9eSKoyTNoW05Qtg,12933
352
364
  firefighter/slack/migrations/0002_usergroup_tag.py,sha256=098tmGA81mT-R2uhb6uQfZ7gKiRG9bFhEwQ8rrp4SKM,583
353
365
  firefighter/slack/migrations/0003_alter_usergroup_tag.py,sha256=ncH3KUWEPZHlbdcAtOJ0KGt5H6EX-cKspTGU3osrAhE,591
@@ -370,7 +382,7 @@ firefighter/slack/signals/get_users.py,sha256=-ZUI3ORJZJ4tLiGhVwIU-pWHoriALtOC7K
370
382
  firefighter/slack/signals/handle_incident_channel_done.py,sha256=pYoS_ZJf2u9seHCqahfPHz4BrfvF1vIZjE1_yy0P5mY,1726
371
383
  firefighter/slack/signals/incident_closed.py,sha256=6QyUmTjEGVg0Wn5W-i2F8DlJuhvHjg3MxkcvAXFn9Bk,687
372
384
  firefighter/slack/signals/incident_updated.py,sha256=MBouWXbLByEynGoytdFeRyioDxi5VFO8dlgI0kygX1E,10137
373
- firefighter/slack/signals/postmortem_created.py,sha256=XY8_lEmBEG3VSsankpbOWdEB9dom9Ke6u8ss4-aDX6Y,1152
385
+ firefighter/slack/signals/postmortem_created.py,sha256=cB3t4eZ96m0-2ybrJnrNHDzOXcsRyCKKwijx4g0ZJCA,2991
374
386
  firefighter/slack/signals/roles_reminders.py,sha256=1Ef_J1PubY8WTVs2WDXz4KWa6X2Lvp1RFObIjSGpiOM,4041
375
387
  firefighter/slack/tasks/__init__.py,sha256=28QxZkakyi9l7Ae83fQuzOS-9EaBiwuh_peUZwIxCx4,179
376
388
  firefighter/slack/tasks/fetch_conversations_members.py,sha256=lLQ491_l8HEJrjoDpD0AETqoFUFogkyMJ002hguA-Dg,5381
@@ -399,19 +411,19 @@ firefighter/slack/views/events/message_deleted.py,sha256=tyA1-sAlG9ImcKIhqSn6Egu
399
411
  firefighter/slack/views/events/reaction_added.py,sha256=AipwBnrU5B35D97YIZCXdSW8W7-9QTIIQqUcrLTLQ5c,4241
400
412
  firefighter/slack/views/modals/__init__.py,sha256=U9PapAIlpuYqBonOUmBGWT8_HjQa35ilMQJXGaFLgd0,1945
401
413
  firefighter/slack/views/modals/close.py,sha256=4j5iA-lmIFuCz7B9pgDmjxrqmfWFysqWEn1YIsE75zc,12161
402
- firefighter/slack/views/modals/closure_reason.py,sha256=eEBK1_sfY9TAKFsajS-As0kZgDPwuU_8DE0z2OANbvc,6943
414
+ firefighter/slack/views/modals/closure_reason.py,sha256=3ot1cb4Wzvwe-TAuR16ZopnWGInUmoeDUaKzOQ838kw,7600
403
415
  firefighter/slack/views/modals/downgrade_workflow.py,sha256=cRWsm3DmKRRI1-Jpjprb5xeY2U7HvRo6eZlUbGuzr1A,3192
404
416
  firefighter/slack/views/modals/edit.py,sha256=1N0OBSxsDuN6lJoH-djbEljy7f0LcDEpJF-U5YoEFXA,5895
405
- firefighter/slack/views/modals/key_event_message.py,sha256=ga3-ITZyzJExwzctX-GfgnDqyQaxTfcqpqnOwY2E38M,5620
417
+ firefighter/slack/views/modals/key_event_message.py,sha256=C6yhQLQ6jBuhIr-YAoAyt-qZKu0V6nJMGZ_t3DLtUbo,5943
406
418
  firefighter/slack/views/modals/open.py,sha256=YIxpo8_C4cWCy_pQ3YRWl7NMyLmjqNjggTQINTBW6mo,29189
407
- firefighter/slack/views/modals/postmortem.py,sha256=YlBZ1ziONmbA818hPo0H54deWogiH3Xy1jArMiazx-4,2851
419
+ firefighter/slack/views/modals/postmortem.py,sha256=7h_at4oMVFxN3IISbk1yzBDC46g4CFDsRnc873d8K-E,2451
408
420
  firefighter/slack/views/modals/select.py,sha256=Y-Ji_ALnzhYkXDBAyi497UL1Xn2vCGqXCtj8eog75Jk,3312
409
421
  firefighter/slack/views/modals/send_sos.py,sha256=bP6HgYyDwPrIcTq7n_sQz6UQsxhYbvBDS4HjM0uRccA,4838
410
422
  firefighter/slack/views/modals/status.py,sha256=C8-eJRtquSeaHe568SC7yCFef1k14m2_6lUqBezdSH8,3970
411
423
  firefighter/slack/views/modals/trigger_oncall.py,sha256=h_LAD5X5rjMFWiDYTEp5VB9OaF7sTvKZhNaW3KQkw5M,5065
412
424
  firefighter/slack/views/modals/update.py,sha256=OF9sf-Z6IiviNmjN28MQNYiUbJ5tha0MdHUQyPpVFiY,2150
413
425
  firefighter/slack/views/modals/update_roles.py,sha256=De3Gv67MZQHyNdonX3S99F5MtKF_Rj3y71gdWibxBaM,2419
414
- firefighter/slack/views/modals/update_status.py,sha256=QZvJj5FzNNgsjJZi9I_GfAD6JpZYy821Z0V0SCSODcI,5708
426
+ firefighter/slack/views/modals/update_status.py,sha256=kJYcyruEwo7hok-z4Ge8HPxLMc-XPm-OrB7X_faNd7U,6000
415
427
  firefighter/slack/views/modals/utils.py,sha256=zKLJD2KhTGcX2d9WCYwshYRa6ok_9-ED1_pgOLp028s,2133
416
428
  firefighter/slack/views/modals/base_modal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
417
429
  firefighter/slack/views/modals/base_modal/base.py,sha256=7mvOxZTtegSmitSMnDvu8BK0qLUXoudUsda6CaLjdkY,12479
@@ -440,6 +452,7 @@ firefighter_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
440
452
  firefighter_tests/conftest.py,sha256=fyVZNk4PX0ATjIjmq0BYjC9k3og26kkpC73npEgFlv8,3228
441
453
  firefighter_tests/test_api/test_api_landbot.py,sha256=a5Fk-8GCLCdwqJOovFzw75uDq6pn6sKIcTRHpbMzx_M,4602
442
454
  firefighter_tests/test_api/test_api_urls.py,sha256=Nn7Flq7dWGYJjgZK5HGdGECFMwKiDdfDYI0g09gXK-c,1936
455
+ firefighter_tests/test_api/test_renderer.py,sha256=ycDknfY31heSmJ1naTd4qxeSHmds9fzt5nxMHLnR6uU,1142
443
456
  firefighter_tests/test_confluence/test_confluence_utils.py,sha256=hD5M4mYwrHlil9KPARNQ5OINdRc5njfYCIdq8a6jUB8,1847
444
457
  firefighter_tests/test_firefighter/test_firefighter_utils.py,sha256=og79bkErCBQB-xkw88J-ronT0dDEChuqlMke2ElxATo,3590
445
458
  firefighter_tests/test_firefighter/test_logging.py,sha256=4HUH73vLDwmOCpMiXwDasMLL7F0kA1B_DxE48mqswSk,878
@@ -458,14 +471,19 @@ firefighter_tests/test_incidents/test_forms/test_update_key_events.py,sha256=rHR
458
471
  firefighter_tests/test_incidents/test_forms/test_update_status_workflow.py,sha256=q0xXU2BbBG8B0uvvyBWlo4HM8ckbcNAP05Fq8oJNtOw,16270
459
472
  firefighter_tests/test_incidents/test_forms/test_workflow_transitions.py,sha256=priKh7QYZxGDPu2SvPC8pGnqOsZWg5cLkyC40pDvLAU,7184
460
473
  firefighter_tests/test_incidents/test_models/test_incident_category.py,sha256=aRoBOhb8fNjLF9CMPZ1FXM8AT51Cd80XPsY2Y3wHY_M,5701
461
- firefighter_tests/test_incidents/test_models/test_incident_model.py,sha256=c0A73G50SuzExHaEWJyByjmJmISChCClQYd2AydnYxY,3649
474
+ firefighter_tests/test_incidents/test_models/test_incident_model.py,sha256=P5tmU5X4grt-yvoqjfuBkbd5tAWX3TptN-b_R3-1a9A,4719
462
475
  firefighter_tests/test_incidents/test_models/test_migrations/test_incident_migrations.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
463
476
  firefighter_tests/test_incidents/test_utils/test_date_utils.py,sha256=ogP7qOEwItL4YGI5gbQPVssOS9ilwiuZC8OrT2qngBY,6568
464
477
  firefighter_tests/test_incidents/test_views/test_incident_detail_view.py,sha256=gKKFWIZVrD_P4p6DJjeHCW5uGXBUBVlCd95gJJYDpWQ,680
465
478
  firefighter_tests/test_incidents/test_views/test_index_view.py,sha256=InpxbaWOFwRn4YWeIKZhj17vMymrQQf2p2LFhe2Bcdw,816
466
479
  firefighter_tests/test_jira_app/__init__.py,sha256=JxZ3v-0kiHOoO-N3kR8NHTmD8tEvuEYKW1GX_S1ZLMY,33
467
480
  firefighter_tests/test_jira_app/conftest.py,sha256=HmZd7EBZgng-rb3kIaB14TPVMixMG4YEvnShVqgjodE,545
481
+ firefighter_tests/test_jira_app/test_incident_key_events_sync.py,sha256=ytXHzlwckhWV7eufaEqwLqA-FTWTUF1o0bFSyZ0ippQ,3891
468
482
  firefighter_tests/test_jira_app/test_jira_client_watchers.py,sha256=IBrFjwhOP0rfm58BBq339CySxjdJkPYjGmISC4oQhZc,4803
483
+ firefighter_tests/test_jira_app/test_models.py,sha256=QfMLrJVXSWqu29XEbJO6xXpOkugcdWti5_bmCEX2m3Y,4626
484
+ firefighter_tests/test_jira_app/test_postmortem_issue_link.py,sha256=CF0UDmHOY08Oj3MYtDKNtkVDq_nbQ0nesvFWF8Rv944,7750
485
+ firefighter_tests/test_jira_app/test_postmortem_service.py,sha256=DAlQxlPlB_Fd038hLeSu0PCO-Izo0z1dBgLXv4twvTE,15114
486
+ firefighter_tests/test_jira_app/test_timeline_template.py,sha256=_PtFnIib2HfjyylNRQXcjvdhrsoAJICOKauIDsYFQRk,4902
469
487
  firefighter_tests/test_raid/conftest.py,sha256=i_TOquYIMLDyVQ97uqxTqPJszVz4qq7L_Q7YJxTuS1o,4090
470
488
  firefighter_tests/test_raid/test_raid_alert_p4_p5.py,sha256=rz9orbt1E1vJ5POQyVZ6-SEPvqB55-xhwIWHicdfgDg,9356
471
489
  firefighter_tests/test_raid/test_raid_client.py,sha256=KTqELERpWno7XhF9LpabpxkHoJiWWrryUg5LHi5Yfjo,22456
@@ -474,7 +492,7 @@ firefighter_tests/test_raid/test_raid_forms.py,sha256=8hiXftYPO_lY0heKHqoreUW2s8
474
492
  firefighter_tests/test_raid/test_raid_models.py,sha256=nq-fVClB_P24W8WrZruOPt8wlHUVGYI7wxJR7tH6AnM,5042
475
493
  firefighter_tests/test_raid/test_raid_serializers.py,sha256=xkMK9npvDMAr2pUpP4mFbLszrR6qyTzsJRd-uNOzEhM,22560
476
494
  firefighter_tests/test_raid/test_raid_service.py,sha256=AqVyrRjW2tr0sfbXS4lGlJ7mcxB2ACEXAR8Bv0pXnj0,16755
477
- firefighter_tests/test_raid/test_raid_signals.py,sha256=twNxB3NQs58s8ZcP-wsZXG7iTHR4yKFC9x-zpq1ZuAo,7002
495
+ firefighter_tests/test_raid/test_raid_signals.py,sha256=QkGd3alp5O6t5kz66ukpNY-uZdIKJsqxvEPc8exORP4,8974
478
496
  firefighter_tests/test_raid/test_raid_transitions.py,sha256=mtmMKwukxmZSM-R619BQ3Z_2AB-qY6imvDgUF0A3_tw,4784
479
497
  firefighter_tests/test_raid/test_raid_utils.py,sha256=i6JBwim1G-qynwxprNZekxl9K7Vis4FFvNkw3wT2jTM,1016
480
498
  firefighter_tests/test_raid/test_raid_views.py,sha256=paAhh4k2EDlmG1ehwNhMuYIhr1okqrvM7xlkaTAo2V0,6825
@@ -484,23 +502,24 @@ firefighter_tests/test_slack/test_conversation_tags.py,sha256=nNqTZRRBfF6Z4wpFSY
484
502
  firefighter_tests/test_slack/test_signals_downgrade.py,sha256=mgl4H5vwr2kImf6g4IZbhv7YEPmMzbYSaVr8E6taL88,5420
485
503
  firefighter_tests/test_slack/test_slack_utils.py,sha256=9PLobMNXh3xDyFuwzcQFpKJhe4j__sIgf_WRHIpANJw,3957
486
504
  firefighter_tests/test_slack/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
487
- firefighter_tests/test_slack/messages/test_slack_messages.py,sha256=60ATcQKWVuLvBbyCEiSyzvR4cnDTgEEhDclg8jrE4Hg,14391
505
+ firefighter_tests/test_slack/messages/test_slack_messages.py,sha256=rs7zeBa_xsAyZIfXbOI1RA-zgViEPT5ZFNw6paBJvxc,17299
488
506
  firefighter_tests/test_slack/test_models/test_conversations.py,sha256=t3ttmgwiu7c-N55iU3XZPmrkEhvkTzJoXszJncy4Bts,793
489
507
  firefighter_tests/test_slack/test_models/test_incident_channel.py,sha256=qWoGe9iadmK6-R8usWvjH87AHRkvhG_dHQeC3kHeJrs,17487
490
508
  firefighter_tests/test_slack/test_models/test_slack_user.py,sha256=uzur-Rf03I5dpUTO4ZI6O1arBUrAorg1Zvgshf8M-J4,7000
491
509
  firefighter_tests/test_slack/views/modals/conftest.py,sha256=TKJVQgqWaFs3Gg1T526pti9XpZBtQs47WBH6L_qSDeo,4532
492
510
  firefighter_tests/test_slack/views/modals/test_close.py,sha256=wuqMkpyUWKvC_gaTnJvRmdm8-wsmbLpCU_ith4ihsLY,45447
493
- firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py,sha256=R-a8NJZmbNKZeVbAS3DFe_U7sw-GM4p_fKRkqBP6yPM,5099
511
+ firefighter_tests/test_slack/views/modals/test_closure_reason_modal.py,sha256=hKfBv8fHfObmZc7OL6NUnxFueA1bowkNbu_-MF1anYI,5615
494
512
  firefighter_tests/test_slack/views/modals/test_edit.py,sha256=ykirry-S3i6PtoSs3rff_k6jqmvv1oMWC_iR8e5Jsg0,12022
495
513
  firefighter_tests/test_slack/views/modals/test_form_utils_multiple_choice.py,sha256=Svab_ZyYTMf0T-uJEQcm7gS1WzxtC4gPh1W--Z2v_Y8,8415
514
+ firefighter_tests/test_slack/views/modals/test_key_event_message.py,sha256=BCg-c27ZLJqNgFuG4JDgXrSTp8_sT4FeBtpASzSq8NI,1107
496
515
  firefighter_tests/test_slack/views/modals/test_open.py,sha256=IzgG9le5NN_CvltehAIqkj94ioTKCqdA6yoRp2NlNsE,10700
497
516
  firefighter_tests/test_slack/views/modals/test_opening_unified.py,sha256=OejtLyc_mehav2TDaLzUnhilMNvhCzc6T4FodCqfQPk,17406
498
517
  firefighter_tests/test_slack/views/modals/test_send_sos.py,sha256=_rE6jD-gOzcGyhlY0R9GzlGtPx65oOOguJYdENgxtLc,1289
499
518
  firefighter_tests/test_slack/views/modals/test_status.py,sha256=oQzPfwdg2tkbo9nfkO1GfS3WydxqSC6vy1AZjZDKT30,2226
500
- firefighter_tests/test_slack/views/modals/test_update_status.py,sha256=5_YW6uZRksVLMwjrrCkXd196jsa7CwJaoWTYf2YYZ_k,55683
519
+ firefighter_tests/test_slack/views/modals/test_update_status.py,sha256=3ARHZPs22FTx7IjgOldzEpVxxWeHqEbe4kQphUuSp34,55928
501
520
  firefighter_tests/test_slack/views/modals/test_utils.py,sha256=DJd2n9q6fFu8UuCRdiq9U_Cn19MdnC5c-ydLLrk6rkc,5218
502
- firefighter_incident-0.0.22.dist-info/METADATA,sha256=cpiif-R-KyeR8ieP1NoKGAtaHzAspcmt4lmpWnGCK9M,5570
503
- firefighter_incident-0.0.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
504
- firefighter_incident-0.0.22.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
505
- firefighter_incident-0.0.22.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
506
- firefighter_incident-0.0.22.dist-info/RECORD,,
521
+ firefighter_incident-0.0.23.dist-info/METADATA,sha256=5pD4MN6Pms0cgm7f16chi42i52sjKke_UItPGRcP9ds,5570
522
+ firefighter_incident-0.0.23.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
523
+ firefighter_incident-0.0.23.dist-info/entry_points.txt,sha256=c13meJbv7YNmYz7MipMOQwzQ5IeFOPXUBYAJ44XMQsM,61
524
+ firefighter_incident-0.0.23.dist-info/licenses/LICENSE,sha256=krRiGp-a9-1nH1bWpBEdxyTKLhjLmn6DMVVoIb0zF90,1087
525
+ firefighter_incident-0.0.23.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from firefighter.api.renderer import CSVRenderer
4
+
5
+
6
+ def test_tablize_hides_headers_when_labels_hidden() -> None:
7
+ renderer = CSVRenderer()
8
+ rows = list(renderer.tablize([{"id": 1, "value": "foo"}], labels="__hidden__"))
9
+
10
+ assert rows == [[1, "foo"]]
11
+
12
+
13
+ def test_tablize_applies_custom_labels() -> None:
14
+ renderer = CSVRenderer()
15
+ rows = list(
16
+ renderer.tablize(
17
+ [{"a": 1, "b": 2}],
18
+ header=["b", "a"],
19
+ labels={"a": "Column A", "b": "Column B"},
20
+ )
21
+ )
22
+
23
+ assert rows[0] == ["Column B", "Column A"]
24
+ assert rows[1] == [2, 1]
25
+
26
+
27
+ def test_get_headers_preserves_explicit_order_without_wildcards() -> None:
28
+ renderer = CSVRenderer()
29
+ _, header = renderer._get_headers([{"x": 1, "y": 2}], ["y", "x"])
30
+
31
+ assert header == ["y", "x"]
32
+
33
+
34
+ def test_get_headers_expands_wildcards_sorted() -> None:
35
+ renderer = CSVRenderer()
36
+ _, header = renderer._get_headers(
37
+ [{"fixed": 1, "meta.foo.value": 10, "meta.bar.value": 20}],
38
+ ["fixed", "meta.*.value"],
39
+ )
40
+
41
+ assert header == ["fixed", "meta.bar.value", "meta.foo.value"]
@@ -96,3 +96,32 @@ class TestIncidentSetStatus:
96
96
 
97
97
  assert recovered_event is not None
98
98
  assert recovered_event.event_type == "recovered"
99
+
100
+
101
+ @pytest.mark.django_db
102
+ class TestIncidentNeedsPostmortem:
103
+ """Test the needs_postmortem property logic.
104
+
105
+ NOTE: The needs_postmortem property checks if:
106
+ 1. Priority requires postmortem (P1/P2)
107
+ 2. Environment is PRD
108
+ 3. At least one post-mortem system is enabled (Confluence OR Jira)
109
+ """
110
+
111
+ def test_p3_does_not_need_postmortem(self) -> None:
112
+ """Test that P3 incident does not require postmortem even in PRD."""
113
+ incident = IncidentFactory.create(
114
+ priority__value=3, # P3
115
+ priority__needs_postmortem=False,
116
+ environment__value="PRD",
117
+ )
118
+ assert incident.needs_postmortem is False
119
+
120
+ def test_p1_non_prd_does_not_need_postmortem(self) -> None:
121
+ """Test that P1 incident in non-PRD environment does not require postmortem."""
122
+ incident = IncidentFactory.create(
123
+ priority__value=1, # P1
124
+ priority__needs_postmortem=True,
125
+ environment__value="STG", # Use STG instead of DEV
126
+ )
127
+ assert incident.needs_postmortem is False
@@ -0,0 +1,112 @@
1
+ """Tests for syncing incident key events to Jira post-mortem timeline."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+ from unittest.mock import MagicMock, patch
7
+
8
+ import pytest
9
+ from django.test import override_settings
10
+ from django.utils import timezone
11
+
12
+ from firefighter.incidents.enums import IncidentStatus
13
+ from firefighter.incidents.factories import IncidentFactory, UserFactory
14
+ from firefighter.incidents.models.incident_update import IncidentUpdate
15
+ from firefighter.incidents.signals import incident_key_events_updated
16
+ from firefighter.jira_app.models import JiraPostMortem
17
+
18
+ if TYPE_CHECKING:
19
+ from firefighter.incidents.models.incident import Incident
20
+ from firefighter.incidents.models.user import User
21
+
22
+
23
+ @pytest.mark.django_db
24
+ class TestIncidentKeyEventsSync:
25
+ """Test syncing incident key events to Jira post-mortem timeline."""
26
+
27
+ @staticmethod
28
+ @override_settings(ENABLE_JIRA_POSTMORTEM=True)
29
+ @patch("firefighter.jira_app.signals.incident_key_events_updated.JiraClient")
30
+ def test_key_events_update_syncs_to_jira_postmortem(
31
+ mock_jira_client: MagicMock,
32
+ ) -> None:
33
+ """Test that updating key events triggers Jira post-mortem timeline update."""
34
+ # Mock Jira client and issue
35
+ mock_client_instance = MagicMock()
36
+ mock_issue = MagicMock()
37
+ mock_client_instance.jira.issue.return_value = mock_issue
38
+ mock_jira_client.return_value = mock_client_instance
39
+
40
+ # Create a user
41
+ user: User = UserFactory.create()
42
+
43
+ # Create an incident
44
+ incident: Incident = IncidentFactory.create(
45
+ _status=IncidentStatus.POST_MORTEM,
46
+ created_by=user,
47
+ )
48
+
49
+ # Create a Jira post-mortem for the incident
50
+ JiraPostMortem.objects.create(
51
+ incident=incident,
52
+ jira_issue_key="TEST-123",
53
+ jira_issue_id="12345",
54
+ created_by=user,
55
+ )
56
+
57
+ # Create a key event (incident update)
58
+ now = timezone.now()
59
+ IncidentUpdate.objects.create(
60
+ incident=incident,
61
+ event_type="detected",
62
+ event_ts=now,
63
+ created_by=user,
64
+ message="Issue was detected",
65
+ )
66
+
67
+ # Trigger the signal (simulating key events update)
68
+ incident_key_events_updated.send(
69
+ sender="test",
70
+ incident=incident,
71
+ )
72
+
73
+ # Verify Jira issue was fetched
74
+ mock_client_instance.jira.issue.assert_called_once_with("TEST-123")
75
+
76
+ # Verify issue.update was called
77
+ mock_issue.update.assert_called_once()
78
+ call_kwargs = mock_issue.update.call_args.kwargs
79
+
80
+ # Verify timeline field was updated
81
+ assert "fields" in call_kwargs
82
+ # Timeline field ID should be present (exact ID depends on config)
83
+ assert len(call_kwargs["fields"]) > 0
84
+
85
+ # Verify timeline content was generated (contains basic incident info)
86
+ timeline_content = next(iter(call_kwargs["fields"].values()))
87
+ assert "Timeline" in timeline_content
88
+ assert "Incident created" in timeline_content
89
+
90
+ @staticmethod
91
+ @patch("firefighter.jira_app.signals.incident_key_events_updated.JiraClient")
92
+ def test_key_events_update_no_postmortem_skips_sync(
93
+ mock_jira_client: MagicMock,
94
+ ) -> None:
95
+ """Test that updating key events for incident without post-mortem skips sync."""
96
+ # Create a user
97
+ user: User = UserFactory.create()
98
+
99
+ # Create an incident WITHOUT post-mortem
100
+ incident: Incident = IncidentFactory.create(
101
+ _status=IncidentStatus.OPEN,
102
+ created_by=user,
103
+ )
104
+
105
+ # Trigger the signal
106
+ incident_key_events_updated.send(
107
+ sender="test",
108
+ incident=incident,
109
+ )
110
+
111
+ # Verify Jira client was NOT called
112
+ mock_jira_client.assert_not_called()
@@ -0,0 +1,138 @@
1
+ """Tests for jira_app models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+ from django.conf import settings
7
+ from django.db.utils import IntegrityError
8
+
9
+ from firefighter.incidents.factories import IncidentFactory, UserFactory
10
+ from firefighter.jira_app.models import JiraPostMortem
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestJiraPostMortem:
15
+ """Test JiraPostMortem model."""
16
+
17
+ def test_create_jira_postmortem(self):
18
+ """Test creating a JiraPostMortem instance."""
19
+ incident = IncidentFactory()
20
+ user = UserFactory()
21
+
22
+ jira_pm = JiraPostMortem.objects.create(
23
+ incident=incident,
24
+ jira_issue_key="INCIDENT-123",
25
+ jira_issue_id="10001",
26
+ created_by=user,
27
+ )
28
+
29
+ assert jira_pm.incident == incident
30
+ assert jira_pm.jira_issue_key == "INCIDENT-123"
31
+ assert jira_pm.jira_issue_id == "10001"
32
+ assert jira_pm.created_by == user
33
+ assert jira_pm.created_at is not None
34
+ assert jira_pm.updated_at is not None
35
+
36
+ def test_jira_postmortem_one_to_one_relationship(self):
37
+ """Test OneToOne relationship with Incident."""
38
+ incident = IncidentFactory()
39
+ user = UserFactory()
40
+
41
+ # Create first post-mortem
42
+ jira_pm1 = JiraPostMortem.objects.create(
43
+ incident=incident,
44
+ jira_issue_key="INCIDENT-123",
45
+ jira_issue_id="10001",
46
+ created_by=user,
47
+ )
48
+
49
+ # Access from incident side
50
+ assert hasattr(incident, "jira_postmortem_for")
51
+ assert incident.jira_postmortem_for == jira_pm1
52
+
53
+ # Try to create second post-mortem for same incident should fail
54
+ with pytest.raises(IntegrityError):
55
+ JiraPostMortem.objects.create(
56
+ incident=incident,
57
+ jira_issue_key="INCIDENT-456",
58
+ jira_issue_id="10002",
59
+ created_by=user,
60
+ )
61
+
62
+ def test_jira_postmortem_issue_url_property(self):
63
+ """Test issue_url property generates correct Jira URL."""
64
+ incident = IncidentFactory()
65
+ user = UserFactory()
66
+
67
+ jira_pm = JiraPostMortem.objects.create(
68
+ incident=incident,
69
+ jira_issue_key="INCIDENT-123",
70
+ jira_issue_id="10001",
71
+ created_by=user,
72
+ )
73
+
74
+ expected_url = f"{settings.RAID_JIRA_API_URL}/browse/INCIDENT-123"
75
+ assert jira_pm.issue_url == expected_url
76
+
77
+ @pytest.mark.django_db(transaction=True)
78
+ def test_jira_postmortem_unique_constraints(self):
79
+ """Test uniqueness constraints on jira_issue_key and jira_issue_id."""
80
+ incident1 = IncidentFactory()
81
+ incident2 = IncidentFactory()
82
+ incident3 = IncidentFactory()
83
+ user = UserFactory()
84
+
85
+ # Create first post-mortem
86
+ JiraPostMortem.objects.create(
87
+ incident=incident1,
88
+ jira_issue_key="INCIDENT-123",
89
+ jira_issue_id="10001",
90
+ created_by=user,
91
+ )
92
+
93
+ # Try to create second post-mortem with same jira_issue_key
94
+ with pytest.raises(IntegrityError):
95
+ JiraPostMortem.objects.create(
96
+ incident=incident2,
97
+ jira_issue_key="INCIDENT-123", # Duplicate key
98
+ jira_issue_id="10002",
99
+ created_by=user,
100
+ )
101
+
102
+ # Try to create second post-mortem with same jira_issue_id
103
+ with pytest.raises(IntegrityError):
104
+ JiraPostMortem.objects.create(
105
+ incident=incident3,
106
+ jira_issue_key="INCIDENT-456",
107
+ jira_issue_id="10001", # Duplicate ID
108
+ created_by=user,
109
+ )
110
+
111
+ def test_jira_postmortem_str_representation(self):
112
+ """Test string representation of JiraPostMortem."""
113
+ incident = IncidentFactory()
114
+ user = UserFactory()
115
+
116
+ jira_pm = JiraPostMortem.objects.create(
117
+ incident=incident,
118
+ jira_issue_key="INCIDENT-123",
119
+ jira_issue_id="10001",
120
+ created_by=user,
121
+ )
122
+
123
+ expected_str = f"Jira Post-mortem INCIDENT-123 for incident #{incident.id}"
124
+ assert str(jira_pm) == expected_str
125
+
126
+ def test_jira_postmortem_created_by_optional(self):
127
+ """Test that created_by is optional (can be None)."""
128
+ incident = IncidentFactory()
129
+
130
+ jira_pm = JiraPostMortem.objects.create(
131
+ incident=incident,
132
+ jira_issue_key="INCIDENT-123",
133
+ jira_issue_id="10001",
134
+ created_by=None, # Optional field
135
+ )
136
+
137
+ assert jira_pm.created_by is None
138
+ assert jira_pm.incident == incident