This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch unomi-3-dev
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/unomi-3-dev by this push:
new 379fc56a8 UNOMI-880 Increase max recursion depth for event processing
from 10 to 20, introduce detailed recursion tracking with ThreadLocal, and
improve diagnostics when limits are exceeded.
379fc56a8 is described below
commit 379fc56a883cc3787b40f0462903d3aaab42e746
Author: Serge Huber <[email protected]>
AuthorDate: Thu Jan 1 16:20:02 2026 +0100
UNOMI-880 Increase max recursion depth for event processing from 10 to 20,
introduce detailed recursion tracking with ThreadLocal, and improve diagnostics
when limits are exceeded.
---
.../unomi/itests/graphql/GraphQLEventIT.java | 11 +-
.../actions/EvaluateProfileAgeAction.java | 38 ++++++-
.../actions/EvaluateProfileSegmentsAction.java | 33 +++++-
.../resources/OSGI-INF/blueprint/blueprint.xml | 4 +-
.../services/impl/events/EventServiceImpl.java | 121 ++++++++++++++++++---
.../services/impl/events/EventServiceImplTest.java | 32 +++---
6 files changed, 195 insertions(+), 44 deletions(-)
diff --git
a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
index f5eada5c9..3e9a04f40 100644
--- a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLEventIT.java
@@ -22,6 +22,7 @@ import org.apache.unomi.api.Profile;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -71,7 +72,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
final Profile profile2 = new Profile("profile-2");
persistenceService.save(profile2);
createEvent("event-3", profile2);
-
+
// Wait for events to be properly indexed before querying via GraphQL
refreshPersistence(Event.class, Profile.class);
// Verify events are queryable via persistence service first
@@ -90,7 +91,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
CloseableHttpResponse resp =
post("graphql/event/find-events.json");
if (resp != null && resp.getEntity() != null) {
// Buffer entity to allow multiple reads
- org.apache.http.entity.BufferedHttpEntity
bufferedEntity =
+ org.apache.http.entity.BufferedHttpEntity
bufferedEntity =
new
org.apache.http.entity.BufferedHttpEntity(resp.getEntity());
resp.setEntity(bufferedEntity);
}
@@ -114,7 +115,7 @@ public class GraphQLEventIT extends BaseGraphQLIT {
}
},
DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
-
+
try {
Assert.assertNotNull("Response context should be available",
contextHolder[0]);
final ResponseContext context = contextHolder[0];
@@ -147,7 +148,9 @@ public class GraphQLEventIT extends BaseGraphQLIT {
}
private Event createEvent(final String eventID, final Profile profile)
throws InterruptedException {
- Event event = new Event(eventID, "profileUpdated", null, profile,
"test", profile, null, new Date());
+ // Use a test-specific event type instead of "profileUpdated" to avoid
triggering rules
+ // that match profileUpdated events and creating loops during
integration tests
+ Event event = new Event(eventID, "testProfileUpdated", null, profile,
"test", profile, null, new Date());
persistenceService.save(event);
return event;
}
diff --git
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileAgeAction.java
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileAgeAction.java
index 6f2b16208..34ab5c552 100644
---
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileAgeAction.java
+++
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileAgeAction.java
@@ -21,10 +21,12 @@ import org.apache.unomi.api.Event;
import org.apache.unomi.api.actions.Action;
import org.apache.unomi.api.actions.ActionExecutor;
import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.tracing.api.RequestTracer;
+import org.apache.unomi.tracing.api.TracerService;
import org.joda.time.DateTime;
import org.joda.time.Years;
-import org.apache.unomi.tracing.api.TracerService;
-import org.apache.unomi.tracing.api.RequestTracer;
+
import java.util.Map;
/**
@@ -32,8 +34,13 @@ import java.util.Map;
*/
public class EvaluateProfileAgeAction implements ActionExecutor {
+ private ProfileService profileService;
private TracerService tracerService;
+ public void setProfileService(ProfileService profileService) {
+ this.profileService = profileService;
+ }
+
public void setTracerService(TracerService tracerService) {
this.tracerService = tracerService;
}
@@ -43,15 +50,23 @@ public class EvaluateProfileAgeAction implements
ActionExecutor {
RequestTracer tracer = null;
if (tracerService != null && tracerService.isTracingEnabled()) {
tracer = tracerService.getCurrentTracer();
- tracer.startOperation("evaluate-age",
+ tracer.startOperation("evaluate-age",
"Evaluating profile age", action);
}
try {
+ if (event.getProfile() == null) {
+ if (tracer != null) {
+ tracer.endOperation(false, "No profile in event");
+ }
+ return EventService.NO_CHANGE;
+ }
+
boolean updated = false;
if (event.getProfile().getProperty("birthDate") != null) {
Integer y = Years.yearsBetween(new
DateTime(event.getProfile().getProperty("birthDate")), new
DateTime()).getYears();
- if (event.getProfile().getProperty("age") == null ||
event.getProfile().getProperty("age") != y) {
+ Integer currentAge = (Integer)
event.getProfile().getProperty("age");
+ if (currentAge == null || !currentAge.equals(y)) {
updated = true;
event.getProfile().setProperty("age", y);
if (tracer != null) {
@@ -67,8 +82,21 @@ public class EvaluateProfileAgeAction implements
ActionExecutor {
}
}
+ // If this action was triggered by a profileUpdated event, save
the profile
+ // but don't return PROFILE_UPDATED to prevent loops
+ if (updated && "profileUpdated".equals(event.getEventType()) &&
profileService != null) {
+ profileService.save(event.getProfile());
+ if (tracer != null) {
+ tracer.trace("Profile saved after age evaluation
(preventing loop)", Map.of(
+ "newAge", event.getProfile().getProperty("age")
+ ));
+ tracer.endOperation(true, "Profile age updated and saved
(loop prevented)");
+ }
+ return EventService.NO_CHANGE;
+ }
+
if (tracer != null) {
- tracer.endOperation(updated,
+ tracer.endOperation(updated,
updated ? "Profile age updated" : "No changes needed");
}
return updated ? EventService.PROFILE_UPDATED :
EventService.NO_CHANGE;
diff --git
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileSegmentsAction.java
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileSegmentsAction.java
index b45034b1a..b4f66ab19 100644
---
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileSegmentsAction.java
+++
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/EvaluateProfileSegmentsAction.java
@@ -22,9 +22,10 @@ import org.apache.unomi.api.actions.Action;
import org.apache.unomi.api.actions.ActionExecutor;
import org.apache.unomi.api.segments.SegmentsAndScores;
import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.services.ProfileService;
import org.apache.unomi.api.services.SegmentService;
-import org.apache.unomi.tracing.api.TracerService;
import org.apache.unomi.tracing.api.RequestTracer;
+import org.apache.unomi.tracing.api.TracerService;
import java.util.Map;
import java.util.Set;
@@ -32,6 +33,7 @@ import java.util.Set;
public class EvaluateProfileSegmentsAction implements ActionExecutor {
private SegmentService segmentService;
+ private ProfileService profileService;
private TracerService tracerService;
public SegmentService getSegmentService() {
@@ -42,6 +44,10 @@ public class EvaluateProfileSegmentsAction implements
ActionExecutor {
this.segmentService = segmentService;
}
+ public void setProfileService(ProfileService profileService) {
+ this.profileService = profileService;
+ }
+
public void setTracerService(TracerService tracerService) {
this.tracerService = tracerService;
}
@@ -51,11 +57,18 @@ public class EvaluateProfileSegmentsAction implements
ActionExecutor {
RequestTracer tracer = null;
if (tracerService != null && tracerService.isTracingEnabled()) {
tracer = tracerService.getCurrentTracer();
- tracer.startOperation("evaluate-segments",
+ tracer.startOperation("evaluate-segments",
"Evaluating profile segments", action);
}
try {
+ if (event.getProfile() == null) {
+ if (tracer != null) {
+ tracer.endOperation(false, "No profile in event");
+ }
+ return EventService.NO_CHANGE;
+ }
+
if (event.getProfile().isAnonymousProfile()) {
if (tracer != null) {
tracer.endOperation(false, "Skipping anonymous profile");
@@ -76,13 +89,27 @@ public class EvaluateProfileSegmentsAction implements
ActionExecutor {
updated = true;
}
+ // If this action was triggered by a profileUpdated event, save
the profile
+ // but don't return PROFILE_UPDATED to prevent loops
+ if (updated && "profileUpdated".equals(event.getEventType()) &&
profileService != null) {
+ profileService.save(event.getProfile());
+ if (tracer != null) {
+ tracer.trace("Profile saved after segment evaluation
(preventing loop)", Map.of(
+ "segmentsCount", segments.size(),
+ "scoresCount", scores.size()
+ ));
+ tracer.endOperation(true, "Profile segments updated and
saved (loop prevented)");
+ }
+ return EventService.NO_CHANGE;
+ }
+
if (tracer != null) {
tracer.trace("Segments evaluated", Map.of(
"segmentsCount", segments.size(),
"scoresCount", scores.size(),
"isUpdated", updated
));
- tracer.endOperation(updated,
+ tracer.endOperation(updated,
updated ? "Profile segments updated" : "No changes
needed");
}
return updated ? EventService.PROFILE_UPDATED :
EventService.NO_CHANGE;
diff --git
a/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index c8cf127ee..2b10af39a 100644
--- a/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -45,7 +45,7 @@
<reference id="persistenceService"
interface="org.apache.unomi.persistence.spi.PersistenceService"/>
<reference id="profileService"
interface="org.apache.unomi.api.services.ProfileService"/>
<reference id="privacyService"
interface="org.apache.unomi.api.services.PrivacyService"/>
- <reference id="schedulerService"
interface="org.apache.unomi.api.services.SchedulerService"/>
+ <reference id="schedulerService"
interface="org.apache.unomi.api.services.SchedulerService" timeout="2000"/>
<reference id="segmentService"
interface="org.apache.unomi.api.services.SegmentService"/>
<reference id="eventService"
interface="org.apache.unomi.api.services.EventService"/>
<reference id="configSharingService"
interface="org.apache.unomi.api.services.ConfigSharingService"/>
@@ -199,6 +199,7 @@
</service-properties>
<bean
class="org.apache.unomi.plugins.baseplugin.actions.EvaluateProfileSegmentsAction">
<property name="segmentService" ref="segmentService"/>
+ <property name="profileService" ref="profileService"/>
<property name="tracerService" ref="tracerService"/>
</bean>
</service>
@@ -208,6 +209,7 @@
<entry key="actionExecutorId" value="evaluateProfileAge"/>
</service-properties>
<bean
class="org.apache.unomi.plugins.baseplugin.actions.EvaluateProfileAgeAction">
+ <property name="profileService" ref="profileService"/>
<property name="tracerService" ref="tracerService"/>
</bean>
</service>
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
index 00cf72037..9e7120814 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
@@ -19,18 +19,18 @@ package org.apache.unomi.services.impl.events;
import org.apache.commons.lang3.StringUtils;
import org.apache.unomi.api.*;
-import org.apache.unomi.services.common.security.IPValidationUtils;
import org.apache.unomi.api.actions.ActionPostExecutor;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.query.Query;
import org.apache.unomi.api.services.DefinitionsService;
import org.apache.unomi.api.services.EventListenerService;
import org.apache.unomi.api.services.EventService;
-import org.apache.unomi.api.services.ResolverService;
+import org.apache.unomi.api.services.TypeResolutionService;
import org.apache.unomi.api.tenants.Tenant;
import org.apache.unomi.api.tenants.TenantService;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
+import org.apache.unomi.services.common.security.IPValidationUtils;
import org.apache.unomi.tracing.api.RequestTracer;
import org.apache.unomi.tracing.api.TracerService;
import org.osgi.framework.BundleContext;
@@ -43,14 +43,59 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class EventServiceImpl implements EventService {
private static final Logger LOGGER =
LoggerFactory.getLogger(EventServiceImpl.class);
- private static final int MAX_RECURSION_DEPTH = 10;
+ private static final int MAX_RECURSION_DEPTH = 20;
+
+ /**
+ * Simple data class to hold event information for recursion tracking.
+ * Focuses on data relevant to rule condition matching: event type, scope,
and key properties.
+ */
+ private static class EventInfo {
+ final String eventType;
+ final String scope;
+ final String propertyKeys;
+
+ EventInfo(Event event) {
+ this.eventType = event.getEventType();
+ this.scope = event.getScope();
+
+ // Collect property keys that might be used in conditions (limit
to first 5 to avoid noise)
+ Map<String, Object> properties = event.getProperties();
+ if (properties != null && !properties.isEmpty()) {
+ List<String> keys = new ArrayList<>(properties.keySet());
+ int maxKeys = Math.min(5, keys.size());
+ this.propertyKeys = keys.subList(0, maxKeys).toString();
+ } else {
+ this.propertyKeys = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new
StringBuilder("Event{type=").append(eventType);
+ if (scope != null) {
+ sb.append(", scope=").append(scope);
+ }
+ if (propertyKeys != null) {
+ sb.append(", properties=").append(propertyKeys);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * ThreadLocal to track event stack for event processing.
+ * This ensures the full event chain is tracked consistently even when
send() is called directly
+ * from actions or other services, preventing infinite recursion and
providing detailed
+ * diagnostics when recursion limits are reached.
+ */
+ private static final ThreadLocal<List<EventInfo>> EVENT_STACK =
ThreadLocal.withInitial(ArrayList::new);
private List<EventListenerService> eventListeners = new
CopyOnWriteArrayList<EventListenerService>();
private PersistenceService persistenceService;
private DefinitionsService definitionsService;
- private ResolverService resolverService;
private TenantService tenantService;
@@ -78,8 +123,12 @@ public class EventServiceImpl implements EventService {
this.definitionsService = definitionsService;
}
- public void setResolverService(ResolverService resolverService) {
- this.resolverService = resolverService;
+ /**
+ * Helper method to get TypeResolutionService from DefinitionsService.
+ * Returns null if DefinitionsService is not available or doesn't have
TypeResolutionService.
+ */
+ private TypeResolutionService getTypeResolutionService() {
+ return definitionsService != null ?
definitionsService.getTypeResolutionService() : null;
}
public void setTenantService(TenantService tenantService) {
@@ -135,17 +184,51 @@ public class EventServiceImpl implements EventService {
public int send(Event event) {
RequestTracer tracer = tracerService.getCurrentTracer();
tracer.trace("Sending event: " + event.getEventType(),
event.getItemId());
- return send(event, 0);
- }
- private int send(Event event, int depth) {
- RequestTracer tracer = tracerService.getCurrentTracer();
- if (depth > MAX_RECURSION_DEPTH) {
+ // Get current event stack from ThreadLocal
+ List<EventInfo> eventStack = EVENT_STACK.get();
+
+ // Check depth before processing (matches original: if (depth >
MAX_RECURSION_DEPTH))
+ // Original allowed depths 0-10 (11 calls), blocking at depth 11
+ if (eventStack.size() > MAX_RECURSION_DEPTH) {
+ EventInfo currentEventInfo = new EventInfo(event);
tracer.trace("Max recursion depth reached for event: " +
event.getEventType(), event.getItemId());
- LOGGER.warn("Max recursion depth reached");
+
+ // Build detailed error message with full event chain
+ StringBuilder errorMsg = new StringBuilder("Max recursion depth
reached (depth: ").append(eventStack.size() + 1)
+ .append(", max: ").append(MAX_RECURSION_DEPTH + 1)
+ .append("). Current event: ").append(currentEventInfo);
+
+ if (!eventStack.isEmpty()) {
+ errorMsg.append("\nEvent chain (oldest first):");
+ for (int i = 0; i < eventStack.size(); i++) {
+ errorMsg.append("\n [").append(i + 1).append("]
").append(eventStack.get(i));
+ }
+ errorMsg.append("\n [").append(eventStack.size() +
1).append("] ").append(currentEventInfo).append(" <-- BLOCKED");
+ }
+
+ LOGGER.warn(errorMsg.toString());
return NO_CHANGE;
}
+ // Add current event to stack
+ EventInfo currentEventInfo = new EventInfo(event);
+ eventStack.add(currentEventInfo);
+
+ try {
+ return sendInternal(event);
+ } finally {
+ // Remove current event from stack and cleanup ThreadLocal if empty
+ eventStack.remove(eventStack.size() - 1);
+ if (eventStack.isEmpty()) {
+ EVENT_STACK.remove();
+ }
+ }
+ }
+
+ private int sendInternal(Event event) {
+ RequestTracer tracer = tracerService.getCurrentTracer();
+
boolean saveSucceeded = true;
if (event.isPersistent()) {
try {
@@ -187,7 +270,8 @@ public class EventServiceImpl implements EventService {
Event profileUpdated = new Event("profileUpdated",
session, event.getProfile(), event.getScope(), event.getSource(),
event.getProfile(), event.getTimeStamp());
profileUpdated.setPersistent(false);
profileUpdated.getAttributes().putAll(event.getAttributes());
- changes |= send(profileUpdated, depth + 1);
+ // Depth is automatically tracked via ThreadLocal, no need
to pass parameter
+ changes |= send(profileUpdated);
if (session != null && session.getProfileId() != null) {
changes |= SESSION_UPDATED;
session.setProfile(event.getProfile());
@@ -279,7 +363,12 @@ public class EventServiceImpl implements EventService {
@Override
public PartialList<Event> searchEvents(Condition condition, int offset,
int size) {
- resolverService.resolveConditionType(condition, "event search");
+ TypeResolutionService typeResolutionService =
getTypeResolutionService();
+ if (typeResolutionService != null) {
+ typeResolutionService.resolveConditionType(condition, "event
search");
+ }
+ // Note: Effective condition resolution happens in the query builder
dispatcher or condition evaluator dispatcher
+ // For in-memory persistence, the condition evaluator dispatcher will
resolve the effective condition
return persistenceService.query(condition, "timeStamp", Event.class,
offset, size);
}
@@ -322,13 +411,15 @@ public class EventServiceImpl implements EventService {
if (query.getScrollIdentifier() != null) {
return persistenceService.continueScrollQuery(Event.class,
query.getScrollIdentifier(), query.getScrollTimeValidity());
}
- if (query.getCondition() != null &&
definitionsService.resolveConditionType(query.getCondition())) {
+ if (query.getCondition() != null) {
+
definitionsService.getConditionValidationService().validate(query.getCondition());
if (StringUtils.isNotBlank(query.getText())) {
return persistenceService.queryFullText(query.getText(),
query.getCondition(), query.getSortby(), Event.class, query.getOffset(),
query.getLimit());
} else {
return persistenceService.query(query.getCondition(),
query.getSortby(), Event.class, query.getOffset(), query.getLimit(),
query.getScrollTimeValidity());
}
} else {
+ // No condition - query without condition
if (StringUtils.isNotBlank(query.getText())) {
return persistenceService.queryFullText(query.getText(),
query.getSortby(), Event.class, query.getOffset(), query.getLimit());
} else {
diff --git
a/services/src/test/java/org/apache/unomi/services/impl/events/EventServiceImplTest.java
b/services/src/test/java/org/apache/unomi/services/impl/events/EventServiceImplTest.java
index 11c20c955..109d5ef97 100644
---
a/services/src/test/java/org/apache/unomi/services/impl/events/EventServiceImplTest.java
+++
b/services/src/test/java/org/apache/unomi/services/impl/events/EventServiceImplTest.java
@@ -20,22 +20,24 @@ import org.apache.unomi.api.*;
import org.apache.unomi.api.actions.ActionPostExecutor;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.query.Query;
-import org.apache.unomi.api.services.ConditionValidationService;
import org.apache.unomi.api.services.EventListenerService;
import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.services.SchedulerService;
import org.apache.unomi.api.tenants.Tenant;
import org.apache.unomi.persistence.spi.PersistenceService;
import
org.apache.unomi.persistence.spi.conditions.evaluator.ConditionEvaluatorDispatcher;
import org.apache.unomi.services.TestHelper;
+import org.apache.unomi.services.common.security.AuditServiceImpl;
import org.apache.unomi.services.common.security.ExecutionContextManagerImpl;
import org.apache.unomi.services.common.security.KarafSecurityService;
-import org.apache.unomi.services.impl.*;
+import org.apache.unomi.services.impl.InMemoryPersistenceServiceImpl;
+import org.apache.unomi.services.impl.TestConditionEvaluators;
+import org.apache.unomi.services.impl.TestTenantService;
import org.apache.unomi.services.impl.cache.MultiTypeCacheServiceImpl;
import org.apache.unomi.services.impl.definitions.DefinitionsServiceImpl;
-import org.apache.unomi.services.common.security.AuditServiceImpl;
import org.apache.unomi.tracing.api.TracerService;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -66,12 +68,11 @@ public class EventServiceImplTest {
@Mock
private BundleContext bundleContext;
- private org.apache.unomi.api.services.SchedulerService schedulerService;
+ private SchedulerService schedulerService;
@Mock
private EventListenerService eventListener;
@Mock
private ServiceReference<EventListenerService> eventListenerReference;
- private ConditionValidationService conditionValidationService;
private TracerService tracerService;
private static final String TENANT_1 = "tenant1";
@@ -80,11 +81,8 @@ public class EventServiceImplTest {
@BeforeEach
public void setUp() {
-
- tenantService = new TestTenantService();
- // Initialize ConditionValidationService using TestHelper
- this.conditionValidationService =
TestHelper.createConditionValidationService();
+ tenantService = new TestTenantService();
// Create tenants using TestHelper
TestHelper.setupCommonTestData(tenantService);
@@ -106,7 +104,9 @@ public class EventServiceImplTest {
schedulerService =
TestHelper.createSchedulerService("event-service-scheduler-node",
persistenceService, executionContextManager, bundleContext, null, -1, true,
true);
// Set up definitions service
- definitionsService =
TestHelper.createDefinitionService(persistenceService, bundleContext,
schedulerService, multiTypeCacheService, executionContextManager,
tenantService, conditionValidationService);
+ definitionsService =
TestHelper.createDefinitionService(persistenceService, bundleContext,
schedulerService, multiTypeCacheService, executionContextManager,
tenantService);
+ // Inject definitionsService into the dispatcher
+
TestHelper.injectDefinitionsServiceIntoDispatcher(conditionEvaluatorDispatcher,
definitionsService);
TestConditionEvaluators.getConditionTypes().forEach((key, value) ->
definitionsService.setConditionType(value));
@@ -123,7 +123,7 @@ public class EventServiceImplTest {
@AfterEach
public void tearDown() throws Exception {
// Use the common tearDown method from TestHelper
- org.apache.unomi.services.TestHelper.tearDown(
+ TestHelper.tearDown(
schedulerService,
multiTypeCacheService,
persistenceService,
@@ -132,9 +132,9 @@ public class EventServiceImplTest {
);
// Clean up references using the helper method
- org.apache.unomi.services.TestHelper.cleanupReferences(
+ TestHelper.cleanupReferences(
tenantService, securityService, executionContextManager,
eventService,
- persistenceService, definitionsService, schedulerService,
conditionValidationService,
+ persistenceService, definitionsService, schedulerService,
multiTypeCacheService, auditService, bundleContext, eventListener,
eventListenerReference, tracerService
);
@@ -172,7 +172,7 @@ public class EventServiceImplTest {
// Verify
assertEquals(EventService.PROFILE_UPDATED, result &
EventService.PROFILE_UPDATED, "Profile update flag should be set after listener
triggers update");
- verify(eventListener, times(11)).onEvent(any(Event.class)); //
Original event + profileUpdated event recursive
+ verify(eventListener, times(21)).onEvent(any(Event.class)); //
Original event + profileUpdated event recursive (max depth 20)
}
@Test
@@ -206,7 +206,7 @@ public class EventServiceImplTest {
int result = eventService.send(event);
// Verify that after max recursion is reached, we get NO_CHANGE
- verify(eventListener, times(11)).onEvent(any(Event.class)); // 10 is
max recursion depth
+ verify(eventListener, times(21)).onEvent(any(Event.class)); // 20 is
max recursion depth
assertEquals(EventService.PROFILE_UPDATED |
EventService.SESSION_UPDATED, result, "Result flags should reflect last
recursion state");
}