This is an automated email from the ASF dual-hosted git repository.
zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 199c52d93df Add more test cases on LoadSingleTableExecutorTest (#38099)
199c52d93df is described below
commit 199c52d93df7f1d13b777e17885600494f67b671
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Feb 19 17:58:53 2026 +0800
Add more test cases on LoadSingleTableExecutorTest (#38099)
---
.codex/skills/gen-ut/SKILL.md | 1 +
.../update/LoadSingleTableExecutorTest.java | 208 +++++++++++++--------
2 files changed, 131 insertions(+), 78 deletions(-)
diff --git a/.codex/skills/gen-ut/SKILL.md b/.codex/skills/gen-ut/SKILL.md
index 656cadc9790..14fc4e6bbd1 100644
--- a/.codex/skills/gen-ut/SKILL.md
+++ b/.codex/skills/gen-ut/SKILL.md
@@ -144,6 +144,7 @@ Module resolution order:
- `R13`: test necessity trimming
- Trimming order `MUST` be fixed as "objective trimming -> exception
retention review".
- In objective trimming stage, `MUST` first remove coverage-equivalent tests
and re-verify coverage uniformly, then remove redundant mock/stub/assertion and
single-use local variables that do not affect branch selection/collaborator
interaction behavior (call count, parameters)/observable assertions; if
retention significantly improves readability, `MAY` keep and mark `Necessity
reason tag`.
+ - Local variable declarations in test code `MUST NOT` use `final`; this rule
applies only to local variables and does not change `R15-E` for
parameterized-test method parameters.
- Each retained item `MUST` carry a `KEEP:<id>:<reason>` tag and be recorded
in the delivery report; items without tags are treated as redundant.
- Each test method `MUST` provide unique value: cover a new branch/path, or
add assertion differences.
- If deleting a test method does not change line/branch coverage and has no
assertion differences, `MUST` delete it.
diff --git
a/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
b/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
index 96081eee489..83342d483e6 100644
---
a/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
+++
b/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
@@ -17,152 +17,204 @@
package org.apache.shardingsphere.single.distsql.handler.update;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
import
org.apache.shardingsphere.database.exception.core.exception.syntax.table.TableExistsException;
-import
org.apache.shardingsphere.distsql.handler.engine.update.DistSQLUpdateExecuteEngine;
-import org.apache.shardingsphere.infra.datanode.DataNode;
+import
org.apache.shardingsphere.distsql.handler.engine.update.rdl.rule.spi.database.DatabaseRuleDefinitionExecutor;
import
org.apache.shardingsphere.infra.exception.kernel.metadata.TableNotFoundException;
import
org.apache.shardingsphere.infra.exception.kernel.metadata.datanode.InvalidDataNodeFormatException;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.resource.storageunit.EmptyStorageUnitException;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.resource.storageunit.InvalidStorageUnitStatusException;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.resource.PhysicalDataSourceAggregator;
import
org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
-import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
-import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
-import
org.apache.shardingsphere.infra.rule.attribute.datasource.DataSourceMapperRuleAttribute;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
-import org.apache.shardingsphere.mode.manager.ContextManager;
-import
org.apache.shardingsphere.mode.persist.service.MetaDataManagerPersistService;
import org.apache.shardingsphere.single.config.SingleRuleConfiguration;
import org.apache.shardingsphere.single.datanode.SingleTableDataNodeLoader;
import org.apache.shardingsphere.single.distsql.segment.SingleTableSegment;
import
org.apache.shardingsphere.single.distsql.statement.rdl.LoadSingleTableStatement;
import org.apache.shardingsphere.single.rule.SingleRule;
-import org.apache.shardingsphere.single.util.SingleTableLoadUtils;
import org.apache.shardingsphere.test.infra.fixture.jdbc.MockedDataSource;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Answers;
import org.mockito.Mock;
+import org.mockito.MockedConstruction;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
-import java.sql.SQLException;
+import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.when;
@ExtendWith(AutoMockExtension.class)
-@StaticMockSettings({SingleTableDataNodeLoader.class,
SingleTableLoadUtils.class, PhysicalDataSourceAggregator.class})
+@StaticMockSettings({SingleTableDataNodeLoader.class,
PhysicalDataSourceAggregator.class})
@MockitoSettings(strictness = Strictness.LENIENT)
class LoadSingleTableExecutorTest {
+ private final DatabaseType databaseType =
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
+
+ private final LoadSingleTableExecutor executor = (LoadSingleTableExecutor)
TypedSPILoader.getService(DatabaseRuleDefinitionExecutor.class,
LoadSingleTableStatement.class);
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ShardingSphereDatabase database;
- @Mock
- private ShardingSphereSchema schema;
-
@BeforeEach
void setUp() {
when(database.getName()).thenReturn("foo_db");
-
when(database.getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
"FIXTURE"));
- when(database.getSchema("foo_db")).thenReturn(schema);
-
when(database.getRuleMetaData().getAttributes(DataSourceMapperRuleAttribute.class)).thenReturn(Collections.emptyList());
+ when(database.getProtocolType()).thenReturn(databaseType);
+ executor.setDatabase(database);
}
- private ContextManager mockContextManager(final SingleRule rule) {
- ContextManager result = mock(ContextManager.class, RETURNS_DEEP_STUBS);
- when(result.getDatabase("foo_db")).thenReturn(database);
- if (null == rule) {
- when(database.getRuleMetaData()).thenReturn(new
RuleMetaData(Collections.emptyList()));
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("assertCheckBeforeUpdateWithPreValidationFailureArguments")
+ void assertCheckBeforeUpdateWithPreValidationFailure(final String name,
final boolean schemaSupported, final SingleTableSegment tableSegment,
+ final boolean
tableExists, final Class<? extends RuntimeException> expectedException) {
+ prepareStorageUnits();
+ prepareSchema(tableExists, schemaSupported ? "foo_schema" : "foo_db");
+ LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Collections.singleton(tableSegment));
+ if (schemaSupported) {
+ try (MockedConstruction<DatabaseTypeRegistry> ignored =
mockSchemaSupportedDatabaseTypeRegistry()) {
+ assertThrows(expectedException, () ->
executor.checkBeforeUpdate(sqlStatement));
+ }
} else {
- when(rule.getAttributes()).thenReturn(new RuleAttributes());
- when(database.getRuleMetaData()).thenReturn(new
RuleMetaData(Collections.singleton(rule)));
+ assertThrows(expectedException, () ->
executor.checkBeforeUpdate(sqlStatement));
}
- return result;
}
@Test
- void assertExecuteUpdateWithInvalidTableNodeFormatWhenSchemaNotSupported()
{
- when(schema.containsTable("foo_tbl")).thenReturn(true);
-
when(database.getResourceMetaData().getNotExistedDataSources(any())).thenReturn(Collections.emptyList());
- LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Collections.singleton(new SingleTableSegment("foo_ds",
"foo_schema", "foo_tbl")));
- sqlStatement.buildAttributes();
- assertThrows(InvalidDataNodeFormatException.class, () -> new
DistSQLUpdateExecuteEngine(sqlStatement, "foo_db",
mockContextManager(mock(SingleRule.class)), null).executeUpdate());
+ void assertCheckBeforeUpdateWithEmptyStorageUnits() {
+ prepareSchema(false, "foo_db");
+
when(database.getResourceMetaData().getStorageUnits()).thenReturn(Collections.emptyMap());
+ assertThrows(EmptyStorageUnitException.class, () ->
executor.checkBeforeUpdate(new
LoadSingleTableStatement(Collections.singletonList(new
SingleTableSegment("foo_ds", "foo_tbl")))));
+ }
+
+ @ParameterizedTest(name = "{0}")
+
@MethodSource("assertCheckBeforeUpdateWithActualTableValidationFailureArguments")
+ void assertCheckBeforeUpdateWithActualTableValidationFailure(final String
name, final Map<String, DataSource> aggregatedDataSources,
+ final
Map<String, Collection<String>> schemaTableNames, final Class<? extends
RuntimeException> expectedException) {
+ prepareActualTableValidationScenario(aggregatedDataSources,
schemaTableNames);
+ assertThrows(expectedException, () -> executor.checkBeforeUpdate(new
LoadSingleTableStatement(Collections.singletonList(new
SingleTableSegment("foo_ds", "foo_tbl")))));
}
@Test
- void assertExecuteUpdateWithExistedLogicTables() {
- when(schema.containsTable("foo_tbl")).thenReturn(true);
-
when(database.getResourceMetaData().getNotExistedDataSources(any())).thenReturn(Collections.emptyList());
- LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Arrays.asList(
- new SingleTableSegment("*", "*"), new SingleTableSegment("*",
"*", "*"), new SingleTableSegment("foo_ds", "*"), new
SingleTableSegment("foo_ds", "foo_tbl")));
- sqlStatement.buildAttributes();
- assertThrows(TableExistsException.class, () -> new
DistSQLUpdateExecuteEngine(sqlStatement, "foo_db",
mockContextManager(mock(SingleRule.class)), null).executeUpdate());
+ void assertCheckBeforeUpdateWithSchemaSupportedDatabaseType() {
+
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new
MockedDataSource()), Collections.singletonMap("foo_schema",
Collections.singleton("foo_tbl")));
+ LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Arrays.asList(new SingleTableSegment("foo_ds",
"foo_schema", "foo_tbl"), new SingleTableSegment("*", "*")));
+ try (MockedConstruction<DatabaseTypeRegistry> ignored =
mockSchemaSupportedDatabaseTypeRegistry()) {
+ prepareSchema(false, "foo_schema");
+ assertDoesNotThrow(() -> executor.checkBeforeUpdate(sqlStatement));
+ }
}
@Test
- void assertExecuteUpdateWithNotExistedActualTables() {
-
when(database.getResourceMetaData().getNotExistedDataSources(any())).thenReturn(Collections.emptyList());
- StorageUnit storageUnit = mock(StorageUnit.class);
- when(storageUnit.getDataSource()).thenReturn(new MockedDataSource());
-
when(database.getResourceMetaData().getStorageUnits()).thenReturn(Collections.singletonMap("foo_ds",
storageUnit));
- when(PhysicalDataSourceAggregator.getAggregatedDataSources(any(),
any())).thenReturn(Collections.singletonMap("foo_ds", new MockedDataSource()));
- LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Collections.singleton(new SingleTableSegment("foo_ds",
"foo_tbl")));
- sqlStatement.buildAttributes();
- assertThrows(TableNotFoundException.class, () -> new
DistSQLUpdateExecuteEngine(sqlStatement, "foo_db",
mockContextManager(mock(SingleRule.class)), null).executeUpdate());
+ void assertCheckBeforeUpdateWithAllTablesPattern() {
+ prepareSchema(false, "foo_db");
+ assertDoesNotThrow(() -> executor.checkBeforeUpdate(new
LoadSingleTableStatement(Collections.singletonList(new SingleTableSegment("*",
"*")))));
}
@Test
- void assertExecuteUpdateWithSingleRule() throws SQLException {
- Collection<String> currentTables = new
LinkedList<>(Collections.singleton("foo_ds.foo_tbl"));
-
when(database.getResourceMetaData().getNotExistedDataSources(any())).thenReturn(Collections.emptyList());
- StorageUnit storageUnit = mock(StorageUnit.class);
- when(storageUnit.getDataSource()).thenReturn(new MockedDataSource());
-
when(database.getResourceMetaData().getStorageUnits()).thenReturn(Collections.singletonMap("foo_ds",
storageUnit));
- when(SingleTableDataNodeLoader.load(eq("foo_db"), any(),
any())).thenReturn(Collections.singletonMap("foo_tbl",
Collections.singleton(new DataNode("foo_ds.foo_tbl"))));
- when(SingleTableLoadUtils.convertToDataNodes(eq("foo_db"), any(),
any())).thenReturn(Collections.singleton(new DataNode("foo_ds.foo_tbl")));
- SingleRuleConfiguration currentConfig = new
SingleRuleConfiguration(currentTables, null);
- LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Collections.singleton(new SingleTableSegment("*",
"bar_tbl")));
- sqlStatement.buildAttributes();
- SingleRule rule = mock(SingleRule.class);
- when(rule.getConfiguration()).thenReturn(currentConfig);
- ContextManager contextManager = mockContextManager(rule);
- new DistSQLUpdateExecuteEngine(sqlStatement, "foo_db", contextManager,
null).executeUpdate();
- MetaDataManagerPersistService metaDataManagerPersistService =
contextManager.getPersistServiceFacade().getModeFacade().getMetaDataManagerService();
- verify(metaDataManagerPersistService).alterRuleConfiguration(any(),
any());
+ void assertCheckBeforeUpdateWithAllSchemaTablesPattern() {
+ prepareSchema(false, "foo_db");
+ assertDoesNotThrow(() -> executor.checkBeforeUpdate(new
LoadSingleTableStatement(Collections.singletonList(new SingleTableSegment("*",
"*", "*")))));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("assertBuildToBeCreatedRuleConfigurationArguments")
+ void assertBuildToBeCreatedRuleConfiguration(final String name, final
Collection<String> currentTables, final LoadSingleTableStatement sqlStatement,
final Collection<String> expectedTables) {
+ if (null != currentTables) {
+ SingleRule rule = mock(SingleRule.class);
+ when(rule.getConfiguration()).thenReturn(new
SingleRuleConfiguration(new LinkedList<>(currentTables), null));
+ executor.setRule(rule);
+ }
+ assertThat(new
HashSet<>(executor.buildToBeCreatedRuleConfiguration(sqlStatement).getTables()),
is(new HashSet<>(expectedTables)));
}
@Test
- void assertExecuteUpdateWithoutSingleRule() throws SQLException {
- Collection<String> currentTables = new
LinkedList<>(Collections.singleton("foo_ds.foo_tbl"));
-
when(database.getResourceMetaData().getNotExistedDataSources(any())).thenReturn(Collections.emptyList());
+ void assertGetRuleClass() {
+ assertThat(executor.getRuleClass(), is(SingleRule.class));
+ }
+
+ private void prepareStorageUnits() {
StorageUnit storageUnit = mock(StorageUnit.class);
when(storageUnit.getDataSource()).thenReturn(new MockedDataSource());
when(database.getResourceMetaData().getStorageUnits()).thenReturn(Collections.singletonMap("foo_ds",
storageUnit));
- when(SingleTableDataNodeLoader.load(eq("foo_db"), any(),
any())).thenReturn(Collections.singletonMap("foo_tbl",
Collections.singleton(new DataNode("foo_ds.foo_tbl"))));
- when(SingleTableLoadUtils.convertToDataNodes(eq("foo_db"), any(),
any())).thenReturn(Collections.singleton(new DataNode("foo_ds.foo_tbl")));
- SingleRuleConfiguration currentConfig = new
SingleRuleConfiguration(currentTables, null);
- LoadSingleTableStatement sqlStatement = new
LoadSingleTableStatement(Collections.singleton(new SingleTableSegment("*",
"bar_tbl")));
- sqlStatement.buildAttributes();
- SingleRule rule = mock(SingleRule.class);
- when(rule.getConfiguration()).thenReturn(currentConfig);
- ContextManager contextManager = mockContextManager(null);
- new DistSQLUpdateExecuteEngine(sqlStatement, "foo_db", contextManager,
null).executeUpdate();
- MetaDataManagerPersistService metaDataManagerPersistService =
contextManager.getPersistServiceFacade().getModeFacade().getMetaDataManagerService();
- verify(metaDataManagerPersistService).alterRuleConfiguration(any(),
any());
+ }
+
+ private void prepareSchema(final boolean tableExists, final String
schemaName) {
+ ShardingSphereSchema schema = mock(ShardingSphereSchema.class);
+ when(schema.containsTable("foo_tbl")).thenReturn(tableExists);
+ when(database.getSchema(schemaName)).thenReturn(schema);
+ }
+
+ private void prepareActualTableValidationScenario(final Map<String,
DataSource> aggregatedDataSources, final Map<String, Collection<String>>
schemaTableNames) {
+ prepareStorageUnits();
+ prepareSchema(false, "foo_db");
+ when(PhysicalDataSourceAggregator.getAggregatedDataSources(any(),
any())).thenReturn(aggregatedDataSources);
+ if (aggregatedDataSources.containsKey("foo_ds")) {
+ when(SingleTableDataNodeLoader.loadSchemaTableNames(eq("foo_db"),
any(), any(), eq("foo_ds"),
eq(Collections.emptyList()))).thenReturn(schemaTableNames);
+ }
+ }
+
+ private MockedConstruction<DatabaseTypeRegistry>
mockSchemaSupportedDatabaseTypeRegistry() {
+ DialectDatabaseMetaData dialectDatabaseMetaData =
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("foo_schema"));
+ return mockConstruction(DatabaseTypeRegistry.class, (mock, context) ->
{
+ when(mock.getDefaultSchemaName("foo_db")).thenReturn("foo_schema");
+
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData);
+ });
+ }
+
+ private static Stream<Arguments>
assertCheckBeforeUpdateWithPreValidationFailureArguments() {
+ return Stream.of(
+ Arguments.of("schema unsupported rejects schema name", false,
new SingleTableSegment("foo_ds", "foo_schema", "foo_tbl"), false,
InvalidDataNodeFormatException.class),
+ Arguments.of("schema required rejects missing schema", true,
new SingleTableSegment("foo_ds", "foo_tbl"), false,
InvalidDataNodeFormatException.class),
+ Arguments.of("logic table existence is rejected", false, new
SingleTableSegment("foo_ds", "foo_tbl"), true, TableExistsException.class));
+ }
+
+ private static Stream<Arguments>
assertCheckBeforeUpdateWithActualTableValidationFailureArguments() {
+ return Stream.of(
+ Arguments.of("invalid storage unit is rejected",
Collections.<String, DataSource>emptyMap(), Collections.emptyMap(),
InvalidStorageUnitStatusException.class),
+ Arguments.of("empty actual table nodes are rejected",
Collections.singletonMap("foo_ds", new MockedDataSource()),
Collections.emptyMap(), TableNotFoundException.class),
+ Arguments.of("missing table in actual table nodes is
rejected", Collections.singletonMap("foo_ds", new MockedDataSource()),
+ Collections.singletonMap("foo_db",
Collections.singleton("bar_tbl")), TableNotFoundException.class));
+ }
+
+ private static Stream<Arguments>
assertBuildToBeCreatedRuleConfigurationArguments() {
+ return Stream.of(
+ Arguments.of("without current rule keeps all requested
tables", null,
+ new LoadSingleTableStatement(Arrays.asList(new
SingleTableSegment("foo_ds", "foo_tbl"), new SingleTableSegment("foo_ds",
"bar_tbl"))),
+ Arrays.asList("foo_ds" + "." + "foo_tbl", "foo_ds" +
"." + "bar_tbl")),
+ Arguments.of("with current rule skips duplicated table",
Collections.singletonList("foo_ds" + "." + "foo_tbl"),
+ new LoadSingleTableStatement(Arrays.asList(new
SingleTableSegment("foo_ds", "foo_tbl"), new SingleTableSegment("foo_ds",
"bar_tbl"))),
+ Arrays.asList("foo_ds" + "." + "foo_tbl", "foo_ds" +
"." + "bar_tbl")),
+ Arguments.of("with current rule keeps existing set when all
requested exist", Collections.singletonList("foo_ds" + "." + "foo_tbl"),
+ new
LoadSingleTableStatement(Collections.singletonList(new
SingleTableSegment("foo_ds", "foo_tbl"))),
+ Collections.singletonList("foo_ds" + "." +
"foo_tbl")));
}
}