From 01a3f56aa7bc398d91dec127457579a97f5479c5 Mon Sep 17 00:00:00 2001
From: Juan C Galvis <8420868+juancgalvis@users.noreply.github.com>
Date: Tue, 11 Aug 2026 15:02:31 -0500
Subject: [PATCH 1/2] fix: allow programmatic features
---
.../EnableReactiveCommonsDynamic.java | 50 ++++
.../config/ReactiveCommonsDomainFeatures.java | 19 ++
.../config/ReactiveCommonsDynamicConfig.java | 269 +++++++++++++++++
.../config/ReactiveCommonsFeatures.java | 52 ++++
.../ReactiveCommonsDynamicConfigTest.java | 273 ++++++++++++++++++
5 files changed, 663 insertions(+)
create mode 100644 starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableReactiveCommonsDynamic.java
create mode 100644 starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDomainFeatures.java
create mode 100644 starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java
create mode 100644 starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsFeatures.java
create mode 100644 starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfigTest.java
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableReactiveCommonsDynamic.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableReactiveCommonsDynamic.java
new file mode 100644
index 00000000..5b5e521a
--- /dev/null
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/impl/config/annotations/EnableReactiveCommonsDynamic.java
@@ -0,0 +1,50 @@
+package org.reactivecommons.async.impl.config.annotations;
+
+import org.reactivecommons.async.starter.config.ReactiveCommonsDynamicConfig;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Enables runtime-configurable reactive-commons features driven by a
+ * {@link org.reactivecommons.async.starter.config.ReactiveCommonsFeatures} bean.
+ *
+ * Place this annotation on any {@code @Configuration} class (typically your
+ * {@code @SpringBootApplication}) and declare a
+ * {@link org.reactivecommons.async.starter.config.ReactiveCommonsFeatures} bean
+ * to control which features are activated at runtime:
+ *
+ *
{@code
+ * @EnableReactiveCommonsDynamic
+ * @SpringBootApplication
+ * public class MyApplication { ... }
+ *
+ * @Bean
+ * public ReactiveCommonsFeatures reactiveCommonsFeatures() {
+ * boolean needsEvents = someService.needsEventListening();
+ * return ReactiveCommonsFeatures.builder()
+ * .listenEvents(needsEvents)
+ * .sendEvents(true)
+ * .listenCommands(true)
+ * .sendCommands(true)
+ * .build();
+ * }
+ * }
+ *
+ * This is a dynamic alternative to combining the static annotations
+ * ({@code @EnableEventListeners}, {@code @EnableCommandListeners},
+ * {@code @EnableDomainEventBus}, etc.). Both approaches coexist — no changes
+ * are needed to existing code that uses the static annotations.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+@Documented
+@Import(ReactiveCommonsDynamicConfig.class)
+@Configuration
+public @interface EnableReactiveCommonsDynamic {
+}
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDomainFeatures.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDomainFeatures.java
new file mode 100644
index 00000000..ec88cbf1
--- /dev/null
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDomainFeatures.java
@@ -0,0 +1,19 @@
+package org.reactivecommons.async.starter.config;
+
+import java.util.HashMap;
+
+
+public class ReactiveCommonsDomainFeatures extends HashMap {
+
+ public ReactiveCommonsDomainFeatures() {
+ super();
+ }
+
+ public ReactiveCommonsFeatures ofDomain(String key) {
+ return this.get(key);
+ }
+
+ public ReactiveCommonsFeatures withDomain(String key) {
+ return this.computeIfAbsent(key, k -> new ReactiveCommonsFeatures());
+ }
+}
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java
new file mode 100644
index 00000000..f82b19c7
--- /dev/null
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java
@@ -0,0 +1,269 @@
+package org.reactivecommons.async.starter.config;
+
+import lombok.extern.log4j.Log4j2;
+import org.reactivecommons.api.domain.DomainEventBus;
+import org.reactivecommons.async.api.DirectAsyncGateway;
+import org.reactivecommons.async.starter.senders.GenericDirectAsyncGateway;
+import org.reactivecommons.async.starter.senders.GenericDomainEventBus;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Dynamic alternative to the static {@code @Enable*} annotations.
+ *
+ * All beans in this configuration are only created when a {@link ReactiveCommonsFeatures} bean
+ * is present in the application context. Each feature is then conditionally activated based
+ * on the flags set in that bean.
+ *
+ * Use {@code @EnableReactiveCommonsDynamic} (or {@code @Import(ReactiveCommonsDynamicConfig.class)})
+ * together with a {@code @Bean} of type {@link ReactiveCommonsFeatures} in your application.
+ */
+@Log4j2
+@Configuration
+@Import({ReactiveCommonsConfig.class, ReactiveCommonsListenersConfig.class})
+public class ReactiveCommonsDynamicConfig {
+
+ // -------------------------------------------------------------------------
+ // Listener activation — side-effect beans that start broker listeners
+ // when the corresponding feature flag is true.
+ // -------------------------------------------------------------------------
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public Object dynamicEventListenerActivator(ConnectionManager manager,
+ DomainHandlers handlers,
+ ReactiveCommonsDomainFeatures features) {
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isListenEvents()) {
+ log.info("ReactiveCommons: activating event listeners for domain '{}'", domain);
+ provider.listenDomainEvents(handlers.get(domain));
+ }
+ });
+ return new Object();
+ }
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public Object dynamicNotificationListenerActivator(ConnectionManager manager,
+ DomainHandlers handlers,
+ ReactiveCommonsDomainFeatures features) {
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isListenNotificationEvents()) {
+ log.info("ReactiveCommons: activating notification event listeners for domain '{}'", domain);
+ provider.listenNotificationEvents(handlers.get(domain));
+ }
+ });
+ return new Object();
+ }
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public Object dynamicCommandListenerActivator(ConnectionManager manager,
+ DomainHandlers handlers,
+ ReactiveCommonsDomainFeatures features) {
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isListenCommands()) {
+ log.info("ReactiveCommons: activating command listeners for domain '{}'", domain);
+ provider.listenCommands(handlers.get(domain));
+ }
+ });
+ return new Object();
+ }
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public Object dynamicQueryListenerActivator(ConnectionManager manager,
+ DomainHandlers handlers,
+ ReactiveCommonsDomainFeatures features) {
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isListenQueries()) {
+ log.info("ReactiveCommons: activating query listeners for domain '{}'", domain);
+ provider.listenQueries(handlers.get(domain));
+ }
+ });
+ return new Object();
+ }
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public Object dynamicQueueListenerActivator(ConnectionManager manager,
+ DomainHandlers handlers,
+ ReactiveCommonsDomainFeatures features) {
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isListenQueues()) {
+ log.info("ReactiveCommons: activating queue listeners for domain '{}'", domain);
+ provider.listenQueues(handlers.get(domain));
+ }
+ });
+ return new Object();
+ }
+
+ // -------------------------------------------------------------------------
+ // Sender beans — always created when ReactiveCommonsFeatures is present.
+ // If the corresponding flag is false, calls return a Mono.error to signal
+ // misconfiguration clearly at use-time rather than at startup.
+ // -------------------------------------------------------------------------
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public DomainEventBus dynamicDomainEventBus(ConnectionManager manager,
+ ReactiveCommonsDomainFeatures features) {
+ ConcurrentMap buses = new ConcurrentHashMap<>();
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isSendEvents()) {
+ log.info("ReactiveCommons: activating DomainEventBus for domain '{}'", domain);
+ buses.put(domain, provider.getDomainBus());
+ }
+ });
+ if (buses.isEmpty()) {
+ return new DisabledDomainEventBus();
+ }
+ return new GenericDomainEventBus(buses);
+ }
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public DirectAsyncGateway dynamicDirectAsyncGateway(ConnectionManager manager,
+ ReactiveCommonsDomainFeatures features) {
+ ConcurrentMap gateways = new ConcurrentHashMap<>();
+ manager.forDomain((domain, provider) -> {
+ if (features.ofDomain(domain).isSendCommands()) {
+ log.info("ReactiveCommons: activating DirectAsyncGateway for domain '{}'", domain);
+ gateways.put(domain, provider.getDirectAsyncGateway());
+ }
+ });
+ if (gateways.isEmpty()) {
+ return new DisabledDirectAsyncGateway();
+ }
+ return new GenericDirectAsyncGateway(gateways);
+ }
+
+ // -------------------------------------------------------------------------
+ // Disabled no-op implementations used when a sender feature flag is false.
+ // These prevent startup failures caused by missing bean definitions while
+ // surfacing a clear error at the point where the disabled feature is used.
+ // -------------------------------------------------------------------------
+
+ private static final String SEND_EVENTS_DISABLED =
+ "sendEvents feature is disabled in ReactiveCommonsFeatures. " +
+ "Set sendEvents=true to publish domain events.";
+
+ private static final String SEND_COMMANDS_DISABLED =
+ "sendCommands feature is disabled in ReactiveCommonsFeatures. " +
+ "Set sendCommands=true to send commands or queries.";
+
+ private static class DisabledDomainEventBus implements DomainEventBus {
+ @Override
+ public org.reactivestreams.Publisher emit(org.reactivecommons.api.domain.DomainEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(String domain,
+ org.reactivecommons.api.domain.DomainEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(io.cloudevents.CloudEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(String domain, io.cloudevents.CloudEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(org.reactivecommons.api.domain.RawMessage event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(String domain,
+ org.reactivecommons.api.domain.RawMessage event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+ }
+
+ private static class DisabledDirectAsyncGateway implements DirectAsyncGateway {
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
+ long delayMillis) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
+ String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
+ long delayMillis, String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis,
+ String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(org.reactivecommons.async.api.AsyncQuery query, String targetName,
+ Class type) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(org.reactivecommons.async.api.AsyncQuery query, String targetName,
+ Class type, String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(io.cloudevents.CloudEvent query,
+ String targetName, Class type) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(io.cloudevents.CloudEvent query,
+ String targetName, Class type,
+ String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono reply(T response, org.reactivecommons.async.api.From from) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+ }
+}
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsFeatures.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsFeatures.java
new file mode 100644
index 00000000..c2c5f1c4
--- /dev/null
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsFeatures.java
@@ -0,0 +1,52 @@
+package org.reactivecommons.async.starter.config;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ReactiveCommonsFeatures {
+
+ /**
+ * Equivalent to {@code @EnableEventListeners}.
+ * Start listening to domain events from the broker.
+ */
+ private boolean listenEvents;
+
+ /**
+ * Equivalent to {@code @EnableNotificationListener}.
+ * Start listening to notification events from the broker.
+ */
+ private boolean listenNotificationEvents;
+
+ /**
+ * Equivalent to {@code @EnableCommandListeners}.
+ * Start listening to commands from the broker.
+ */
+ private boolean listenCommands;
+
+ /**
+ * Equivalent to {@code @EnableQueryListeners}.
+ * Start listening to queries from the broker.
+ */
+ private boolean listenQueries;
+
+ /**
+ * Equivalent to {@code @EnableQueueListeners}.
+ * Start listening to queues from the broker.
+ */
+ private boolean listenQueues;
+
+ /**
+ * Equivalent to {@code @EnableDomainEventBus}.
+ * Exposes a {@link org.reactivecommons.api.domain.DomainEventBus} bean for publishing events.
+ */
+ private boolean sendEvents;
+
+ /**
+ * Equivalent to {@code @EnableDirectAsyncGateway}.
+ * Exposes a {@link org.reactivecommons.async.api.DirectAsyncGateway} bean for sending
+ * commands and request-reply queries.
+ */
+ private boolean sendCommands;
+}
diff --git a/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfigTest.java b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfigTest.java
new file mode 100644
index 00000000..a3da605f
--- /dev/null
+++ b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfigTest.java
@@ -0,0 +1,273 @@
+package org.reactivecommons.async.starter.config;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.reactivecommons.api.domain.DomainEventBus;
+import org.reactivecommons.async.api.DirectAsyncGateway;
+import org.reactivecommons.async.commons.HandlerResolver;
+import org.reactivecommons.async.starter.broker.BrokerProvider;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN;
+import static reactor.test.StepVerifier.create;
+
+@ExtendWith(MockitoExtension.class)
+class ReactiveCommonsDynamicConfigTest {
+
+ @Mock
+ private BrokerProvider> provider;
+ @Mock
+ private HandlerResolver resolver;
+ @Mock
+ private DomainEventBus domainEventBus;
+ @Mock
+ private DirectAsyncGateway directAsyncGateway;
+
+ private ConnectionManager manager;
+ private DomainHandlers handlers;
+ private ReactiveCommonsDynamicConfig config;
+
+ @BeforeEach
+ void setUp() {
+ config = new ReactiveCommonsDynamicConfig();
+ manager = new ConnectionManager();
+ manager.addDomain(DEFAULT_DOMAIN, provider);
+ handlers = new DomainHandlers();
+ handlers.add(DEFAULT_DOMAIN, resolver);
+ }
+
+ // -------------------------------------------------------------------------
+ // Helper: build a ReactiveCommonsDomainFeatures for DEFAULT_DOMAIN
+ // -------------------------------------------------------------------------
+
+ private ReactiveCommonsDomainFeatures domainFeatures(boolean listenEvents,
+ boolean listenNotifications,
+ boolean listenCommands,
+ boolean listenQueries,
+ boolean sendEvents,
+ boolean sendCommands) {
+ ReactiveCommonsDomainFeatures features = new ReactiveCommonsDomainFeatures();
+ ReactiveCommonsFeatures f = features.withDomain(DEFAULT_DOMAIN);
+ f.setListenEvents(listenEvents);
+ f.setListenNotificationEvents(listenNotifications);
+ f.setListenCommands(listenCommands);
+ f.setListenQueries(listenQueries);
+ f.setSendEvents(sendEvents);
+ f.setSendCommands(sendCommands);
+ return features;
+ }
+
+ private ReactiveCommonsDomainFeatures allFalse() {
+ return domainFeatures(false, false, false, false, false, false);
+ }
+
+ // -------------------------------------------------------------------------
+ // Listener activation tests
+ // -------------------------------------------------------------------------
+
+ @Test
+ void shouldActivateEventListenersWhenFlagIsTrue() {
+ ReactiveCommonsDomainFeatures features = domainFeatures(true, false, false, false, false, false);
+ config.dynamicEventListenerActivator(manager, handlers, features);
+ verify(provider).listenDomainEvents(resolver);
+ }
+
+ @Test
+ void shouldNotActivateEventListenersWhenFlagIsFalse() {
+ config.dynamicEventListenerActivator(manager, handlers, allFalse());
+ verify(provider, never()).listenDomainEvents(resolver);
+ }
+
+ @Test
+ void shouldActivateNotificationListenersWhenFlagIsTrue() {
+ ReactiveCommonsDomainFeatures features = domainFeatures(false, true, false, false, false, false);
+ config.dynamicNotificationListenerActivator(manager, handlers, features);
+ verify(provider).listenNotificationEvents(resolver);
+ }
+
+ @Test
+ void shouldNotActivateNotificationListenersWhenFlagIsFalse() {
+ config.dynamicNotificationListenerActivator(manager, handlers, allFalse());
+ verify(provider, never()).listenNotificationEvents(resolver);
+ }
+
+ @Test
+ void shouldActivateCommandListenersWhenFlagIsTrue() {
+ ReactiveCommonsDomainFeatures features = domainFeatures(false, false, true, false, false, false);
+ config.dynamicCommandListenerActivator(manager, handlers, features);
+ verify(provider).listenCommands(resolver);
+ }
+
+ @Test
+ void shouldNotActivateCommandListenersWhenFlagIsFalse() {
+ config.dynamicCommandListenerActivator(manager, handlers, allFalse());
+ verify(provider, never()).listenCommands(resolver);
+ }
+
+ @Test
+ void shouldActivateQueryListenersWhenFlagIsTrue() {
+ ReactiveCommonsDomainFeatures features = domainFeatures(false, false, false, true, false, false);
+ config.dynamicQueryListenerActivator(manager, handlers, features);
+ verify(provider).listenQueries(resolver);
+ }
+
+ @Test
+ void shouldNotActivateQueryListenersWhenFlagIsFalse() {
+ config.dynamicQueryListenerActivator(manager, handlers, allFalse());
+ verify(provider, never()).listenQueries(resolver);
+ }
+
+ @Test
+ void shouldActivateAllListenersWhenAllFlagsAreTrue() {
+ ReactiveCommonsDomainFeatures features = domainFeatures(true, true, true, true, false, false);
+
+ config.dynamicEventListenerActivator(manager, handlers, features);
+ config.dynamicNotificationListenerActivator(manager, handlers, features);
+ config.dynamicCommandListenerActivator(manager, handlers, features);
+ config.dynamicQueryListenerActivator(manager, handlers, features);
+
+ verify(provider).listenDomainEvents(resolver);
+ verify(provider).listenNotificationEvents(resolver);
+ verify(provider).listenCommands(resolver);
+ verify(provider).listenQueries(resolver);
+ }
+
+ @Test
+ void shouldActivateNoListenersWhenAllFlagsAreFalse() {
+ ReactiveCommonsDomainFeatures features = allFalse();
+
+ config.dynamicEventListenerActivator(manager, handlers, features);
+ config.dynamicNotificationListenerActivator(manager, handlers, features);
+ config.dynamicCommandListenerActivator(manager, handlers, features);
+ config.dynamicQueryListenerActivator(manager, handlers, features);
+
+ verify(provider, never()).listenDomainEvents(resolver);
+ verify(provider, never()).listenNotificationEvents(resolver);
+ verify(provider, never()).listenCommands(resolver);
+ verify(provider, never()).listenQueries(resolver);
+ }
+
+ @Test
+ void shouldActivateListenersOnMultipleDomainsWhenFlagIsTrue() {
+ manager.addDomain("other-domain", provider);
+ handlers.add("other-domain", resolver);
+
+ ReactiveCommonsDomainFeatures features = new ReactiveCommonsDomainFeatures();
+ features.withDomain(DEFAULT_DOMAIN).setListenEvents(true);
+ features.withDomain("other-domain").setListenEvents(true);
+
+ config.dynamicEventListenerActivator(manager, handlers, features);
+
+ verify(provider, times(2)).listenDomainEvents(resolver);
+ }
+
+ @Test
+ void shouldOnlyActivateListenerForConfiguredDomainWhenOtherDomainFlagIsFalse() {
+ manager.addDomain("other-domain", provider);
+ handlers.add("other-domain", resolver);
+
+ ReactiveCommonsDomainFeatures features = new ReactiveCommonsDomainFeatures();
+ features.withDomain(DEFAULT_DOMAIN).setListenEvents(true);
+ features.withDomain("other-domain"); // listenEvents=false by default
+
+ config.dynamicEventListenerActivator(manager, handlers, features);
+
+ verify(provider, times(1)).listenDomainEvents(resolver);
+ }
+
+ // -------------------------------------------------------------------------
+ // Sender bean tests
+ // -------------------------------------------------------------------------
+
+ @Test
+ void shouldCreateRealDomainEventBusWhenSendEventsIsTrue() {
+ when(provider.getDomainBus()).thenReturn(domainEventBus);
+ ReactiveCommonsDomainFeatures features = domainFeatures(false, false, false, false, true, false);
+
+ DomainEventBus bus = config.dynamicDomainEventBus(manager, features);
+
+ assertThat(bus).isNotNull();
+ verify(provider).getDomainBus();
+ }
+
+ @Test
+ void shouldReturnDisabledDomainEventBusWhenSendEventsIsFalse() {
+ ReactiveCommonsDomainFeatures features = allFalse();
+
+ DomainEventBus bus = config.dynamicDomainEventBus(manager, features);
+
+ assertThat(bus).isNotNull();
+ verify(provider, never()).getDomainBus();
+ }
+
+ @Test
+ void disabledDomainEventBusShouldEmitErrorOnUse() {
+ ReactiveCommonsDomainFeatures features = allFalse();
+ DomainEventBus bus = config.dynamicDomainEventBus(manager, features);
+
+ create(bus.emit(new org.reactivecommons.api.domain.DomainEvent<>("test", "1", "data")))
+ .expectErrorSatisfies(ex -> assertThat(ex)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("sendEvents"))
+ .verify();
+ }
+
+ @Test
+ void shouldCreateRealDirectAsyncGatewayWhenSendCommandsIsTrue() {
+ when(provider.getDirectAsyncGateway()).thenReturn(directAsyncGateway);
+ ReactiveCommonsDomainFeatures features = domainFeatures(false, false, false, false, false, true);
+
+ DirectAsyncGateway gateway = config.dynamicDirectAsyncGateway(manager, features);
+
+ assertThat(gateway).isNotNull();
+ verify(provider).getDirectAsyncGateway();
+ }
+
+ @Test
+ void shouldCreateDirectAsyncGatewayEvenWhenSendCommandsIsFalse() {
+ // The gateway is always registered in the map regardless of sendCommands flag.
+ // Conditional logging only; the actual put is unconditional.
+ ReactiveCommonsDomainFeatures features = allFalse();
+
+ DirectAsyncGateway gateway = config.dynamicDirectAsyncGateway(manager, features);
+
+ assertThat(gateway).isNotNull();
+ }
+
+ // -------------------------------------------------------------------------
+ // Default flags for a new ReactiveCommonsFeatures instance
+ // -------------------------------------------------------------------------
+
+ @Test
+ void newReactiveCommonsFeaturesShouldHaveAllFlagsFalse() {
+ ReactiveCommonsFeatures features = new ReactiveCommonsFeatures();
+ assertThat(features.isListenEvents()).isFalse();
+ assertThat(features.isListenNotificationEvents()).isFalse();
+ assertThat(features.isListenCommands()).isFalse();
+ assertThat(features.isListenQueries()).isFalse();
+ assertThat(features.isSendEvents()).isFalse();
+ assertThat(features.isSendCommands()).isFalse();
+ }
+
+ @Test
+ void withDomainShouldReturnSameInstanceOnSubsequentCalls() {
+ ReactiveCommonsDomainFeatures features = new ReactiveCommonsDomainFeatures();
+ ReactiveCommonsFeatures first = features.withDomain(DEFAULT_DOMAIN);
+ ReactiveCommonsFeatures second = features.withDomain(DEFAULT_DOMAIN);
+ assertThat(first).isSameAs(second);
+ }
+
+ @Test
+ void ofDomainShouldReturnConfiguredFeatures() {
+ ReactiveCommonsDomainFeatures features = new ReactiveCommonsDomainFeatures();
+ features.withDomain(DEFAULT_DOMAIN).setListenEvents(true);
+ assertThat(features.ofDomain(DEFAULT_DOMAIN).isListenEvents()).isTrue();
+ }
+}
From 78917a3c633bbbb3190adaaab4905cf1463ce57c Mon Sep 17 00:00:00 2001
From: Juan C Galvis <8420868+juancgalvis@users.noreply.github.com>
Date: Tue, 11 Aug 2026 15:21:34 -0500
Subject: [PATCH 2/2] fix: allow programmatic features
---
.../config/ReactiveCommonsDynamicConfig.java | 126 +---------------
.../disabled/DisabledDirectAsyncGateway.java | 85 +++++++++++
.../disabled/DisabledDomainEventBus.java | 42 ++++++
.../DisabledDirectAsyncGatewayTest.java | 141 ++++++++++++++++++
.../disabled/DisabledDomainEventBusTest.java | 81 ++++++++++
5 files changed, 351 insertions(+), 124 deletions(-)
create mode 100644 starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGateway.java
create mode 100644 starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBus.java
create mode 100644 starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGatewayTest.java
create mode 100644 starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBusTest.java
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java
index f82b19c7..aa4c8248 100644
--- a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/ReactiveCommonsDynamicConfig.java
@@ -3,12 +3,13 @@
import lombok.extern.log4j.Log4j2;
import org.reactivecommons.api.domain.DomainEventBus;
import org.reactivecommons.async.api.DirectAsyncGateway;
+import org.reactivecommons.async.starter.config.disabled.DisabledDirectAsyncGateway;
+import org.reactivecommons.async.starter.config.disabled.DisabledDomainEventBus;
import org.reactivecommons.async.starter.senders.GenericDirectAsyncGateway;
import org.reactivecommons.async.starter.senders.GenericDomainEventBus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
-import reactor.core.publisher.Mono;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -143,127 +144,4 @@ public DirectAsyncGateway dynamicDirectAsyncGateway(ConnectionManager manager,
return new GenericDirectAsyncGateway(gateways);
}
- // -------------------------------------------------------------------------
- // Disabled no-op implementations used when a sender feature flag is false.
- // These prevent startup failures caused by missing bean definitions while
- // surfacing a clear error at the point where the disabled feature is used.
- // -------------------------------------------------------------------------
-
- private static final String SEND_EVENTS_DISABLED =
- "sendEvents feature is disabled in ReactiveCommonsFeatures. " +
- "Set sendEvents=true to publish domain events.";
-
- private static final String SEND_COMMANDS_DISABLED =
- "sendCommands feature is disabled in ReactiveCommonsFeatures. " +
- "Set sendCommands=true to send commands or queries.";
-
- private static class DisabledDomainEventBus implements DomainEventBus {
- @Override
- public org.reactivestreams.Publisher emit(org.reactivecommons.api.domain.DomainEvent event) {
- return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
- }
-
- @Override
- public org.reactivestreams.Publisher emit(String domain,
- org.reactivecommons.api.domain.DomainEvent event) {
- return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
- }
-
- @Override
- public org.reactivestreams.Publisher emit(io.cloudevents.CloudEvent event) {
- return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
- }
-
- @Override
- public org.reactivestreams.Publisher emit(String domain, io.cloudevents.CloudEvent event) {
- return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
- }
-
- @Override
- public org.reactivestreams.Publisher emit(org.reactivecommons.api.domain.RawMessage event) {
- return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
- }
-
- @Override
- public org.reactivestreams.Publisher emit(String domain,
- org.reactivecommons.api.domain.RawMessage event) {
- return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
- }
- }
-
- private static class DisabledDirectAsyncGateway implements DirectAsyncGateway {
- @Override
- public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
- long delayMillis) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
- String domain) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
- long delayMillis, String domain) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, String domain) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis,
- String domain) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono requestReply(org.reactivecommons.async.api.AsyncQuery query, String targetName,
- Class type) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono requestReply(org.reactivecommons.async.api.AsyncQuery query, String targetName,
- Class type, String domain) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono requestReply(io.cloudevents.CloudEvent query,
- String targetName, Class type) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono requestReply(io.cloudevents.CloudEvent query,
- String targetName, Class type,
- String domain) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
-
- @Override
- public Mono reply(T response, org.reactivecommons.async.api.From from) {
- return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
- }
- }
}
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGateway.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGateway.java
new file mode 100644
index 00000000..23c62a7b
--- /dev/null
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGateway.java
@@ -0,0 +1,85 @@
+package org.reactivecommons.async.starter.config.disabled;
+
+import org.reactivecommons.async.api.DirectAsyncGateway;
+import reactor.core.publisher.Mono;
+
+public class DisabledDirectAsyncGateway implements DirectAsyncGateway {
+
+ private static final String SEND_COMMANDS_DISABLED =
+ "sendCommands feature is disabled in ReactiveCommonsFeatures. " +
+ "Set sendCommands=true to send commands or queries.";
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
+ long delayMillis) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
+ String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(org.reactivecommons.api.domain.Command command, String targetName,
+ long delayMillis, String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono sendCommand(io.cloudevents.CloudEvent command, String targetName, long delayMillis,
+ String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(org.reactivecommons.async.api.AsyncQuery query, String targetName,
+ Class type) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(org.reactivecommons.async.api.AsyncQuery query, String targetName,
+ Class type, String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(io.cloudevents.CloudEvent query,
+ String targetName, Class type) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono requestReply(io.cloudevents.CloudEvent query,
+ String targetName, Class type,
+ String domain) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+
+ @Override
+ public Mono reply(T response, org.reactivecommons.async.api.From from) {
+ return Mono.error(new IllegalStateException(SEND_COMMANDS_DISABLED));
+ }
+}
\ No newline at end of file
diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBus.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBus.java
new file mode 100644
index 00000000..0886033a
--- /dev/null
+++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBus.java
@@ -0,0 +1,42 @@
+package org.reactivecommons.async.starter.config.disabled;
+
+import org.reactivecommons.api.domain.DomainEventBus;
+import reactor.core.publisher.Mono;
+
+public class DisabledDomainEventBus implements DomainEventBus {
+ private static final String SEND_EVENTS_DISABLED =
+ "sendEvents feature is disabled in ReactiveCommonsFeatures. " +
+ "Set sendEvents=true to publish domain events.";
+
+ @Override
+ public org.reactivestreams.Publisher emit(org.reactivecommons.api.domain.DomainEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(String domain,
+ org.reactivecommons.api.domain.DomainEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(io.cloudevents.CloudEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(String domain, io.cloudevents.CloudEvent event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(org.reactivecommons.api.domain.RawMessage event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+
+ @Override
+ public org.reactivestreams.Publisher emit(String domain,
+ org.reactivecommons.api.domain.RawMessage event) {
+ return Mono.error(new IllegalStateException(SEND_EVENTS_DISABLED));
+ }
+}
\ No newline at end of file
diff --git a/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGatewayTest.java b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGatewayTest.java
new file mode 100644
index 00000000..e0d1a1be
--- /dev/null
+++ b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDirectAsyncGatewayTest.java
@@ -0,0 +1,141 @@
+package org.reactivecommons.async.starter.config.disabled;
+
+import io.cloudevents.CloudEvent;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.reactivecommons.api.domain.Command;
+import org.reactivecommons.async.api.AsyncQuery;
+import org.reactivecommons.async.api.From;
+import reactor.test.StepVerifier;
+
+@ExtendWith(MockitoExtension.class)
+class DisabledDirectAsyncGatewayTest {
+
+ private DisabledDirectAsyncGateway gateway;
+ private static final String ERROR_MESSAGE = "sendCommands feature is disabled in ReactiveCommonsFeatures. " +
+ "Set sendCommands=true to send commands or queries.";
+
+ @Mock
+ private CloudEvent cloudEvent;
+
+ @Mock
+ private Command command;
+
+ @Mock
+ private AsyncQuery query;
+
+ @Mock
+ private From from;
+
+ @BeforeEach
+ void setUp() {
+ gateway = new DisabledDirectAsyncGateway();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCommandWithoutDelay() {
+ StepVerifier.create(gateway.sendCommand(command, "targetName"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCommandWithDelay() {
+ StepVerifier.create(gateway.sendCommand(command, "targetName", 1000L))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCommandWithDomain() {
+ StepVerifier.create(gateway.sendCommand(command, "targetName", "domain"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCommandWithDelayAndDomain() {
+ StepVerifier.create(gateway.sendCommand(command, "targetName", 1000L, "domain"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCloudEventCommand() {
+ StepVerifier.create(gateway.sendCommand(cloudEvent, "targetName"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCloudEventCommandWithDelay() {
+ StepVerifier.create(gateway.sendCommand(cloudEvent, "targetName", 1000L))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCloudEventCommandWithDomain() {
+ StepVerifier.create(gateway.sendCommand(cloudEvent, "targetName", "domain"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenSendingCloudEventCommandWithDelayAndDomain() {
+ StepVerifier.create(gateway.sendCommand(cloudEvent, "targetName", 1000L, "domain"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenRequestingReplyWithQuery() {
+ StepVerifier.create(gateway.requestReply(query, "targetName", String.class))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenRequestingReplyWithQueryAndDomain() {
+ StepVerifier.create(gateway.requestReply(query, "targetName", String.class, "domain"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenRequestingReplyWithCloudEvent() {
+ StepVerifier.create(gateway.requestReply(cloudEvent, "targetName", CloudEvent.class))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenRequestingReplyWithCloudEventAndDomain() {
+ StepVerifier.create(gateway.requestReply(cloudEvent, "targetName", CloudEvent.class, "domain"))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenReplyingToQuery() {
+ StepVerifier.create(gateway.reply("response", from))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+}
diff --git a/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBusTest.java b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBusTest.java
new file mode 100644
index 00000000..60a3e96e
--- /dev/null
+++ b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/config/disabled/DisabledDomainEventBusTest.java
@@ -0,0 +1,81 @@
+package org.reactivecommons.async.starter.config.disabled;
+
+import io.cloudevents.CloudEvent;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.reactivecommons.api.domain.DomainEvent;
+import org.reactivecommons.api.domain.RawMessage;
+import reactor.test.StepVerifier;
+
+@ExtendWith(MockitoExtension.class)
+class DisabledDomainEventBusTest {
+
+ private DisabledDomainEventBus eventBus;
+ private static final String ERROR_MESSAGE = "sendEvents feature is disabled in ReactiveCommonsFeatures. " +
+ "Set sendEvents=true to publish domain events.";
+
+ @Mock
+ private CloudEvent cloudEvent;
+
+ @Mock
+ private DomainEvent domainEvent;
+
+ @Mock
+ private RawMessage rawMessage;
+
+ @BeforeEach
+ void setUp() {
+ eventBus = new DisabledDomainEventBus();
+ }
+
+ @Test
+ void shouldThrowErrorWhenEmittingDomainEvent() {
+ StepVerifier.create(eventBus.emit(domainEvent))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenEmittingDomainEventWithDomain() {
+ StepVerifier.create(eventBus.emit("domain", domainEvent))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenEmittingCloudEvent() {
+ StepVerifier.create(eventBus.emit(cloudEvent))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenEmittingCloudEventWithDomain() {
+ StepVerifier.create(eventBus.emit("domain", cloudEvent))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenEmittingRawMessage() {
+ StepVerifier.create(eventBus.emit(rawMessage))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+
+ @Test
+ void shouldThrowErrorWhenEmittingRawMessageWithDomain() {
+ StepVerifier.create(eventBus.emit("domain", rawMessage))
+ .expectErrorMatches(error -> error instanceof IllegalStateException &&
+ error.getMessage().equals(ERROR_MESSAGE))
+ .verify();
+ }
+}