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")));
     }
 }

Reply via email to