1
+ from django .db import models
2
+ from django .db .models import Value
3
+
1
4
from sentry .notifications .models .notificationaction import ActionTarget
2
5
from sentry .testutils .cases import TestCase
3
6
from sentry .testutils .silo import region_silo_test
@@ -61,7 +64,9 @@ def test_deduplicate_actions_different_types(self) -> None:
61
64
},
62
65
)
63
66
64
- actions_queryset = Action .objects .filter (id__in = [self .slack_action .id , email_action .id ])
67
+ actions_queryset = Action .objects .filter (
68
+ id__in = [self .slack_action .id , email_action .id ]
69
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
65
70
66
71
result = deduplicate_actions (actions_queryset )
67
72
@@ -84,7 +89,9 @@ def test_deduplicate_actions_same_slack_channels(self) -> None:
84
89
},
85
90
)
86
91
87
- actions_queryset = Action .objects .filter (id__in = [slack_action_1 .id , slack_action_2 .id ])
92
+ actions_queryset = Action .objects .filter (
93
+ id__in = [slack_action_1 .id , slack_action_2 .id ]
94
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
88
95
89
96
result = deduplicate_actions (actions_queryset )
90
97
@@ -111,7 +118,9 @@ def test_deduplicate_actions_different_slack_channels(self) -> None:
111
118
},
112
119
)
113
120
114
- actions_queryset = Action .objects .filter (id__in = [slack_action_1 .id , slack_action_2 .id ])
121
+ actions_queryset = Action .objects .filter (
122
+ id__in = [slack_action_1 .id , slack_action_2 .id ]
123
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
115
124
116
125
result = deduplicate_actions (actions_queryset )
117
126
@@ -135,7 +144,9 @@ def test_deduplicate_multiple_slack_actions_same_channel_different_name(self) ->
135
144
},
136
145
)
137
146
138
- actions_queryset = Action .objects .filter (id__in = [slack_action_1 .id , slack_action_2 .id ])
147
+ actions_queryset = Action .objects .filter (
148
+ id__in = [slack_action_1 .id , slack_action_2 .id ]
149
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
139
150
140
151
result = deduplicate_actions (actions_queryset )
141
152
@@ -167,7 +178,9 @@ def test_deduplicate_actions_same_slack_different_data(self) -> None:
167
178
data = {"notes" : "second action" },
168
179
)
169
180
170
- actions_queryset = Action .objects .filter (id__in = [slack_action_1 .id , slack_action_2 .id ])
181
+ actions_queryset = Action .objects .filter (
182
+ id__in = [slack_action_1 .id , slack_action_2 .id ]
183
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
171
184
172
185
result = deduplicate_actions (actions_queryset )
173
186
@@ -202,7 +215,9 @@ def test_deduplicate_actions_different_slack_integrations(self) -> None:
202
215
data = {"notes" : "second action" },
203
216
)
204
217
205
- actions_queryset = Action .objects .filter (id__in = [slack_action_1 .id , slack_action_2 .id ])
218
+ actions_queryset = Action .objects .filter (
219
+ id__in = [slack_action_1 .id , slack_action_2 .id ]
220
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
206
221
207
222
result = deduplicate_actions (actions_queryset )
208
223
@@ -231,7 +246,9 @@ def test_deduplicate_actions_email_same_target(self) -> None:
231
246
},
232
247
)
233
248
234
- actions_queryset = Action .objects .filter (id__in = [email_action_1 .id , email_action_2 .id ])
249
+ actions_queryset = Action .objects .filter (
250
+ id__in = [email_action_1 .id , email_action_2 .id ]
251
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
235
252
236
253
result = deduplicate_actions (actions_queryset )
237
254
@@ -257,7 +274,9 @@ def test_deduplicate_actions_email_different_target_identifier(self) -> None:
257
274
},
258
275
)
259
276
260
- actions_queryset = Action .objects .filter (id__in = [email_action_1 .id , email_action_2 .id ])
277
+ actions_queryset = Action .objects .filter (
278
+ id__in = [email_action_1 .id , email_action_2 .id ]
279
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
261
280
262
281
result = deduplicate_actions (actions_queryset )
263
282
@@ -285,7 +304,9 @@ def test_deduplicate_actions_email_different_target_type(self) -> None:
285
304
},
286
305
)
287
306
288
- actions_queryset = Action .objects .filter (id__in = [email_action_1 .id , email_action_2 .id ])
307
+ actions_queryset = Action .objects .filter (
308
+ id__in = [email_action_1 .id , email_action_2 .id ]
309
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
289
310
290
311
result = deduplicate_actions (actions_queryset )
291
312
@@ -319,7 +340,9 @@ def test_deduplicate_actions_email_different_fallthrough_type(self) -> None:
319
340
},
320
341
)
321
342
322
- actions_queryset = Action .objects .filter (id__in = [email_action_1 .id , email_action_2 .id ])
343
+ actions_queryset = Action .objects .filter (
344
+ id__in = [email_action_1 .id , email_action_2 .id ]
345
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
323
346
324
347
result = deduplicate_actions (actions_queryset )
325
348
@@ -352,7 +375,9 @@ def test_deduplicate_actions_email_everything_is_same(self) -> None:
352
375
},
353
376
)
354
377
355
- actions_queryset = Action .objects .filter (id__in = [email_action_1 .id , email_action_2 .id ])
378
+ actions_queryset = Action .objects .filter (
379
+ id__in = [email_action_1 .id , email_action_2 .id ]
380
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
356
381
357
382
result = deduplicate_actions (actions_queryset )
358
383
@@ -383,7 +408,7 @@ def test_deduplicate_actions_sentry_app_same_identifier(self) -> None:
383
408
384
409
actions_queryset = Action .objects .filter (
385
410
id__in = [sentry_app_action_1 .id , sentry_app_action_2 .id ]
386
- )
411
+ ). annotate ( workflow_id = Value ( 1 , output_field = models . IntegerField ()))
387
412
388
413
result = deduplicate_actions (actions_queryset )
389
414
@@ -408,7 +433,9 @@ def test_deduplicate_actions_webhook_same_target_identifier(self) -> None:
408
433
},
409
434
)
410
435
411
- actions_queryset = Action .objects .filter (id__in = [webhook_action_1 .id , webhook_action_2 .id ])
436
+ actions_queryset = Action .objects .filter (
437
+ id__in = [webhook_action_1 .id , webhook_action_2 .id ]
438
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
412
439
413
440
result = deduplicate_actions (actions_queryset )
414
441
@@ -421,7 +448,9 @@ def test_deduplicate_actions_plugin_actions(self) -> None:
421
448
422
449
plugin_action_2 = self .create_action (type = Action .Type .PLUGIN )
423
450
424
- actions_queryset = Action .objects .filter (id__in = [plugin_action_1 .id , plugin_action_2 .id ])
451
+ actions_queryset = Action .objects .filter (
452
+ id__in = [plugin_action_1 .id , plugin_action_2 .id ]
453
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
425
454
426
455
result = deduplicate_actions (actions_queryset )
427
456
@@ -451,7 +480,9 @@ def test_deduplicate_actions_mixed_types_integration_bucket(self) -> None:
451
480
},
452
481
)
453
482
454
- actions_queryset = Action .objects .filter (id__in = [slack_action .id , pagerduty_action .id ])
483
+ actions_queryset = Action .objects .filter (
484
+ id__in = [slack_action .id , pagerduty_action .id ]
485
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
455
486
456
487
result = deduplicate_actions (actions_queryset )
457
488
@@ -486,7 +517,9 @@ def test_deduplicate_actions_ticketing_actions(self) -> None:
486
517
},
487
518
)
488
519
489
- actions_queryset = Action .objects .filter (id__in = [jira_action_1 .id , jira_action_2 .id ])
520
+ actions_queryset = Action .objects .filter (
521
+ id__in = [jira_action_1 .id , jira_action_2 .id ]
522
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
490
523
491
524
result = deduplicate_actions (actions_queryset )
492
525
@@ -520,7 +553,9 @@ def test_deduplicate_actions_ticketing_actions_same_integration_and_data(self) -
520
553
},
521
554
)
522
555
523
- actions_queryset = Action .objects .filter (id__in = [jira_action_1 .id , jira_action_2 .id ])
556
+ actions_queryset = Action .objects .filter (
557
+ id__in = [jira_action_1 .id , jira_action_2 .id ]
558
+ ).annotate (workflow_id = Value (1 , output_field = models .IntegerField ()))
524
559
525
560
result = deduplicate_actions (actions_queryset )
526
561
@@ -541,11 +576,55 @@ def test_deduplicate_actions_single_action(self) -> None:
541
576
"""Test deduplication with single action."""
542
577
single_action = self .slack_action
543
578
544
- actions_queryset = Action .objects .filter (id = single_action .id )
579
+ actions_queryset = Action .objects .filter (id = single_action .id ).annotate (
580
+ workflow_id = Value (1 , output_field = models .IntegerField ())
581
+ )
545
582
546
583
result = deduplicate_actions (actions_queryset )
547
584
548
585
# Should return the single action
549
586
result_ids = list (result .values_list ("id" , flat = True ))
550
587
assert len (result_ids ) == 1
551
588
assert result_ids [0 ] == single_action .id
589
+
590
+ def test_deduplicate_actions_same_actions_different_workflows (self ) -> None :
591
+ """Test that identical actions from different workflows are NOT deduplicated."""
592
+ # Create two identical Slack actions
593
+ slack_action_1 = self .create_action (
594
+ type = Action .Type .SLACK ,
595
+ integration_id = self .slack_integration .id ,
596
+ config = {
597
+ "target_type" : ActionTarget .SPECIFIC ,
598
+ "target_identifier" : "channel-123" ,
599
+ "target_display" : "Test Channel" ,
600
+ },
601
+ )
602
+
603
+ slack_action_2 = self .create_action (
604
+ type = Action .Type .SLACK ,
605
+ integration_id = self .slack_integration .id ,
606
+ config = {
607
+ "target_type" : ActionTarget .SPECIFIC ,
608
+ "target_identifier" : "channel-123" ,
609
+ "target_display" : "Test Channel" ,
610
+ },
611
+ )
612
+
613
+ # Annotate with different workflow IDs
614
+ actions_queryset = Action .objects .filter (
615
+ id__in = [slack_action_1 .id , slack_action_2 .id ]
616
+ ).annotate (
617
+ workflow_id = models .Case (
618
+ models .When (id = slack_action_1 .id , then = models .Value (1 )),
619
+ models .When (id = slack_action_2 .id , then = models .Value (2 )),
620
+ output_field = models .IntegerField (),
621
+ )
622
+ )
623
+
624
+ result = deduplicate_actions (actions_queryset )
625
+
626
+ # Both actions should remain since they're from different workflows
627
+ result_ids = list (result .values_list ("id" , flat = True ))
628
+ assert len (result_ids ) == 2
629
+ assert slack_action_1 .id in result_ids
630
+ assert slack_action_2 .id in result_ids
0 commit comments