diff --git a/cdc-service/src/main/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSource.java b/cdc-service/src/main/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSource.java
index dcc0e653..673a4578 100644
--- a/cdc-service/src/main/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSource.java
+++ b/cdc-service/src/main/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSource.java
@@ -11,8 +11,12 @@
/**
* Adapter for the live Debezium PostgreSQL capture path owned by {@link CdcService}.
*
- *
{@link #start(Map)} / {@link #stop()} delegate to the service when available so the SPI
- * is a real control surface for the supported source type.
+ * The live service reads connection and Debezium settings from deployment configuration. Per-call
+ * SPI configuration is intentionally unsupported: callers must pass an empty map so values such as
+ * credentials are never accepted and then silently ignored.
+ *
+ * {@link #start(Map)} and {@link #stop()} delegate to the service when available so the SPI is a
+ * real control surface for the supported source type.
*/
@Component
public final class PostgresDebeziumCdcSource implements CdcSourceConnector {
@@ -21,6 +25,11 @@ public final class PostgresDebeziumCdcSource implements CdcSourceConnector {
private final ObjectProvider cdcService;
+ /**
+ * Creates the PostgreSQL CDC adapter backed by the deployment-configured live service.
+ *
+ * @param cdcService provider for the live CDC service
+ */
public PostgresDebeziumCdcSource(ObjectProvider cdcService) {
this.cdcService = cdcService;
}
@@ -67,13 +76,34 @@ public SourceCapabilities capabilities() {
return new SourceCapabilities("debezium-embedded", Set.of("postgresql"), false);
}
+ /**
+ * Validates the per-call configuration accepted by this adapter.
+ *
+ * The live PostgreSQL capture service is configured by the deployment, not by this SPI call.
+ * An empty map is therefore the only valid value.
+ *
+ * @param config per-call settings; must be non-null and empty
+ * @throws IllegalArgumentException when {@code config} is null or contains any entry
+ */
@Override
public void validate(Map config) {
if (config == null) {
throw new IllegalArgumentException("config must not be null");
}
+ if (!config.isEmpty()) {
+ throw new IllegalArgumentException(
+ "postgres-debezium uses deployment-owned configuration; per-call config must be empty"
+ );
+ }
}
+ /**
+ * Starts the deployment-configured PostgreSQL CDC service.
+ *
+ * @param config per-call settings; must be non-null and empty
+ * @throws IllegalArgumentException when {@code config} is null or contains any entry
+ * @throws IllegalStateException when the live {@link CdcService} is unavailable
+ */
@Override
public void start(Map config) {
validate(config);
diff --git a/cdc-service/src/test/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSourceConfigurationTest.java b/cdc-service/src/test/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSourceConfigurationTest.java
new file mode 100644
index 00000000..8d97a210
--- /dev/null
+++ b/cdc-service/src/test/java/com/xtrmetl/cdc/spi/PostgresDebeziumCdcSourceConfigurationTest.java
@@ -0,0 +1,74 @@
+package com.xtrmetl.cdc.spi;
+
+import com.xtrmetl.cdc.service.CdcService;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.ObjectProvider;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Guards the PostgreSQL source SPI against silently ignored per-call configuration.
+ */
+class PostgresDebeziumCdcSourceConfigurationTest {
+
+ @Test
+ void rejectsCallerConfigurationBeforeStartingDeploymentConfiguredService() {
+ CdcService service = mock(CdcService.class);
+ @SuppressWarnings("unchecked")
+ ObjectProvider provider = mock(ObjectProvider.class);
+ when(provider.getIfAvailable()).thenReturn(service);
+ PostgresDebeziumCdcSource source = new PostgresDebeziumCdcSource(provider);
+ String rejectedSecret = "buyer-secret-password-8472";
+
+ IllegalArgumentException failure = assertThrows(
+ IllegalArgumentException.class,
+ () -> source.start(Map.of("database.password", rejectedSecret))
+ );
+
+ assertEquals(
+ "postgres-debezium uses deployment-owned configuration; per-call config must be empty",
+ failure.getMessage()
+ );
+ assertFalse(failure.getMessage().contains(rejectedSecret));
+ assertFalse(failure.getMessage().contains("database.password"));
+ verifyNoInteractions(service);
+ }
+
+ @Test
+ void rejectsNullConfigurationBeforeServiceLookup() {
+ CdcService service = mock(CdcService.class);
+ @SuppressWarnings("unchecked")
+ ObjectProvider provider = mock(ObjectProvider.class);
+ PostgresDebeziumCdcSource source = new PostgresDebeziumCdcSource(provider);
+
+ IllegalArgumentException failure = assertThrows(
+ IllegalArgumentException.class,
+ () -> source.start(null)
+ );
+
+ assertEquals("config must not be null", failure.getMessage());
+ verifyNoInteractions(provider, service);
+ }
+
+ @Test
+ void emptyConfigurationStartsDeploymentConfiguredService() {
+ CdcService service = mock(CdcService.class);
+ @SuppressWarnings("unchecked")
+ ObjectProvider provider = mock(ObjectProvider.class);
+ when(provider.getIfAvailable()).thenReturn(service);
+ PostgresDebeziumCdcSource source = new PostgresDebeziumCdcSource(provider);
+
+ source.start(Map.of());
+
+ verify(provider).getIfAvailable();
+ verify(service).start();
+ }
+}