/* * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hawkular.alerts.extensions; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.drools.core.ClockType; import org.drools.core.time.impl.PseudoClockScheduler; import org.hawkular.alerts.api.model.event.Event; import org.junit.Assert; import org.junit.Test; import org.kie.api.KieBase; import org.kie.api.KieBaseConfiguration; import org.kie.api.conf.EventProcessingOption; import org.kie.api.io.ResourceType; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.runtime.conf.ClockTypeOption; import org.kie.internal.KnowledgeBaseFactory; import org.kie.internal.utils.KieHelper; /** * @author Jay Shaughnessy * @author Lucas Ponce */ public class DrlTests { public static final String TEST_TENANT = "28026b36-8fe4-4332-84c8-524e173a68bf"; public static final String DATA_ID = "data-id"; KieBaseConfiguration kieBaseConfiguration; KieBase kieBase; KieSessionConfiguration kieSessionConf; KieSession kieSession; List<String> results; PseudoClockScheduler clock; public static String uuid() { return UUID.randomUUID().toString(); } private void startSession(String drl) { kieBaseConfiguration = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(); kieBaseConfiguration.setOption( EventProcessingOption.STREAM ); kieBase = new KieHelper().addContent(drl, ResourceType.DRL).build(kieBaseConfiguration); kieSessionConf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration(); kieSessionConf.setOption( ClockTypeOption.get( "pseudo" ) ); kieSession = kieBase.newKieSession(kieSessionConf, null); clock = kieSession.getSessionClock(); // kieSession.addEventListener(new DebugAgendaEventListener()); // kieSession.addEventListener(new DebugRuleRuntimeEventListener()); results = new ArrayList<>(); kieSession.setGlobal("clock", clock); kieSession.setGlobal("results", results); } private void stopSession() { kieSession.dispose(); } private void insert(Object... objs) { for (Object o : objs) { kieSession.insert(o); } kieSession.fireAllRules(); } @Test public void marketingScenarioTest() { String drl = " import org.hawkular.alerts.api.model.event.Event; \n " + " import org.kie.api.time.SessionClock; \n " + " import java.util.List; \n " + " global java.util.List results; \n" + " global SessionClock clock; \n" + " declare Event \n" + " @role( event ) \n" + " @expires( 10m ) \n" + " @timestamp( ctime ) \n" + " end \n\n" + " declare AccountId accountId : String end \n " + " rule \"Extract accountId\" \n " + " when \n " + " Event( $accountId : context[\"accountId\"] != null ) \n " + " not AccountId ( accountId == $accountId ) \n " + " then \n" + " insert( new AccountId ( $accountId ) ); \n " + " end \n " + " \n " + " rule \"Marketing Scenario\" \n " + " when" + " AccountId ( $accountId : accountId ) \n " + " accumulate( $event : Event( context[\"accountId\"] == $accountId) over window:time(10s); \n " + " $sizeEvents : count( $event ), \n" + " $eventList : collectList( $event ); \n" + " $sizeEvents > 2) \n " + " then \n " + " System.out.println(\"Clock: \" + clock.getCurrentTime()); \n " + " System.out.println(\"Account: \" + $accountId + \" has \" + $sizeEvents + " + " \" events in 10s \"); \n " + " System.out.println(\"Events: \"); \n " + " $eventList.stream().forEach(e -> System.out.println(e));" + " results.add( $accountId ); \n " + " end \n " + " \n "; startSession(drl); // Init t0 clock.setStartupTime(1); long now = clock.getCurrentTime(); // User1 buys 5 times in < 10 seconds Event e1 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E1 - Buy Book"); e1.addContext("duration", "1000"); e1.addContext("accountId", "user1"); // User2 buys 3 times > 10 seconds Event e6 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E6 - Buy Book"); e6.addContext("duration", "1000"); e6.addContext("accountId", "user2"); insert(e1, e6); // t0 + 1000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e2 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E2 - Buy Music"); e2.addContext("duration", "2000"); e2.addContext("accountId", "user1"); insert(e2); // t0 + 2000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e3 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E3 - Buy Groceries"); e3.addContext("duration", "1500"); e3.addContext("accountId", "user1"); insert(e3); // t0 + 3000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e4 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E4 - Buy VideoGames"); e4.addContext("duration", "3000"); e4.addContext("accountId", "user1"); insert(e4); // t0 + 4000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e5 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E5 - Buy VideoGames"); e5.addContext("duration", "3000"); e5.addContext("accountId", "user1"); insert(e5); // t0 + 5000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e7 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E7 - Buy Music"); e7.addContext("duration", "2000"); e7.addContext("accountId", "user2"); insert(e7); // t0 + 11000 now = clock.advanceTime(6000, TimeUnit.MILLISECONDS); Event e8 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E8 - Buy Groceries"); e8.addContext("duration", "1500"); e8.addContext("accountId", "user2"); insert(e8); stopSession(); Assert.assertEquals(4, results.size()); Assert.assertTrue(results.contains("user1")); } @Test public void marketingScenarioDebugTest() { String drl = " import " + TaggedEvent.class.getCanonicalName() + "; \n " + " import org.kie.api.time.SessionClock; \n" + " global java.util.List results; \n" + " global SessionClock clock; \n " + " declare AccountId accountId : String end \n " + " declare TaggedEvent \n" + " @role( event ) \n" + " @expires( 10m ) \n" + " @timestamp( ctime ) \n" + " end \n\n" + " rule \"Extract accountId\" \n " + " when \n " + " TaggedEvent( $accountId : tags[\"accountId\"] != null ) \n " + " not AccountId ( accountId == $accountId ) \n " + " then \n" + " insert( new AccountId ( $accountId ) ); \n " + " end \n " + " \n " + " rule \"Marketing Scenario\" \n " + " when" + " AccountId ( $accountId : accountId ) \n " + " accumulate( $event : TaggedEvent( tags[\"accountId\"] == $accountId) over window:time(10s); \n " + " $sizeEvents : count( $event ), \n" + " $eventList : collectList( $event ); \n" + " $sizeEvents > 2) \n " + " then \n " + " System.out.println(\"Clock: \" + clock.getCurrentTime()); \n" + " System.out.println(\"Account: \" + $accountId + \" has \" + $sizeEvents + " + " \" events in 10s \"); \n " + " System.out.println(\"Events: \" + $eventList); \n " + " results.add( $accountId ); \n " + " end \n " + " \n "; KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration(); sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) ); KieSession kieSession = new KieHelper().addContent( drl, ResourceType.DRL ) .build( EventProcessingOption.STREAM ) .newKieSession(sessionConfig, null); PseudoClockScheduler clock = kieSession.getSessionClock(); List<String> results = new ArrayList<String>(); kieSession.setGlobal( "results", results ); kieSession.setGlobal( "clock", clock ); // Init t0 clock.setStartupTime(1); long now = clock.getCurrentTime(); // User1 buys 5 times in < 10 seconds TaggedEvent e1 = new TaggedEvent( "E1" , now ).addTag( "accountId", "user1" ); // User2 buys 3 times > 10 seconds TaggedEvent e6 = new TaggedEvent( "E6", now ).addTag( "accountId", "user2" ); kieSession.insert(e1); kieSession.insert(e6); kieSession.fireAllRules(); // t0 + 1000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e2 = new TaggedEvent( "2", now ).addTag( "accountId", "user1" ); kieSession.insert(e2); kieSession.fireAllRules(); // t0 + 2000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e3 = new TaggedEvent( "3", now ).addTag( "accountId", "user1" ); kieSession.insert(e3); kieSession.fireAllRules(); // t0 + 3000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e4 = new TaggedEvent( "4", now ).addTag( "accountId", "user1" ); kieSession.insert(e4); kieSession.fireAllRules(); // t0 + 4000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e5 = new TaggedEvent( "5", now ).addTag( "accountId", "user1" ); kieSession.insert(e5); kieSession.fireAllRules(); // t0 + 5000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e7 = new TaggedEvent( "7", now ).addTag( "accountId", "user2" ); kieSession.insert(e7); kieSession.fireAllRules(); // t0 + 11000 now = clock.advanceTime(6000, TimeUnit.MILLISECONDS); TaggedEvent e8 = new TaggedEvent( "8", now ).addTag( "accountId", "user2" ); kieSession.insert(e8); kieSession.fireAllRules(); kieSession.dispose(); Assert.assertEquals(4, results.size()); Assert.assertTrue(results.contains("user1")); } @Test public void fraudScenarioTest() { String drl = " import org.hawkular.alerts.api.model.event.Event; \n " + " import org.kie.api.time.SessionClock; \n " + " import java.util.List; \n " + " global java.util.List results; \n" + " global SessionClock clock; \n" + " declare Event \n" + " @role( event ) \n" + " @expires( 10m ) \n" + " @timestamp( ctime ) \n" + " end \n\n" + " declare AccountId accountId : String end \n " + " rule \"Extract accountId\" \n " + " when \n " + " Event( $accountId : tags[\"accountId\"] != null ) \n " + " not AccountId ( accountId == $accountId ) \n " + " then \n" + " insert( new AccountId ( $accountId ) ); \n " + " end \n " + " \n " + " rule \"Fraud Scenario\" \n " + " when" + " AccountId ( $accountId : accountId ) \n " + " accumulate( $event : Event( tags[\"accountId\"] == $accountId, $location : tags[\"location\"] ) " + "over window:time(10s); \n " + " $sizeEvents : count( $event ), \n" + " $eventList : collectList( $event ), \n" + " $locations : collectSet( $location ); \n" + " $sizeEvents > 1, \n " + " $locations.size > 1 ) \n " + " then \n " + " System.out.println(\"Clock: \" + clock.getCurrentTime()); \n " + " System.out.println(\"Account: \" + $accountId + \" has \" + $sizeEvents + " + " \" events in 10s from these locations: \" + $locations); \n " + " System.out.println(\"Events: \"); \n " + " $eventList.stream().forEach(e -> System.out.println(e));" + " results.add( $accountId ); \n " + " end \n " + " \n "; startSession(drl); // Init t0 clock.setStartupTime(1); long now = clock.getCurrentTime(); // User1 buys 5 times in < 10 seconds from different locations Event e1 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Book"); e1.addTag("duration", "1000"); e1.addTag("accountId", "user1"); e1.addTag("location", "ip1"); // User2 buys 3 times > 10 seconds from single location Event e6 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Book"); e6.addTag("duration", "1000"); e6.addTag("accountId", "user2"); e6.addTag("location", "ip3"); // User3 buys 5 times in < 10 seconds from single location Event e11 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Book"); e11.addTag("duration", "1000"); e11.addTag("accountId", "user3"); e11.addTag("location", "ip10"); insert(e1, e6, e11); // t0 + 1000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e2 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Music"); e2.addTag("duration", "2000"); e2.addTag("accountId", "user1"); e2.addTag("location", "ip1"); Event e12 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Music"); e12.addTag("duration", "2000"); e12.addTag("accountId", "user3"); e12.addTag("location", "ip10"); insert(e2, e12); // t0 + 2000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e3 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Groceries"); e3.addTag("duration", "1500"); e3.addTag("accountId", "user1"); e3.addTag("location", "ip1"); Event e13 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Groceries"); e13.addTag("duration", "1500"); e13.addTag("accountId", "user3"); e13.addTag("location", "ip10"); insert(e3, e13); // t0 + 3000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e4 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy VideoGames"); e4.addTag("duration", "3000"); e4.addTag("accountId", "user1"); e4.addTag("location", "ip2"); Event e14 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy VideoGames"); e14.addTag("duration", "3000"); e14.addTag("accountId", "user3"); e14.addTag("location", "ip10"); insert(e4, e14); // t0 + 4000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e5 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy VideoGames"); e5.addTag("duration", "3000"); e5.addTag("accountId", "user1"); e5.addTag("location", "ip1"); Event e15 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy VideoGames"); e15.addTag("duration", "3000"); e15.addTag("accountId", "user3"); e15.addTag("location", "ip10"); insert(e5, e15); // t0 + 15000 now = clock.advanceTime(11000, TimeUnit.MILLISECONDS); Event e7 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Music"); e7.addTag("duration", "2000"); e7.addTag("accountId", "user2"); e7.addTag("location", "ip4"); insert(e7); // t0 + 20000 now = clock.advanceTime(5000, TimeUnit.MILLISECONDS); Event e8 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "Buy Groceries"); e8.addTag("duration", "1500"); e8.addTag("accountId", "user2"); e8.addTag("location", "ip5"); insert(e8); stopSession(); Assert.assertEquals(3, results.size()); Assert.assertTrue(results.contains("user1")); } @Test public void fraudScenarioDebugTest() { String drl = " import " + TaggedEvent.class.getCanonicalName() + "; \n " + " import org.kie.api.time.SessionClock; \n " + " import java.util.List; \n " + " global java.util.List results; \n" + " global SessionClock clock; \n" + " declare TaggedEvent \n" + " @role( event ) \n" + " @expires( 10m ) \n" + " @timestamp( ctime ) \n" + " end \n\n" + " declare AccountId accountId : String end \n " + " rule \"Extract accountId\" \n " + " when \n " + " TaggedEvent( $accountId : tags[\"accountId\"] != null ) \n " + " not AccountId ( accountId == $accountId ) \n " + " then \n" + " insert( new AccountId ( $accountId ) ); \n " + " end \n " + " \n " + " rule \"Fraud Scenario\" \n " + " when" + " AccountId ( $accountId : accountId ) \n " + " accumulate( $event : TaggedEvent( tags[\"accountId\"] == $accountId, $location : tags[\"location\"] ) " + "over window:time(10s); \n " + " $sizeEvents : count( $event ), \n" + " $eventList : collectList( $event ), \n" + " $locations : collectSet( $location ); \n" + " $sizeEvents > 1, \n " + " $locations.size > 1 ) \n " + " then \n " + " System.out.println(\"Clock: \" + clock.getCurrentTime()); \n " + " System.out.println(\"Account: \" + $accountId + \" has \" + $sizeEvents + " + " \" events in 10s from these locations: \" + $locations); \n " + " System.out.println(\"Events: \"); \n " + " $eventList.stream().forEach(e -> System.out.println(e));" + " results.add( $accountId ); \n " + " end \n " + " \n "; KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration(); sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) ); KieSession kieSession = new KieHelper().addContent( drl, ResourceType.DRL ) .build( EventProcessingOption.STREAM ) .newKieSession(sessionConfig, null); PseudoClockScheduler clock = kieSession.getSessionClock(); List<String> results = new ArrayList<String>(); kieSession.setGlobal( "results", results ); kieSession.setGlobal( "clock", clock ); System.out.println("[" + new Date() + "] start!"); // Init t0 clock.setStartupTime(1); long now = clock.getCurrentTime(); // User1 buys 5 times in < 10 seconds from different locations TaggedEvent e1 = new TaggedEvent( "E1" , now ).addTag( "accountId", "user1" ).addTag("location", "ip1"); // User2 buys 3 times > 10 seconds from single location TaggedEvent e6 = new TaggedEvent( "E6" , now ).addTag( "accountId", "user2" ).addTag("location", "ip3"); // User3 buys 5 times in < 10 seconds from single location TaggedEvent e11 = new TaggedEvent( "E11" , now ).addTag( "accountId", "user3" ).addTag("location", "ip10"); kieSession.insert(e1); kieSession.insert(e6); kieSession.insert(e11); kieSession.fireAllRules(); // t0 + 1000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e2 = new TaggedEvent( "E2" , now ).addTag( "accountId", "user1" ).addTag("location", "ip1"); TaggedEvent e12 = new TaggedEvent( "E12" , now ).addTag( "accountId", "user3" ).addTag("location", "ip10"); kieSession.insert(e2); kieSession.insert(e12); kieSession.fireAllRules(); // t0 + 2000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e3 = new TaggedEvent( "E3" , now ).addTag( "accountId", "user1" ).addTag("location", "ip1"); TaggedEvent e13 = new TaggedEvent( "E13" , now ).addTag( "accountId", "user3" ).addTag("location", "ip10"); kieSession.insert(e3); kieSession.insert(e13); kieSession.fireAllRules(); // t0 + 3000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e4 = new TaggedEvent( "E4" , now ).addTag( "accountId", "user1" ).addTag("location", "ip2"); TaggedEvent e14 = new TaggedEvent( "E14" , now ).addTag( "accountId", "user3" ).addTag("location", "ip10"); kieSession.insert(e4); kieSession.insert(e14); kieSession.fireAllRules(); // t0 + 4000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); TaggedEvent e5 = new TaggedEvent( "E5" , now ).addTag( "accountId", "user1" ).addTag("location", "ip1"); TaggedEvent e15 = new TaggedEvent( "E15" , now ).addTag( "accountId", "user3" ).addTag("location", "ip10"); kieSession.insert(e5); kieSession.insert(e15); kieSession.fireAllRules(); // t0 + 15000 now = clock.advanceTime(11000, TimeUnit.MILLISECONDS); TaggedEvent e7 = new TaggedEvent( "E7" , now ).addTag( "accountId", "user2" ).addTag("location", "ip4"); kieSession.insert(e7); kieSession.fireAllRules(); // t0 + 20000 now = clock.advanceTime(5000, TimeUnit.MILLISECONDS); TaggedEvent e8 = new TaggedEvent( "E8" , now ).addTag( "accountId", "user2" ).addTag("location", "ip5"); kieSession.insert(e8); kieSession.fireAllRules(); kieSession.dispose(); Assert.assertEquals(3, results.size()); Assert.assertTrue(results.contains("user1")); } @Test public void customerRetentionScenarioTest() { String drl = " import org.hawkular.alerts.api.model.event.Event; \n " + " import org.kie.api.time.SessionClock; \n " + " import java.util.List; \n " + " global java.util.List results; \n" + " global SessionClock clock; \n" + " declare Event \n" + " @role( event ) \n" + " @expires( 10m ) \n" + " @timestamp( ctime ) \n" + " end \n\n" + " declare TraceId traceId : String end \n " + " rule \"Extract traceId\" \n " + " when \n " + " Event( $traceId : tags[\"traceId\"] != null ) \n " + " not TraceId ( traceId == $traceId ) \n " + " then \n" + " insert( new TraceId ( $traceId ) ); \n " + " end \n " + " \n " + " rule \"Custmer Retention Scenario\" \n " + " when" + " TraceId ( $traceId : traceId ) \n " + " accumulate( $event : Event( tags[\"traceId\"] == $traceId, \n" + " (category == \"Credit Check\" && text == \"Exceptionally Good\") || " + " (category == \"Stock Check\" && text == \"Out of Stock\") ); \n " + " $sizeEvents : count( $event ), \n" + " $eventList : collectList( $event )," + " $users : collectSet( $event.getTags().get(\"accountId\") );" + " $sizeEvents > 1," + " $users.size == 1 ) \n " + " then \n " + " System.out.println(\"Clock: \" + clock.getCurrentTime()); \n " + " System.out.println(\"TraceId: \" + $traceId + " + " \" for user: \" + $users + \" deserves a special offer. \"); " + " System.out.println(\"Events: \"); \n " + " $eventList.stream().forEach(e -> System.out.println(e));" + " results.add( $traceId ); \n " + " end \n " + " \n "; startSession(drl); // Init t0 clock.setStartupTime(1); long now = clock.getCurrentTime(); Event e1 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Credit Check", "Exceptionally Good"); e1.addTag("duration", "1000"); e1.addTag("traceId", "trace1"); e1.addTag("accountId", "user1"); Event e11 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Credit Check", "Exceptionally Good"); e11.addTag("duration", "1000"); e11.addTag("traceId", "trace4"); e11.addTag("accountId", "user2"); insert(e1, e11); // t0 + 1000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e2 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Stock Check", "Out of Stock"); e2.addTag("duration", "2000"); e2.addTag("traceId", "trace1"); e2.addTag("accountId", "user1"); Event e12 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Stock Check", "Out of Stock"); e12.addTag("duration", "2000"); e12.addTag("traceId", "trace4"); e12.addTag("accountId", "user2"); insert(e2, e12); // t0 + 2000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e3 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Credit Check", "Good"); e3.addTag("duration", "1500"); e3.addTag("traceId", "trace2"); e3.addTag("accountId", "user1"); Event e13 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Credit Check", "Good"); e13.addTag("duration", "1500"); e13.addTag("traceId", "trace5"); e13.addTag("accountId", "user2"); insert(e3, e13); // t0 + 3000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e4 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Stock Check", "Out of Stock"); e4.addTag("duration", "2000"); e4.addTag("traceId", "trace2"); e4.addTag("accountId", "user1"); Event e14 = new Event(TEST_TENANT, uuid(), now + 3, DATA_ID, "Stock Check", "Out of Stock"); e14.addTag("duration", "2000"); e14.addTag("traceId", "trace5"); e14.addTag("accountId", "user2"); insert(e4, e14); // t0 + 4000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e5 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Credit Check", "Exceptionally Good"); e5.addTag("duration", "1500"); e5.addTag("traceId", "trace3"); e5.addTag("accountId", "user1"); Event e15 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Credit Check", "Exceptionally Good"); e15.addTag("duration", "1500"); e15.addTag("traceId", "trace6"); e15.addTag("accountId", "user2"); insert(e5, e15); // t0 + 5000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event e6 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Stock Check", "Available"); e6.addTag("duration", "2000"); e6.addTag("traceId", "trace3"); e6.addTag("accountId", "user1"); Event e16 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "Stock Check", "Available"); e16.addTag("duration", "2000"); e16.addTag("traceId", "trace6"); e16.addTag("accountId", "user2"); insert(e6, e16); stopSession(); Assert.assertEquals(2, results.size()); Assert.assertTrue(results.contains("trace1")); Assert.assertTrue(results.contains("trace4")); } public static class TaggedEvent { private final String id; private final long ctime; private final Map<String, String> tags = new HashMap<>(); public TaggedEvent( String id , long ctime) { this.id = id; this.ctime = ctime; } public Map<String, String> getTags() { return tags; } public TaggedEvent addTag(String key, String value) { tags.put( key, value ); return this; } public long getCtime() { return ctime; } @Override public String toString() { return "TaggedEvent{" + "id='" + id + '\'' + ", ctime=" + ctime + '}'; } } }