1+ """
2+ Test for Keep Provider time_delta filtering bug.
3+ This test reproduces the issue described in https://github.com/keephq/keep/issues/5180
4+ """
5+
6+ import datetime
7+ import pytest
8+ import time
9+ import uuid
10+ from datetime import timezone , timedelta
11+ from freezegun import freeze_time
12+
13+ from keep .api .core .dependencies import SINGLE_TENANT_UUID
14+ from keep .api .models .db .alert import Alert , LastAlert
15+ from keep .api .models .alert import AlertStatus
16+ from keep .api .utils .enrichment_helpers import convert_db_alerts_to_dto_alerts
17+ from keep .providers .keep_provider .keep_provider import KeepProvider
18+ from keep .contextmanager .contextmanager import ContextManager
19+ from keep .providers .models .provider_config import ProviderConfig
20+
21+
22+ def _create_valid_event (d , lastReceived = None ):
23+ """Helper function to create a valid event similar to conftest.py"""
24+ event = {
25+ "id" : str (uuid .uuid4 ()),
26+ "name" : "some-test-event" ,
27+ "status" : "firing" ,
28+ "lastReceived" : (
29+ str (lastReceived )
30+ if lastReceived
31+ else datetime .datetime .now (tz = timezone .utc ).isoformat ()
32+ ),
33+ }
34+ event .update (d )
35+ return event
36+
37+
38+ def test_keep_provider_time_delta_filtering_bug (db_session ):
39+ """
40+ Test that reproduces the time_delta filtering bug.
41+
42+ This test verifies that when using Keep Provider with version 2 and time_delta,
43+ only alerts within the specified timeframe are returned, not all alerts.
44+ """
45+ # Setup context
46+ tenant_id = SINGLE_TENANT_UUID
47+ context_manager = ContextManager (
48+ tenant_id = tenant_id ,
49+ workflow_id = None
50+ )
51+
52+ # Create KeepProvider instance
53+ provider_config = ProviderConfig (authentication = {})
54+ provider = KeepProvider (
55+ context_manager = context_manager ,
56+ provider_id = "test-keep" ,
57+ config = provider_config
58+ )
59+
60+ # Create alerts with different timestamps
61+ now = datetime .datetime .now (timezone .utc )
62+ old_time = now - timedelta (hours = 2 ) # 2 hours ago
63+ recent_time = now - timedelta (seconds = 30 ) # 30 seconds ago
64+
65+ # Create alert details similar to conftest.py setup_alerts
66+ alert_details = [
67+ {
68+ "source" : ["test" ],
69+ "status" : AlertStatus .FIRING .value ,
70+ "lastReceived" : old_time .isoformat (),
71+ "fingerprint" : "old-alert-fingerprint" ,
72+ "id" : "old-alert"
73+ },
74+ {
75+ "source" : ["test" ],
76+ "status" : AlertStatus .FIRING .value ,
77+ "lastReceived" : recent_time .isoformat (),
78+ "fingerprint" : "recent-alert-fingerprint" ,
79+ "id" : "recent-alert"
80+ }
81+ ]
82+
83+ # Create Alert objects
84+ alerts = []
85+ for detail in alert_details :
86+ # Create timestamps from lastReceived
87+ timestamp = datetime .datetime .fromisoformat (detail ["lastReceived" ].replace ('Z' , '+00:00' ))
88+
89+ alert = Alert (
90+ tenant_id = tenant_id ,
91+ provider_type = "test" ,
92+ provider_id = "test" ,
93+ event = _create_valid_event (detail , detail ["lastReceived" ]),
94+ fingerprint = detail ["fingerprint" ],
95+ timestamp = timestamp
96+ )
97+ alerts .append (alert )
98+
99+ # Add alerts to database
100+ db_session .add_all (alerts )
101+ db_session .commit ()
102+
103+ # Create LastAlert entries
104+ last_alerts = []
105+ for alert in alerts :
106+ last_alert = LastAlert (
107+ tenant_id = tenant_id ,
108+ fingerprint = alert .fingerprint ,
109+ timestamp = alert .timestamp ,
110+ first_timestamp = alert .timestamp ,
111+ alert_id = alert .id ,
112+ )
113+ last_alerts .append (last_alert )
114+
115+ db_session .add_all (last_alerts )
116+ db_session .commit ()
117+
118+ # Test with time_delta of approximately 1 minute (0.000694445 days)
119+ # This should only return the recent alert, not the old one
120+ time_delta_1_minute = 0.000694445
121+
122+ # Query using Keep Provider version 2 (which uses SearchEngine)
123+ with freeze_time (now ):
124+ results = provider ._query (
125+ version = 2 ,
126+ filter = "status == 'firing'" ,
127+ time_delta = time_delta_1_minute ,
128+ limit = 10000
129+ )
130+
131+ # This should fail because the bug causes all alerts to be returned
132+ # instead of just the ones within the time_delta
133+ assert len (results ) == 1 , f"Expected 1 alert within time_delta, but got { len (results )} "
134+
135+ # Verify it's the recent alert
136+ assert results [0 ].id == "recent-alert"
137+
138+
139+ def test_keep_provider_time_delta_filtering_version_1 (db_session ):
140+ """
141+ Test that version 1 of Keep Provider correctly filters by time_delta.
142+ This should work correctly as it uses get_alerts_with_filters directly.
143+ """
144+ # This test is simpler since we're testing version 1 which should work
145+ from keep .api .core .db import get_alerts_with_filters
146+
147+ tenant_id = SINGLE_TENANT_UUID
148+
149+ # Create alerts with different timestamps
150+ now = datetime .datetime .now (timezone .utc )
151+ old_time = now - timedelta (hours = 2 ) # 2 hours ago
152+ recent_time = now - timedelta (seconds = 30 ) # 30 seconds ago
153+
154+ # Create Alert objects directly
155+ alerts = []
156+
157+ old_alert = Alert (
158+ tenant_id = tenant_id ,
159+ provider_type = "test" ,
160+ provider_id = "test" ,
161+ event = _create_valid_event ({
162+ "id" : "old-alert-v1" ,
163+ "status" : AlertStatus .FIRING .value ,
164+ "lastReceived" : old_time .isoformat (),
165+ }),
166+ fingerprint = "old-alert-v1-fingerprint" ,
167+ timestamp = old_time
168+ )
169+
170+ recent_alert = Alert (
171+ tenant_id = tenant_id ,
172+ provider_type = "test" ,
173+ provider_id = "test" ,
174+ event = _create_valid_event ({
175+ "id" : "recent-alert-v1" ,
176+ "status" : AlertStatus .FIRING .value ,
177+ "lastReceived" : recent_time .isoformat (),
178+ }),
179+ fingerprint = "recent-alert-v1-fingerprint" ,
180+ timestamp = recent_time
181+ )
182+
183+ alerts = [old_alert , recent_alert ]
184+
185+ # Add alerts to database
186+ db_session .add_all (alerts )
187+ db_session .commit ()
188+
189+ # Create LastAlert entries (required by get_alerts_with_filters)
190+ last_alerts = []
191+ for alert in alerts :
192+ last_alert = LastAlert (
193+ tenant_id = tenant_id ,
194+ fingerprint = alert .fingerprint ,
195+ timestamp = alert .timestamp ,
196+ first_timestamp = alert .timestamp ,
197+ alert_id = alert .id ,
198+ )
199+ last_alerts .append (last_alert )
200+
201+ db_session .add_all (last_alerts )
202+ db_session .commit ()
203+
204+ # Test using get_alerts_with_filters directly (version 1 approach)
205+ time_delta_1_minute = 0.000694445
206+
207+ with freeze_time (now ):
208+ filtered_alerts = get_alerts_with_filters (
209+ tenant_id = tenant_id ,
210+ filters = None , # Test just time_delta filtering without additional filters
211+ time_delta = time_delta_1_minute
212+ )
213+
214+ # This should work correctly - only return recent alert
215+ assert len (filtered_alerts ) == 1 , f"Expected 1 alert within time_delta, but got { len (filtered_alerts )} "
216+ assert filtered_alerts [0 ].event ["id" ] == "recent-alert-v1"
0 commit comments