/* * 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 static org.hawkular.alerts.api.model.trigger.Mode.FIRING; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.drools.core.time.impl.PseudoClockScheduler; import org.hawkular.alerts.api.json.JsonUtil; import org.hawkular.alerts.api.model.condition.ExternalCondition; import org.hawkular.alerts.api.model.event.Event; import org.hawkular.alerts.api.model.trigger.FullTrigger; import org.hawkular.alerts.api.model.trigger.Trigger; 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 ExpressionTest { public static final String TEST_TENANT = "28026b36-8fe4-4332-84c8-524e173a68bf"; public static final String DATA_ID = "data-id"; public static final String ALERTER_ID = "EventsAggregation"; KieBaseConfiguration kieBaseConfiguration; KieBase kieBase; KieSessionConfiguration kieSessionConf; KieSession kieSession; List<Event> 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", new CepEngine() { @Override public void sendResult(Event event) { results.add(event); } @Override public void updateConditions(String expiration, Collection<FullTrigger> activeTriggers) { } @Override public void processEvents(TreeSet<Event> events) { } @Override public void stop() { } }); } private void stopSession() { kieSession.dispose(); } private void insert(Object... objs) { for (Object o : objs) { kieSession.insert(o); } kieSession.fireAllRules(); } @Test public void marketingScenarioDsl() { Trigger trigger = new Trigger(TEST_TENANT, "marketing-scenario","Marketing Scenario"); String expression = "event:groupBy(context.accountId):window(time,10s):having(count > 2)"; ExternalCondition condition = new ExternalCondition(trigger.getId(), FIRING, DATA_ID, ALERTER_ID, expression); Expression exp = new Expression(Arrays.asList(new FullTrigger(trigger, null, Arrays.asList(condition)))); String drl = exp.getDrl(); System.out.println(drl); 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()); results.stream().forEach(e -> { Assert.assertEquals("user1", e.getContext().get("accountId")); System.out.println("Event: "); System.out.println(e); System.out.println("Source events:"); extractEvents(e).stream().forEach(extracted -> { System.out.println(extracted); }); System.out.println("\n"); }); } @Test public void fraudScenarioDsl() { Trigger trigger = new Trigger(TEST_TENANT, "fraud-scenario", "Fraud Scenario"); String expression = "event:groupBy(tags.accountId):window(time,10s):having(count > 1, count.tags.location > 1)"; ExternalCondition condition = new ExternalCondition(trigger.getId(), FIRING, DATA_ID, ALERTER_ID, expression); Expression exp = new Expression(Arrays.asList(new FullTrigger(trigger, null, Arrays.asList(condition)))); String drl = exp.getDrl(); System.out.println(drl); 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()); results.stream().forEach(e -> { String accountId = e.getContext().get("accountId"); Assert.assertTrue(accountId.equals("user1") || accountId.equals("user2")); System.out.println("Event: "); System.out.println(e); System.out.println("Source events:"); extractEvents(e).stream().forEach(extracted -> { System.out.println(extracted); }); System.out.println("\n"); }); } @Test public void customerRetentionScenarioDsl() { Trigger trigger = new Trigger(TEST_TENANT, "customer-retention-scenario", "Customer Retention Scenario"); String expression = "event:groupBy(tags.traceId):" + "filter((category == \"Credit Check\" && text == \"Exceptionally Good\") || " + "(category == \"Stock Check\" && text == \"Out of Stock\")):" + "having(count > 1, count.tags.accountId == 1)"; ExternalCondition condition = new ExternalCondition(trigger.getId(), FIRING, DATA_ID, ALERTER_ID, expression); Expression exp = new Expression(Arrays.asList(new FullTrigger(trigger, null, Arrays.asList(condition)))); String drl = exp.getDrl(); System.out.println(drl); 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()); results.stream().forEach(e -> { String traceId = e.getContext().get("traceId"); Assert.assertTrue(traceId.equals("trace1") || traceId.equals("trace4")); System.out.println("Event: "); System.out.println(e); System.out.println("Source events:"); extractEvents(e).stream().forEach(extracted -> { System.out.println(extracted); }); System.out.println("\n"); }); } @Test public void combinedScenarios() { List<FullTrigger> activeTriggers = new ArrayList<>(); // Marketing Trigger trigger = new Trigger(TEST_TENANT, "marketing-scenario","Marketing Scenario"); String expression = "event:groupBy(tags.accountId):window(time,10s):having(count > 2)"; ExternalCondition condition = new ExternalCondition(trigger.getId(), FIRING, DATA_ID, ALERTER_ID, expression); activeTriggers.add(new FullTrigger(trigger, null, Arrays.asList(condition))); // Fraud trigger = new Trigger(TEST_TENANT, "fraud-scenario", "Fraud Scenario"); expression = "event:groupBy(tags.accountId):window(time,10s):having(count > 1, count.tags.location > 1)"; condition = new ExternalCondition(trigger.getId(), FIRING, DATA_ID, ALERTER_ID, expression); activeTriggers.add(new FullTrigger(trigger, null, Arrays.asList(condition))); // Customer retention trigger = new Trigger(TEST_TENANT, "customer-retention-scenario", "Customer Retention Scenario"); expression = "event:groupBy(tags.traceId):" + "filter((category == \"Credit Check\" && text == \"Exceptionally Good\") || " + "(category == \"Stock Check\" && text == \"Out of Stock\")):" + "having(count > 1, count.tags.accountId == 1)"; condition = new ExternalCondition(trigger.getId(), FIRING, DATA_ID, ALERTER_ID, expression); activeTriggers.add(new FullTrigger(trigger, null, Arrays.asList(condition))); Expression exp = new Expression(activeTriggers); String drl = exp.getDrl(); System.out.println(drl); startSession(drl); // Init t0 clock.setStartupTime(1); long now = clock.getCurrentTime(); // User1 buys 5 times in < 10 seconds Event a1 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A1 - Buy Book"); a1.addContext("duration", "1000"); a1.addContext("accountId", "user1"); // User2 buys 3 times > 10 seconds Event a6 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A6 - Buy Book"); a6.addContext("duration", "1000"); a6.addContext("accountId", "user2"); // User1 buys 5 times in < 10 seconds from different locations Event b1 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B1 - Buy Book"); b1.addTag("duration", "1000"); b1.addTag("accountId", "user1"); b1.addTag("location", "ip1"); // User2 buys 3 times > 10 seconds from single location Event b6 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B6 - Buy Book"); b6.addTag("duration", "1000"); b6.addTag("accountId", "user2"); b6.addTag("location", "ip3"); // User3 buys 5 times in < 10 seconds from single location Event b11 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B11 - Buy Book"); b11.addTag("duration", "1000"); b11.addTag("accountId", "user3"); b11.addTag("location", "ip10"); // User1 buys 5 times in < 10 seconds from different locations Event e1 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E1 - 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", "E6 - 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", "E11 - Buy Book"); e11.addTag("duration", "1000"); e11.addTag("accountId", "user3"); e11.addTag("location", "ip10"); insert(a1, a6, b1, b6, b11, e1, e6, e11); // t0 + 1000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event a2 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A2 - Buy Music"); a2.addContext("duration", "2000"); a2.addContext("accountId", "user1"); Event b2 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B2 - Buy Music"); b2.addTag("duration", "2000"); b2.addTag("accountId", "user1"); b2.addTag("location", "ip1"); Event b12 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B12 - Buy Music"); b12.addTag("duration", "2000"); b12.addTag("accountId", "user3"); b12.addTag("location", "ip10"); Event e2 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E2 - 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", "E12 - Buy Music"); e12.addTag("duration", "2000"); e12.addTag("accountId", "user3"); e12.addTag("location", "ip10"); insert(a2, b2, b12, e2, e12); // t0 + 2000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event a3 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A3 - Buy Groceries"); a3.addContext("duration", "1500"); a3.addContext("accountId", "user1"); Event b3 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B3 - Buy Groceries"); b3.addTag("duration", "1500"); b3.addTag("accountId", "user1"); b3.addTag("location", "ip1"); Event b13 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B13 - Buy Groceries"); b13.addTag("duration", "1500"); b13.addTag("accountId", "user3"); b13.addTag("location", "ip10"); Event e3 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E3 - 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", "E13 - Buy Groceries"); e13.addTag("duration", "1500"); e13.addTag("accountId", "user3"); e13.addTag("location", "ip10"); insert(a3, b3, b13, e3, e13); // t0 + 3000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event a4 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A4 - Buy VideoGames"); a4.addContext("duration", "3000"); a4.addContext("accountId", "user1"); Event b4 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B4 - Buy VideoGames"); b4.addTag("duration", "3000"); b4.addTag("accountId", "user1"); b4.addTag("location", "ip2"); Event b14 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B14 - Buy VideoGames"); b14.addTag("duration", "3000"); b14.addTag("accountId", "user3"); b14.addTag("location", "ip10"); Event e4 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E4 - 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", "E14 - Buy VideoGames"); e14.addTag("duration", "3000"); e14.addTag("accountId", "user3"); e14.addTag("location", "ip10"); insert(a4, b4, b14, e4, e14); // t0 + 4000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event a5 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A5 - Buy VideoGames"); a5.addContext("duration", "3000"); a5.addContext("accountId", "user1"); Event b5 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B5 - Buy VideoGames"); b5.addTag("duration", "3000"); b5.addTag("accountId", "user1"); b5.addTag("location", "ip1"); Event b15 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B15 - Buy VideoGames"); b15.addTag("duration", "3000"); b15.addTag("accountId", "user3"); b15.addTag("location", "ip10"); Event e5 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E5 - 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", "E15 - Buy VideoGames"); e15.addTag("duration", "3000"); e15.addTag("accountId", "user3"); e15.addTag("location", "ip10"); insert(a5, b5, b15, e5, e15); // t0 + 5000 now = clock.advanceTime(1000, TimeUnit.MILLISECONDS); Event a7 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A7 - Buy Music"); a7.addContext("duration", "2000"); a7.addContext("accountId", "user2"); insert(a7); // t0 + 11000 now = clock.advanceTime(6000, TimeUnit.MILLISECONDS); Event a8 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "A8 - Buy Groceries"); a8.addContext("duration", "1500"); a8.addContext("accountId", "user2"); insert(a8); // t0 + 15000 now = clock.advanceTime(4000, TimeUnit.MILLISECONDS); Event b7 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B7 - Buy Music"); b7.addTag("duration", "2000"); b7.addTag("accountId", "user2"); b7.addTag("location", "ip4"); Event e7 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E7 - Buy Music"); e7.addTag("duration", "2000"); e7.addTag("accountId", "user2"); e7.addTag("location", "ip4"); insert(b7, e7); // t0 + 20000 now = clock.advanceTime(5000, TimeUnit.MILLISECONDS); Event b8 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "B8 - Buy Groceries"); b8.addTag("duration", "1500"); b8.addTag("accountId", "user2"); b8.addTag("location", "ip5"); Event e8 = new Event(TEST_TENANT, uuid(), now, DATA_ID, "TraceCompletion", "E8 - Buy Groceries"); e8.addTag("duration", "1500"); e8.addTag("accountId", "user2"); e8.addTag("location", "ip5"); insert(b8, e8); stopSession(); Assert.assertEquals(15, results.size()); results.stream().forEach(e -> { System.out.println("Event: "); System.out.println(e); System.out.println("Source events:"); extractEvents(e).stream().forEach(extracted -> { System.out.println(extracted); }); System.out.println("\n"); }); } public static List<Event> extractEvents(Event e) { List<Event> events = new ArrayList<>(); JsonUtil.fromJson(e.getContext().get("events"), ArrayList.class).stream() .forEach(o -> { events.add(JsonUtil.fromJson(JsonUtil.toJson(o), Event.class)); }); return events; } }