-
Notifications
You must be signed in to change notification settings - Fork 0
test(cdc): reproduce connector registration gap on live develop #315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
6b37735
test(cdc): reproduce connector identity authority gap
seonghobae 7b900d0
merge: refresh registry test onto live develop
seonghobae d86af10
fix(cdc): enforce registry identity invariants
seonghobae d4fc6f9
chore: refresh CDC registry branch to live develop
seonghobae 3686d38
test(cdc): reproduce Spring source registry wiring defect
seonghobae 06f7b3b
merge: refresh CDC registry reproduction on protected develop
seonghobae 770a4cb
test(cdc): bind Spring registry constructor selection
seonghobae 88d7b36
fix(cdc): bind Spring source registry constructor
seonghobae ba68aa0
Merge branch 'develop' into repro/cdc-registry-live-feaea5d
github-actions[bot] 2427a8c
Merge branch 'develop' into repro/cdc-registry-live-feaea5d
opencode-agent[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
cdc-service/src/test/java/com/xtrmetl/cdc/spi/CdcRegistryIdentityTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| package com.xtrmetl.cdc.spi; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.beans.factory.ObjectProvider; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
|
|
||
| import java.lang.reflect.Constructor; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertSame; | ||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| /** | ||
| * Fail-first contract for CDC connector registration authority. | ||
| * | ||
| * <p>Connector identifiers select production implementations. Invalid registration must fail before | ||
| * registry mutation so bean order or plugin code cannot silently replace or remove that authority.</p> | ||
| */ | ||
| class CdcRegistryIdentityTest { | ||
|
|
||
| @Test | ||
| void duplicateSourceConnectorIdsFailClosedInsteadOfReplacingRegistration() { | ||
| CdcSourceConnector first = source("duplicate-source"); | ||
| CdcSourceConnector second = source("duplicate-source"); | ||
| CdcSourceRegistry registry = new CdcSourceRegistry(List.of(first)); | ||
|
|
||
| IllegalArgumentException failure = assertThrows( | ||
| IllegalArgumentException.class, | ||
| () -> registry.register(second) | ||
| ); | ||
|
|
||
| assertEquals("Duplicate CDC source connector id: duplicate-source", failure.getMessage()); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| assertSame(first, registry.find("duplicate-source").orElseThrow()); | ||
| } | ||
|
|
||
| @Test | ||
| void duplicateTargetConnectorIdsFailClosedInsteadOfReplacingRegistration() { | ||
| CdcTargetRegistry registry = new CdcTargetRegistry(); | ||
| CdcTargetConnector originalKafka = registry.find(KafkaCdcTargetConnector.ID).orElseThrow(); | ||
| CdcTargetConnector duplicateKafka = target(KafkaCdcTargetConnector.ID); | ||
|
|
||
| IllegalArgumentException failure = assertThrows( | ||
| IllegalArgumentException.class, | ||
| () -> registry.register(duplicateKafka) | ||
| ); | ||
|
|
||
| assertEquals("Duplicate CDC target connector id: kafka", failure.getMessage()); | ||
| assertSame(originalKafka, registry.find(KafkaCdcTargetConnector.ID).orElseThrow()); | ||
| } | ||
|
|
||
| @Test | ||
| void springDiscoveryConstructorIsExplicitlyAutowired() { | ||
| Constructor<?> discoveryConstructor = Arrays.stream(CdcSourceRegistry.class.getConstructors()) | ||
| .filter(constructor -> Arrays.equals( | ||
| constructor.getParameterTypes(), | ||
| new Class<?>[]{ObjectProvider.class} | ||
| )) | ||
| .findFirst() | ||
| .orElseThrow(); | ||
|
|
||
| assertTrue(discoveryConstructor.isAnnotationPresent(Autowired.class), | ||
| "Spring discovery constructor must be explicitly selected when other public constructors exist"); | ||
| } | ||
|
|
||
| @Test | ||
| void nullSourceConnectorFailsBeforeRegistryMutation() { | ||
| CdcSourceRegistry registry = new CdcSourceRegistry(); | ||
| IllegalArgumentException failure = assertThrows(IllegalArgumentException.class, () -> registry.register(null)); | ||
| assertEquals("CDC source connector must not be null", failure.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| void nullTargetConnectorFailsBeforeRegistryMutation() { | ||
| CdcTargetRegistry registry = new CdcTargetRegistry(); | ||
| IllegalArgumentException failure = assertThrows(IllegalArgumentException.class, () -> registry.register(null)); | ||
| assertEquals("CDC target connector must not be null", failure.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| void blankSourceConnectorIdFailsBeforeRegistryMutation() { | ||
| CdcSourceConnector blank = source(" "); | ||
| IllegalArgumentException failure = assertThrows( | ||
| IllegalArgumentException.class, | ||
| () -> new CdcSourceRegistry(List.of(blank)) | ||
| ); | ||
| assertEquals("CDC source connector id must not be blank", failure.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| void blankTargetConnectorIdFailsBeforeRegistryMutation() { | ||
| CdcTargetRegistry registry = new CdcTargetRegistry(); | ||
| CdcTargetConnector blank = target(""); | ||
| IllegalArgumentException failure = assertThrows(IllegalArgumentException.class, () -> registry.register(blank)); | ||
| assertEquals("CDC target connector id must not be blank", failure.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| void sourceConnectorCollectionCannotDeleteRegistrationAuthority() { | ||
| CdcSourceRegistry registry = new CdcSourceRegistry(List.of(source("immutable-source"))); | ||
| assertThrows(UnsupportedOperationException.class, () -> registry.all().clear()); | ||
| assertTrue(registry.find("immutable-source").isPresent()); | ||
| } | ||
|
|
||
| @Test | ||
| void targetConnectorCollectionCannotDeleteRegistrationAuthority() { | ||
| CdcTargetRegistry registry = new CdcTargetRegistry(); | ||
| assertThrows(UnsupportedOperationException.class, () -> registry.all().clear()); | ||
| assertTrue(registry.find(KafkaCdcTargetConnector.ID).isPresent()); | ||
| assertTrue(registry.find(JdbcReplicaCdcTargetConnector.ID).isPresent()); | ||
| } | ||
|
|
||
| private static CdcSourceConnector source(String id) { | ||
| CdcSourceConnector connector = mock(CdcSourceConnector.class); | ||
| when(connector.id()).thenReturn(id); | ||
| return connector; | ||
| } | ||
|
|
||
| private static CdcTargetConnector target(String id) { | ||
| CdcTargetConnector connector = mock(CdcTargetConnector.class); | ||
| when(connector.id()).thenReturn(id); | ||
| return connector; | ||
| } | ||
| } | ||
71 changes: 71 additions & 0 deletions
71
cdc-service/src/test/java/com/xtrmetl/cdc/spi/CdcSourceRegistrySpringWiringTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| package com.xtrmetl.cdc.spi; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.context.annotation.AnnotationConfigApplicationContext; | ||
|
|
||
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertSame; | ||
|
|
||
| /** | ||
| * Verifies that the Spring-managed source registry receives discovered connector beans. | ||
| * | ||
| * <p>This test reaches the actual Spring constructor-selection boundary instead of directly | ||
| * instantiating {@link CdcSourceRegistry}. It prevents a public no-argument constructor from | ||
| * silently bypassing the {@code ObjectProvider<CdcSourceConnector>} integration path.</p> | ||
| */ | ||
| class CdcSourceRegistrySpringWiringTest { | ||
|
|
||
| @Test | ||
| void springContextRegistersDiscoveredSourceConnectorBean() { | ||
| TestSourceConnector connector = new TestSourceConnector(); | ||
|
|
||
| try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { | ||
| context.registerBean(CdcSourceConnector.class, () -> connector); | ||
| context.register(CdcSourceRegistry.class); | ||
| context.refresh(); | ||
|
|
||
| CdcSourceRegistry registry = context.getBean(CdcSourceRegistry.class); | ||
|
|
||
| assertSame( | ||
| connector, | ||
| registry.find(connector.id()).orElseThrow(), | ||
| "Spring must construct the registry through its connector-provider constructor" | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| private static final class TestSourceConnector implements CdcSourceConnector { | ||
|
|
||
| @Override | ||
| public String id() { | ||
| return "test_source"; | ||
| } | ||
|
|
||
| @Override | ||
| public String displayName() { | ||
| return "Test source"; | ||
| } | ||
|
|
||
| @Override | ||
| public SourceCapabilities capabilities() { | ||
| return new SourceCapabilities("test", Set.of("test_database"), false); | ||
| } | ||
|
|
||
| @Override | ||
| public void validate(Map<String, String> config) { | ||
| // No configuration is required for this constructor-selection regression fixture. | ||
| } | ||
|
|
||
| @Override | ||
| public void start(Map<String, String> config) { | ||
| // No runtime capture is required for this constructor-selection regression fixture. | ||
| } | ||
|
|
||
| @Override | ||
| public void stop() { | ||
| // No runtime capture is started by this constructor-selection regression fixture. | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.