@@ -104,12 +104,12 @@ def test_dismissal_validation_at_creation_time():
104104
105105def test_dismissal_expiry_bug_search_filters (db_session ):
106106 """
107- Test that reproduces the dismissedUntil expiry bug with search filters.
107+ Test that verifies the dismissedUntil expiry fix works correctly with search filters.
108108
109- This test demonstrates the bug where alerts with expired dismissedUntil
110- don't appear in searches for dismissed == false.
109+ This test demonstrates that alerts with expired dismissedUntil properly appear
110+ in searches for dismissed == false after the watcher processes them .
111111
112- This test should FAIL due to the bug (without watcher running) .
112+ This test should PASS with the fixed watcher implementation .
113113 """
114114 tenant_id = SINGLE_TENANT_UUID
115115
@@ -200,14 +200,14 @@ def test_dismissal_expiry_bug_search_filters(db_session):
200200 cel_query = "dismissed == false" , # or !dismissed
201201 )
202202
203- # BUG: This should return the alert because its dismissal has expired,
204- # but it won 't because there's no background process updating the status
203+ # Before watcher: Alert still appears dismissed in database because
204+ # the enrichment hasn 't been updated yet (dismissal expired but database not updated)
205205 non_dismissed_alerts_before = SearchEngine (tenant_id = tenant_id ).search_alerts (search_query_not_dismissed )
206206
207- # Demonstrate the bug exists - should return 0 because bug prevents proper filtering
207+ # Before watcher runs: Database still shows alert as dismissed (expected behavior)
208208 assert len (non_dismissed_alerts_before ) == 0 , (
209- "Bug reproduction confirmed : Alert doesn't appear in non-dismissed filter after "
210- "dismissUntil expires (this is the bug we're fixing )"
209+ "Before watcher : Alert doesn't appear in non-dismissed filter because "
210+ "database hasn't been updated yet (dismissal expired but enrichment not processed )"
211211 )
212212
213213 # NOW APPLY THE FIX: Run dismissal expiry watcher
@@ -216,7 +216,7 @@ def test_dismissal_expiry_bug_search_filters(db_session):
216216 # AFTER FIX: The alert should now appear correctly
217217 non_dismissed_alerts_after = SearchEngine (tenant_id = tenant_id ).search_alerts (search_query_not_dismissed )
218218
219- # This should now PASS - proving our fix works
219+ # After watcher runs: Alert should now appear in non-dismissed filter
220220 assert len (non_dismissed_alerts_after ) == 1 , (
221221 "FIXED: Alert now appears in non-dismissed filter after watcher processes expired dismissal"
222222 )
@@ -226,10 +226,10 @@ def test_dismissal_expiry_bug_search_filters(db_session):
226226
227227def test_dismissal_expiry_bug_sidebar_filter (db_session ):
228228 """
229- Test that reproduces the bug specifically with sidebar "Not dismissed" filter.
229+ Test that verifies sidebar "Not dismissed" filter works correctly after watcher processes expired dismissals .
230230
231231 This simulates the UI scenario described in the GitHub issue.
232- This test should FAIL due to the bug .
232+ This test should PASS with the fixed watcher implementation .
233233 """
234234 tenant_id = SINGLE_TENANT_UUID
235235
@@ -395,9 +395,9 @@ def test_dismissal_expiry_bug_sidebar_filter(db_session):
395395
396396def test_dismissal_expiry_bug_with_cel_filter (db_session ):
397397 """
398- Test the bug specifically with CEL-based filters like dismissed == false.
398+ Test that CEL-based filters work correctly with dismissed == false after watcher processes expired dismissals .
399399
400- This test should FAIL due to the bug .
400+ This test should PASS with the fixed watcher implementation .
401401 """
402402 tenant_id = SINGLE_TENANT_UUID
403403
@@ -475,13 +475,13 @@ def test_dismissal_expiry_bug_with_cel_filter(db_session):
475475 cel_query = cel_expr ,
476476 )
477477
478- # BUG: All of these should return 1 alert, but will return 0
478+ # Before watcher: Database hasn't been updated yet, so filter won't find the alert
479479 results_before = SearchEngine (tenant_id = tenant_id ).search_alerts (search_query )
480480
481- # Demonstrate the bug exists - should return 0 due to bug
481+ # Before watcher runs: Database still shows alert as dismissed
482482 assert len (results_before ) == 0 , (
483- f"Bug reproduction : CEL expression '{ cel_expr } ' should return 1 alert "
484- f"after dismissal expires, but returns { len ( results_before ) } due to bug "
483+ f"Before watcher : CEL expression '{ cel_expr } ' returns 0 because "
484+ f"database hasn't been updated yet (dismissal expired but not processed) "
485485 )
486486
487487 # APPLY THE FIX: Run dismissal expiry watcher
@@ -500,7 +500,7 @@ def test_dismissal_expiry_bug_with_cel_filter(db_session):
500500 # Should now return 1 alert correctly
501501 results_after = SearchEngine (tenant_id = tenant_id ).search_alerts (search_query )
502502
503- # This should now PASS - proving our fix works
503+ # After watcher runs: Should now work correctly
504504 assert len (results_after ) == 1 , (
505505 f"FIXED: CEL expression '{ cel_expr } ' now correctly returns 1 alert "
506506 f"after watcher processes expired dismissal"
@@ -776,6 +776,70 @@ def test_dismissal_expiry_bug_fixed_with_watcher(db_session):
776776 getattr (non_dismissed_alerts_after [0 ], 'disposable_dismissed' , None ) is None
777777
778778
779+ def test_dismissal_expiry_boolean_comparison_fix (db_session ):
780+ """
781+ Test that specifically validates the boolean comparison fix in get_alerts_with_expired_dismissals.
782+
783+ This test ensures that the function correctly finds dismissed alerts stored with different boolean
784+ formats across different database types, which was the root cause of the original bug.
785+ """
786+ from keep .api .bl .dismissal_expiry_bl import DismissalExpiryBl
787+ import datetime
788+ from keep .api .models .db .alert import AlertEnrichment
789+
790+ tenant_id = SINGLE_TENANT_UUID
791+ current_time = datetime .datetime .now (datetime .timezone .utc )
792+ past_time = current_time - datetime .timedelta (hours = 1 )
793+ past_time_str = past_time .strftime ("%Y-%m-%dT%H:%M:%S.%fZ" )
794+
795+ # Test different boolean representations that might be encountered
796+ test_cases = [
797+ {
798+ "fingerprint" : "boolean-test-True" ,
799+ "dismissed" : "True" , # Capitalized string (common in JavaScript/Python serialization)
800+ "description" : "Capitalized 'True' string"
801+ },
802+ {
803+ "fingerprint" : "boolean-test-true" ,
804+ "dismissed" : "true" , # Lowercase string (JSON standard)
805+ "description" : "Lowercase 'true' string"
806+ }
807+ ]
808+
809+ created_enrichments = []
810+ for test_case in test_cases :
811+ enrichment = AlertEnrichment (
812+ tenant_id = tenant_id ,
813+ alert_fingerprint = test_case ["fingerprint" ],
814+ enrichments = {
815+ "dismissed" : test_case ["dismissed" ],
816+ "dismissUntil" : past_time_str , # Expired timestamp
817+ "status" : "suppressed"
818+ },
819+ timestamp = current_time
820+ )
821+ created_enrichments .append (enrichment )
822+ db_session .add (enrichment )
823+
824+ db_session .commit ()
825+
826+ # Test that get_alerts_with_expired_dismissals finds all variations
827+ expired_dismissals = DismissalExpiryBl .get_alerts_with_expired_dismissals (db_session )
828+ found_fingerprints = {e .alert_fingerprint for e in expired_dismissals }
829+
830+ # Verify all test cases are found regardless of boolean format
831+ for test_case in test_cases :
832+ assert test_case ["fingerprint" ] in found_fingerprints , (
833+ f"Boolean comparison fix should find dismissed alerts with { test_case ['description' ]} "
834+ f"(found: { found_fingerprints } )"
835+ )
836+
837+ # Verify the found enrichment has the expected data
838+ test_found = next (e for e in expired_dismissals if e .alert_fingerprint == test_case ["fingerprint" ])
839+ assert test_found .enrichments ["dismissed" ] == test_case ["dismissed" ]
840+ assert test_found .enrichments ["dismissUntil" ] == past_time_str
841+
842+
779843def test_github_issue_5047_cel_filters_dismisseduntil_bug_fixed (db_session ):
780844 """
781845 Explicit test that solves GitHub Issue #5047:
0 commit comments