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 4b8fc310821 Add more test cases on SingleSQLFederationDeciderTest 
(#38081)
4b8fc310821 is described below

commit 4b8fc310821a31a9b88d9fc72f789ea04d3ec858
Author: Liang Zhang <[email protected]>
AuthorDate: Wed Feb 18 17:00:03 2026 +0800

    Add more test cases on SingleSQLFederationDeciderTest (#38081)
---
 .codex/skills/gen-ut/SKILL.md                      |  34 +++++
 .../single/decider/SingleSQLFederationDecider.java |   3 -
 .../decider/SingleSQLFederationDeciderTest.java    | 140 ++++++++++++++-------
 3 files changed, 130 insertions(+), 47 deletions(-)

diff --git a/.codex/skills/gen-ut/SKILL.md b/.codex/skills/gen-ut/SKILL.md
index 444426968f3..656cadc9790 100644
--- a/.codex/skills/gen-ut/SKILL.md
+++ b/.codex/skills/gen-ut/SKILL.md
@@ -53,6 +53,7 @@ Module resolution order:
 - `R2`: test types and naming
   - Non-parameterized scenarios `MUST` use JUnit `@Test`.
   - Data-driven scenarios `MUST` use JUnit `@ParameterizedTest(name = "{0}")` 
with `@MethodSource` + `Arguments`.
+  - Parameterized test method signatures `MUST` use `final String name` as the 
first parameter.
   - Each parameterized test `MUST` provide at least 3 `Arguments` rows; fewer 
than 3 is a violation and `MUST` be converted to non-parameterized `@Test`.
   - `MUST NOT` use `@RepeatedTest`.
   - Test method naming `MUST` follow `CODE_OF_CONDUCT.md`: use the `assert` 
prefix; when a single test uniquely covers a production method, use 
`assert<MethodName>`.
@@ -161,6 +162,7 @@ Module resolution order:
   - `R15-B` (metadata accessor test ban): unless the user explicitly requests 
it in the current turn, tests targeting `getType` / `getOrder` / `getTypeClass` 
`MUST NOT` be added.
   - `R15-C` (scope mutation guard): test-generation tasks `MUST NOT` introduce 
new diffs under any `src/main/` path.
   - `R15-D` (parameterized argument floor): each `@ParameterizedTest` `MUST` 
bind to `@MethodSource` providers that together contain at least 3 `Arguments` 
rows; otherwise it is a violation.
+  - `R15-E` (parameterized name parameter): each `@ParameterizedTest` method 
`MUST` declare the first parameter exactly as `final String name`.
 
 ## Workflow
 
@@ -446,6 +448,38 @@ PY
 '
 ```
 
+5.3 `R15-E` parameterized first-parameter scan:
+```bash
+bash -lc '
+python3 - <ResolvedTestFileSet> <<'"'"'PY'"'"'
+import re
+import sys
+from pathlib import Path
+
+PARAM_METHOD_PATTERN = 
re.compile(r"@ParameterizedTest(?:\\s*\\([^)]*\\))?\\s*(?:@\\w+(?:\\s*\\([^)]*\\))?\\s*)*void\\s+(assert\\w+)\\s*\\(([^)]*)\\)",
 re.S)
+violations = []
+for path in (each for each in sys.argv[1:] if each.endswith(".java")):
+    source = Path(path).read_text(encoding="utf-8")
+    for match in PARAM_METHOD_PATTERN.finditer(source):
+        method_name = match.group(1)
+        params = match.group(2).strip()
+        line = source.count("\\n", 0, match.start()) + 1
+        if not params:
+            violations.append(f"{path}:{line} method={method_name} 
missingParameters")
+            continue
+        first_param = params.split(",", 1)[0].strip()
+        normalized = re.sub(r"\\s+", " ", first_param)
+        if "final String name" != normalized:
+            violations.append(f"{path}:{line} method={method_name} 
firstParam={first_param}")
+if violations:
+    print("[R15-E] each @ParameterizedTest method must declare first parameter 
as `final String name`")
+    for each in violations:
+        print(each)
+    sys.exit(1)
+PY
+'
+```
+
 6. `R14` hard-gate scan:
 ```bash
 bash -lc '
diff --git 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDecider.java
 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDecider.java
index a578d49905d..2c31e0fba5d 100644
--- 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDecider.java
+++ 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDecider.java
@@ -72,9 +72,6 @@ public final class SingleSQLFederationDecider implements 
SQLFederationDecider<Si
     }
     
     private boolean isInnerCommaJoin(final SQLStatement sqlStatement) {
-        if (!(sqlStatement instanceof SelectStatement)) {
-            return true;
-        }
         SelectStatement selectStatement = (SelectStatement) sqlStatement;
         if (!selectStatement.getFrom().isPresent() || 
!(selectStatement.getFrom().get() instanceof JoinTableSegment)) {
             return true;
diff --git 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDeciderTest.java
 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDeciderTest.java
index 5ad572966ef..0afe73e4ff3 100644
--- 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDeciderTest.java
+++ 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decider/SingleSQLFederationDeciderTest.java
@@ -18,26 +18,39 @@
 package org.apache.shardingsphere.single.decider;
 
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import 
org.apache.shardingsphere.infra.binder.context.statement.type.dal.ExplainStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
 import org.apache.shardingsphere.infra.datanode.DataNode;
+import 
org.apache.shardingsphere.infra.exception.generic.UnsupportedSQLOperationException;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
 import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedTable;
 import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
 import 
org.apache.shardingsphere.infra.rule.attribute.datanode.MutableDataNodeRuleAttribute;
+import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.single.rule.SingleRule;
+import org.apache.shardingsphere.sql.parser.statement.core.enums.JoinType;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.JoinTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import org.apache.shardingsphere.sqlfederation.spi.SQLFederationDecider;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 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.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -46,77 +59,116 @@ import static org.mockito.Mockito.when;
 
 class SingleSQLFederationDeciderTest {
     
+    private static final DatabaseType DATABASE_TYPE = 
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
+    
+    private final SingleSQLFederationDecider decider = 
(SingleSQLFederationDecider) OrderedSPILoader.getServicesByClass(
+            SQLFederationDecider.class, 
Collections.singleton(SingleRule.class)).get(SingleRule.class);
+    
     @Test
-    void assertDecideWhenNotContainsSingleTable() {
-        SelectStatementContext select = mockStatementContext();
+    void assertDecideWhenSingleTablesContainView() {
+        Collection<QualifiedTable> qualifiedTables = 
Collections.singletonList(new QualifiedTable("foo_db", "foo_tbl"));
+        SingleRule rule = createSingleRule(qualifiedTables, true);
+        SelectStatementContext selectStatementContext = 
createSelectStatementContext(new SelectStatement(DATABASE_TYPE));
         Collection<DataNode> includedDataNodes = new HashSet<>();
-        assertFalse(new SingleSQLFederationDecider().decide(select, 
Collections.emptyList(), mock(RuleMetaData.class), mockDatabase(), 
mock(SingleRule.class), includedDataNodes));
+        assertTrue(decider.decide(selectStatementContext, 
Collections.emptyList(), mock(RuleMetaData.class), createDatabase(true), rule, 
includedDataNodes));
         assertTrue(includedDataNodes.isEmpty());
     }
     
-    @Test
-    void assertDecideWhenAllSingleTablesInSameComputeNode() {
-        Collection<QualifiedTable> qualifiedTables = Arrays.asList(new 
QualifiedTable("foo_db", "t_order"), new QualifiedTable("foo_db", 
"t_order_item"));
-        SingleRule rule = createSingleRule(qualifiedTables);
-        SelectStatementContext select = mockStatementContext();
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("assertDecideWithComputeNodeResultArguments")
+    void assertDecideWithComputeNodeResult(final String name, final boolean 
allTablesInSameComputeNode, final boolean hasOrderDataNode, final boolean 
expectedDecideResult,
+                                           final int 
expectedIncludedDataNodeSize) {
+        Collection<QualifiedTable> qualifiedTables = 
Collections.singletonList(new QualifiedTable("foo_db", "foo_tbl"));
+        SelectStatementContext selectStatementContext = 
createSelectStatementContext(new SelectStatement(DATABASE_TYPE));
+        SingleRule rule = createSingleRule(qualifiedTables, hasOrderDataNode);
+        when(rule.isAllTablesInSameComputeNode(any(), 
any())).thenReturn(allTablesInSameComputeNode);
         Collection<DataNode> includedDataNodes = new HashSet<>();
-        when(rule.isAllTablesInSameComputeNode(includedDataNodes, 
qualifiedTables)).thenReturn(true);
-        assertFalse(new SingleSQLFederationDecider().decide(select, 
Collections.emptyList(), mock(RuleMetaData.class), mockDatabase(), rule, 
includedDataNodes));
-        assertThat(includedDataNodes.size(), is(2));
+        assertThat(name, decider.decide(selectStatementContext, 
Collections.emptyList(), mock(RuleMetaData.class), createDatabase(false), rule, 
includedDataNodes), is(expectedDecideResult));
+        assertThat(includedDataNodes.size(), is(expectedIncludedDataNodeSize));
     }
     
-    @Test
-    void assertDecideWhenAllSingleTablesNotInSameComputeNode() {
-        Collection<QualifiedTable> qualifiedTables = Arrays.asList(new 
QualifiedTable("foo_db", "t_order"), new QualifiedTable("foo_db", 
"t_order_item"));
-        SingleRule rule = createSingleRule(qualifiedTables);
-        SelectStatementContext select = mockStatementContext();
-        Collection<DataNode> includedDataNodes = new HashSet<>();
-        when(rule.isAllTablesInSameComputeNode(includedDataNodes, 
qualifiedTables)).thenReturn(false);
-        assertTrue(new SingleSQLFederationDecider().decide(select, 
Collections.emptyList(), mock(RuleMetaData.class), mockDatabase(), rule, 
includedDataNodes));
-        assertThat(includedDataNodes.size(), is(2));
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("assertDecideWithIncludedDataNodesAndJoinTypeArguments")
+    void assertDecideWithIncludedDataNodesAndJoinType(final String name, final 
SelectStatement sqlStatement, final boolean expectedDecideResult, final int 
expectedIncludedDataNodeSize) {
+        Collection<QualifiedTable> qualifiedTables = 
Collections.singletonList(new QualifiedTable("foo_db", "foo_tbl"));
+        SingleRule rule = createSingleRule(qualifiedTables, true);
+        when(rule.isAllTablesInSameComputeNode(any(), any())).thenReturn(true);
+        SelectStatementContext selectStatementContext = 
createSelectStatementContext(sqlStatement);
+        Collection<DataNode> includedDataNodes = new 
HashSet<>(Collections.singleton(new DataNode("ds_1", (String) null, "t_user")));
+        assertThat(name, decider.decide(selectStatementContext, 
Collections.emptyList(), mock(RuleMetaData.class), createDatabase(false), rule, 
includedDataNodes), is(expectedDecideResult));
+        assertThat(includedDataNodes.size(), is(expectedIncludedDataNodeSize));
     }
     
     @Test
-    void assertDecideWhenAllTablesInSameComputeNode() {
-        Collection<QualifiedTable> qualifiedTables = Arrays.asList(new 
QualifiedTable("foo_db", "t_order"), new QualifiedTable("foo_db", 
"t_order_item"));
-        SingleRule rule = createSingleRule(qualifiedTables);
-        SelectStatementContext select = mockStatementContext();
-        Collection<DataNode> includedDataNodes = new 
HashSet<>(Collections.singleton(new DataNode("ds_0", (String) null, "t_user")));
-        when(rule.isAllTablesInSameComputeNode(includedDataNodes, 
qualifiedTables)).thenReturn(true);
-        assertFalse(new SingleSQLFederationDecider().decide(select, 
Collections.emptyList(), mock(RuleMetaData.class), mockDatabase(), rule, 
includedDataNodes));
-        assertThat(includedDataNodes.size(), is(3));
+    void assertDecideWhenExplainStatementContext() {
+        ExplainStatementContext explainStatementContext = 
mock(ExplainStatementContext.class);
+        SelectStatementContext explainableSQLStatementContext = 
createSelectStatementContext(new SelectStatement(DATABASE_TYPE));
+        
when(explainStatementContext.getExplainableSQLStatementContext()).thenReturn(explainableSQLStatementContext);
+        Collection<DataNode> includedDataNodes = new HashSet<>();
+        SingleRule rule = createSingleRule(Collections.emptyList(), true);
+        assertFalse(decider.decide(explainStatementContext, 
Collections.emptyList(), mock(RuleMetaData.class), createDatabase(false), rule, 
includedDataNodes));
+        assertTrue(includedDataNodes.isEmpty());
     }
     
     @Test
-    void assertDecideWhenAllTablesNotInSameComputeNode() {
-        Collection<QualifiedTable> qualifiedTables = Arrays.asList(new 
QualifiedTable("foo_db", "t_order"), new QualifiedTable("foo_db", 
"t_order_item"));
-        SingleRule rule = createSingleRule(qualifiedTables);
-        SelectStatementContext select = mockStatementContext();
-        Collection<DataNode> includedDataNodes = new 
HashSet<>(Collections.singleton(new DataNode("ds_1", (String) null, "t_user")));
-        when(rule.isAllTablesInSameComputeNode(includedDataNodes, 
qualifiedTables)).thenReturn(false);
-        assertTrue(new SingleSQLFederationDecider().decide(select, 
Collections.emptyList(), mock(RuleMetaData.class), mockDatabase(), rule, 
includedDataNodes));
-        assertThat(includedDataNodes.size(), is(3));
+    void assertDecideWhenSQLStatementContextNotSupported() {
+        SQLStatementContext sqlStatementContext = 
mock(SQLStatementContext.class);
+        when(sqlStatementContext.getSqlStatement()).thenReturn(new 
SelectStatement(DATABASE_TYPE));
+        UnsupportedSQLOperationException actual = 
assertThrows(UnsupportedSQLOperationException.class,
+                () -> decider.decide(sqlStatementContext, 
Collections.emptyList(), mock(RuleMetaData.class), createDatabase(false), 
mock(SingleRule.class), new HashSet<>()));
+        assertThat(actual.getMessage(), is("Unsupported SQL operation: 
unsupported SQL statement SelectStatement in sql federation."));
     }
     
-    private SingleRule createSingleRule(final Collection<QualifiedTable> 
qualifiedTables) {
+    private SingleRule createSingleRule(final Collection<QualifiedTable> 
qualifiedTables, final boolean hasOrderDataNode) {
         SingleRule result = mock(SingleRule.class);
         when(result.getSingleTables(any())).thenReturn(qualifiedTables);
+        when(result.getQualifiedTables(any(), 
any())).thenReturn(qualifiedTables);
         MutableDataNodeRuleAttribute ruleAttribute = 
mock(MutableDataNodeRuleAttribute.class);
-        when(ruleAttribute.findTableDataNode("foo_db", 
"t_order")).thenReturn(Optional.of(new DataNode("ds_0", (String) null, 
"t_order")));
-        when(ruleAttribute.findTableDataNode("foo_db", 
"t_order_item")).thenReturn(Optional.of(new DataNode("ds_0", (String) null, 
"t_order_item")));
+        when(ruleAttribute.findTableDataNode("foo_db", 
"foo_tbl")).thenReturn(hasOrderDataNode ? Optional.of(new DataNode("ds_0", 
(String) null, "foo_tbl")) : Optional.empty());
         when(result.getAttributes()).thenReturn(new 
RuleAttributes(ruleAttribute));
         return result;
     }
     
-    private SelectStatementContext mockStatementContext() {
-        SelectStatementContext result = mock(SelectStatementContext.class, 
RETURNS_DEEP_STUBS);
-        
when(result.getSqlStatement().getDatabaseType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "FIXTURE"));
+    private SelectStatementContext createSelectStatementContext(final 
SelectStatement sqlStatement) {
+        SelectStatementContext result = mock(SelectStatementContext.class);
+        when(result.getSqlStatement()).thenReturn(sqlStatement);
         return result;
     }
     
-    private ShardingSphereDatabase mockDatabase() {
+    private ShardingSphereDatabase createDatabase(final boolean containsView) {
         ShardingSphereDatabase result = mock(ShardingSphereDatabase.class, 
RETURNS_DEEP_STUBS);
         when(result.getName()).thenReturn("foo_db");
+        
when(result.getSchema("foo_db").containsView("foo_tbl")).thenReturn(containsView);
+        return result;
+    }
+    
+    private static Stream<Arguments> 
assertDecideWithComputeNodeResultArguments() {
+        return Stream.of(
+                Arguments.of("all tables in same compute node with table data 
node", true, true, false, 1),
+                Arguments.of("all tables not in same compute node with table 
data node", false, true, true, 1),
+                Arguments.of("all tables in same compute node without table 
data node", true, false, false, 0));
+    }
+    
+    private static Stream<Arguments> 
assertDecideWithIncludedDataNodesAndJoinTypeArguments() {
+        return Stream.of(
+                Arguments.of("select statement without from segment", new 
SelectStatement(DATABASE_TYPE), false, 2),
+                Arguments.of("select statement with non join from segment", 
createSelectStatementWithNonJoinFrom(), false, 2),
+                Arguments.of("select statement with inner join", 
createSelectStatementWithJoinType(JoinType.INNER.name()), false, 2),
+                Arguments.of("select statement with comma join", 
createSelectStatementWithJoinType(JoinType.COMMA.name()), false, 2),
+                Arguments.of("select statement with left join", 
createSelectStatementWithJoinType(JoinType.LEFT.name()), true, 1));
+    }
+    
+    private static SelectStatement createSelectStatementWithNonJoinFrom() {
+        SelectStatement result = new SelectStatement(DATABASE_TYPE);
+        result.setFrom(mock(TableSegment.class));
+        return result;
+    }
+    
+    private static SelectStatement createSelectStatementWithJoinType(final 
String joinType) {
+        SelectStatement result = new SelectStatement(DATABASE_TYPE);
+        JoinTableSegment joinTableSegment = new JoinTableSegment();
+        joinTableSegment.setJoinType(joinType);
+        result.setFrom(joinTableSegment);
         return result;
     }
 }

Reply via email to