@@ -90,17 +90,6 @@ private static long epoch(final String dateTime) {
9090 return localDateTime .toInstant (UTC ).toEpochMilli ();
9191 }
9292
93- private static long jumpToTime (final String dateTime ) throws InterruptedException {
94- final long jumpTime = epoch (dateTime );
95- CLOCK .useMockTime (jumpTime );
96- CLOCK .tick (5 ); // simulate move time forward
97-
98- // give thread to detectionCronScheduler and to quartz scheduler -
99- // (quartz idle time is weaved to 100 ms for test speed)
100- Thread .sleep (1000 );
101- return jumpTime ;
102- }
103-
10493 @ BeforeClass
10594 public void beforeClass () throws Exception {
10695 // ensure time is controlled via the TimeProvider CLOCK - ie weaving is working correctly
@@ -195,134 +184,111 @@ public void testOnboardingTaskRun() throws Exception {
195184 assertThat (alertLastTimestamp ).isEqualTo (epoch ("2020-02-16 00:00" ));
196185 }
197186
187+ /**
188+ * the alert detects anomalies on [feb 3 , feb6), [feb 10, feb 13), [feb 17, feb 20), [feb 24, feb
189+ * 27), etc...
190+ * this test checks the behaviour for the anomaly on [feb 17, feb 20)
191+ * time is currently "2020-02-16 15:00". time is increased day by day from feb 16 to feb 24, and
192+ * the state is checked every day
193+ * the max merge gap is P3D, so once there is no anomaly detected on [feb 23, feb 24), the
194+ * completed anomaly notification should be sent
195+ */
198196 @ Test (dependsOnMethods = "testOnboardingTaskRun" , timeOut = TEST_IMEOUT )
199- public void testDailyFeb21 () throws InterruptedException {
200- // get current number of anomalies
201- final int numAnomaliesBeforeDetectionRun = client .getAnomalies ().size ();
202-
203- final List <AnomalyApi > parentAnomalies = client .getParentAnomalies ();
204- assertThat (parentAnomalies ).hasSize (1 );
205-
206- // No notifications sent yet.
207- assertThat (nsf .getCount ()).isZero ();
208- long jumpTime = jumpToTime ("2020-02-21 00:00" );
209-
210- waitForDetectionRun ();
211- // wait for new anomalies to be created
212- while (client .getAnomalies ().size () == numAnomaliesBeforeDetectionRun ) {
213- Thread .sleep (1000 );
214- }
215-
216- // check that lastTimestamp after detection is the runTime of the cron
217- assertThat (getAlertLastTimestamp ()).isEqualTo (jumpTime );
218-
197+ public void testCompletedAnomalyIsSentCorrectly () throws InterruptedException {
198+ // sanity checks after the onboarding task
219199 assertThat (client .getParentAnomalies ()).hasSize (2 );
200+ // no notification happened yet - time has not increased since subscription group creation
201+ assertThat (nsf .notificationSentCount ()).isZero ();
220202
221- // There is at least 1 successful subscription group task
203+ // run the detections every day and check the results
204+ // the cron is at 5 am, so moving to 6 means the cron at 5 will be triggered
205+ // no anomaly on [Feb 16, Feb 17)
206+ CLOCK .useMockTime (epoch ("2020-02-17 06:00" ));
207+ waitForDetectionRun ();
208+ // notification cron is every day at 7 am, so moving to 8 will trigger a notification
209+ CLOCK .useMockTime (epoch ("2020-02-17 08:00" ));
222210 waitForNotificationTaskRun ();
223- assertThat (nsf .getCount ()).isEqualTo (0 );
224-
225- // Move time forward by 5 minutes to make subscription group task run again
226- jumpToTime ("2020-02-21 00:05" );
211+ assertThat (nsf .notificationSentCount ()).isZero ();
227212
213+ // anomaly on [Feb 17, Feb 18)
214+ CLOCK .useMockTime (epoch ("2020-02-18 06:00" ));
215+ assertThat (nsf .notificationSentCount ()).isZero ();
216+ waitForDetectionRun ();
217+ CLOCK .useMockTime (epoch ("2020-02-18 08:00" ));
228218 waitForNotificationTaskRun ();
229- assertThat (nsf .getCount ()).isEqualTo (1 );
230-
231- final NotificationPayloadApi notificationPayload = nsf .getNotificationPayload ();
219+ assertThat (nsf .notificationSentCount ()).isEqualTo (1 ); // a new anomaly is detected and notified
220+ final NotificationPayloadApi notificationPayload = nsf .lastNotificationPayload ();
232221 assertThat (notificationPayload .getAnomalyReports ()).hasSize (1 );
233-
234222 final AnomalyApi anomalyApi = notificationPayload .getAnomalyReports ().getFirst ().getAnomaly ();
235223 assertThat (anomalyApi .getStartTime ()).isEqualTo (new Date (epoch ("2020-02-17 00:00" )));
236- assertThat (anomalyApi .getEndTime ()).isEqualTo (new Date (epoch ("2020-02-21 00:00" )));
237- }
238-
239- @ Test (dependsOnMethods = "testDailyFeb21" , timeOut = TEST_IMEOUT )
240- public void testDailyFeb22 () throws InterruptedException {
241- jumpToTimeAndWait ("2020-02-22 00:06" );
242- assertThat (client .getParentAnomalies ()).hasSize (2 );
243- assertThat (nsf .getCount ()).isEqualTo (1 ); // no new notifications
244- }
245-
246- @ Test (dependsOnMethods = "testDailyFeb22" , timeOut = TEST_IMEOUT )
247- public void testDailyFeb23 () throws InterruptedException {
248- jumpToTimeAndWait ("2020-02-23 00:06" );
249- assertThat (client .getParentAnomalies ()).hasSize (2 );
250- assertThat (nsf .getCount ()).isEqualTo (1 ); // no new notifications
251- }
252-
253- @ Test (dependsOnMethods = "testDailyFeb23" , timeOut = TEST_IMEOUT )
254- public void testDailyFeb24 () throws InterruptedException {
255- jumpToTimeAndWait ("2020-02-24 00:06" );
256- assertThat (client .getParentAnomalies ()).hasSize (2 );
257- assertThat (nsf .getCount ()).isEqualTo (1 ); // no new notifications
258- }
259-
260- @ Test (dependsOnMethods = "testDailyFeb24" , timeOut = TEST_IMEOUT )
261- public void testDailyFeb25 () throws InterruptedException {
262- jumpToTimeAndWait ("2020-02-25 00:06" );
263-
264- final List <AnomalyApi > parentAnomalies = client .getParentAnomalies ();
265- assertThat (parentAnomalies ).hasSize (2 );
224+ assertThat (anomalyApi .getEndTime ()).isEqualTo (new Date (epoch ("2020-02-18 00:00" )));
266225
267- final AnomalyApi ongoingAnomaly = parentAnomalies . stream ( )
268- . filter ( a -> a . getStartTime (). equals ( new Date ( epoch ("2020-02-17 00 :00" ))))
269- . findFirst ()
270- . orElseThrow (() -> new AssertionError ( "Anomaly not found " ));
271-
272- // anomalies are merged here
273- assertThat ( ongoingAnomaly . getEndTime ()). isEqualTo ( new Date ( epoch ( "2020-02-25 00:00" )));
226+ // anomaly on [Feb 18, Feb 19 )
227+ CLOCK . useMockTime ( epoch ("2020-02-19 06 :00" ));
228+ waitForDetectionRun ();
229+ CLOCK . useMockTime ( epoch ( "2020-02-19 08:00 " ));
230+ waitForNotificationTaskRun ();
231+ assertThat ( nsf . notificationSentCount ()). isEqualTo (
232+ 1 ); // this point is anomalous but merged in current anomaly
274233
275- // No new notifications yet
276- assertThat (nsf .getCount ()).isEqualTo (1 );
277- }
234+ // anomaly on [Feb 19, Feb 20)
235+ CLOCK .useMockTime (epoch ("2020-02-20 06:00" ));
236+ waitForDetectionRun ();
237+ CLOCK .useMockTime (epoch ("2020-02-20 08:00" ));
238+ waitForNotificationTaskRun ();
239+ assertThat (nsf .notificationSentCount ()).isEqualTo (
240+ 1 ); // this point is anomalous but merged in current anomaly
278241
279- @ Test ( dependsOnMethods = "testDailyFeb25" , timeOut = TEST_IMEOUT )
280- public void testDailyMar3 () throws InterruptedException {
281- jumpToTimeAndWait ( "2020-03-03 00:06" );
242+ // sanity checks on the detection state
243+ // check that lastTimestamp after detection is the expected runTime of the cron, floored to alert granularity 2020-02-20 06:00 --> 2020-02-20 00:00
244+ assertThat ( getAlertLastTimestamp ()). isEqualTo ( epoch ( "2020-02-20 00:00" ) );
282245 assertThat (client .getParentAnomalies ()).hasSize (3 );
283- assertThat (nsf .getCount ()).isEqualTo (1 );
284- }
246+ final int anomaliesCurrentCount = client .getAnomalies ().size ();
285247
286- @ Test ( dependsOnMethods = "testDailyMar3" , timeOut = TEST_IMEOUT )
287- public void testDailyMar4 () throws InterruptedException {
288- jumpToTimeAndWait ( "2020-03-04 00:06" );
289-
290- final List < AnomalyApi > parentAnomalies = client .getParentAnomalies ( );
291- assertThat ( parentAnomalies ). hasSize ( 3 );
292-
293- // No new notifications yet
294- assertThat (nsf .getCount ()).isEqualTo (2 );
248+ // no anomaly on [Feb 20, Feb 21 )
249+ CLOCK . useMockTime ( epoch ( "2020-02-21 06:00" ));
250+ waitForDetectionRun ( );
251+ // ensure the number of anomalies hasn't changed
252+ assertThat ( client .getAnomalies ()). hasSize ( anomaliesCurrentCount );
253+ CLOCK . useMockTime ( epoch ( "2020-02-21 08:00" ) );
254+ waitForNotificationTaskRun ();
255+ // ensure the number of notification hasn't changed
256+ assertThat (nsf .notificationSentCount ()).isEqualTo (1 );
295257
296- final NotificationPayloadApi notificationPayload = nsf .getNotificationPayload ();
297- assertThat (notificationPayload .getAnomalyReports ()).hasSize (1 );
258+ // no anomaly on [Feb 21, Feb 22) - same checks as above
259+ CLOCK .useMockTime (epoch ("2020-02-22 06:00" ));
260+ waitForDetectionRun ();
261+ assertThat (client .getAnomalies ()).hasSize (anomaliesCurrentCount );
262+ CLOCK .useMockTime (epoch ("2020-02-22 08:00" ));
263+ waitForNotificationTaskRun ();
264+ assertThat (nsf .notificationSentCount ()).isEqualTo (1 );
298265
299- final AnomalyApi anomalyApi = notificationPayload .getAnomalyReports ().getFirst ().getAnomaly ();
300- assertThat (anomalyApi .getStartTime ()).isEqualTo (new Date (epoch ("2020-03-02 00:00" )));
301- assertThat (anomalyApi .getEndTime ()).isEqualTo (new Date (epoch ("2020-03-03 00:00" )));
302- }
266+ // no anomaly on [Feb 22, Feb 23) - same checks as above
267+ CLOCK .useMockTime (epoch ("2020-02-23 06:00" ));
268+ waitForDetectionRun ();
269+ assertThat (client .getAnomalies ()).hasSize (anomaliesCurrentCount );
270+ CLOCK .useMockTime (epoch ("2020-02-23 08:00" ));
271+ waitForNotificationTaskRun ();
272+ assertThat (nsf .notificationSentCount ()).isEqualTo (1 );
303273
304- @ Test (dependsOnMethods = "testDailyMar4" , timeOut = TEST_IMEOUT )
305- public void testDailyMar5 () throws InterruptedException {
306- jumpToTimeAndWait ("2020-03-05 00:06" );
274+ // no anomaly on [Feb 23, Feb 24)
275+ CLOCK .useMockTime (epoch ("2020-02-24 06:00" ));
276+ waitForDetectionRun ();
277+ assertThat (client .getAnomalies ()).hasSize (anomaliesCurrentCount );
278+ CLOCK .useMockTime (epoch ("2020-02-24 08:00" ));
279+ waitForNotificationTaskRun ();
280+ // maxMergeGap is P3D, so a notification for completed anomaly can be sent now
281+ assertThat (nsf .notificationSentCount ()).isEqualTo (2 );
282+ // sanity check on number of anomalies
307283 assertThat (client .getParentAnomalies ()).hasSize (3 );
308- assertThat (nsf .getCount ()).isEqualTo (3 );
309-
310- final NotificationPayloadApi payload = nsf .getNotificationPayload ();
284+ // ensure the completed anomaly notification was sent
285+ final NotificationPayloadApi payload = nsf .lastNotificationPayload ();
311286 assertThat (payload .getAnomalyReports ()).hasSize (0 );
312287 assertThat (payload .getCompletedAnomalyReports ()).hasSize (1 );
313288 }
314289
315- private void jumpToTimeAndWait (final String dateTime ) throws InterruptedException {
316- jumpToTime (dateTime ); // allow both detection and notification to run
317- waitForDetectionRun ();
318- waitForNotificationTaskRun ();
319- }
320-
321290 private void waitForDetectionRun () throws InterruptedException {
322291 nDetectionTaskRuns = waitFor (alertId , nDetectionTaskRuns );
323-
324- // Even after the task is complete, anomalies are persisted async. Giving another sec
325- Thread .sleep (1000 );
326292 }
327293
328294 private void waitForNotificationTaskRun () throws InterruptedException {
@@ -333,11 +299,11 @@ private int waitFor(final Long refId, int currentCount) throws InterruptedExcept
333299 int nTasks ;
334300 do {
335301 nTasks = client .getSuccessfulTasks (refId ).size ();
336- if (!( nTasks <= currentCount ) ) {
302+ if (nTasks > currentCount ) {
337303 break ;
338304 }
339- // should trigger another task after time jump
340- Thread .sleep (1000 );
305+ // should give time to run tasks
306+ Thread .sleep (500 );
341307 } while (true );
342308
343309 return nTasks ;
0 commit comments