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 5b3c7df25d2 Add more test cases on 
DatabaseProxyBackendHandlerFactoryTest (#37981)
5b3c7df25d2 is described below

commit 5b3c7df25d2c5c320bcb3151ceca1362a2467a74
Author: Liang Zhang <[email protected]>
AuthorDate: Sun Feb 8 11:02:16 2026 +0800

    Add more test cases on DatabaseProxyBackendHandlerFactoryTest (#37981)
---
 .codex/skills/gen-ut/SKILL.md                      | 40 ++++++++---
 AGENTS.md                                          |  3 +-
 .../DatabaseProxyBackendHandlerFactoryTest.java    | 82 +++++++++++++++++-----
 .../variable/SetDistVariableExecutorTest.java      |  3 +-
 4 files changed, 98 insertions(+), 30 deletions(-)

diff --git a/.codex/skills/gen-ut/SKILL.md b/.codex/skills/gen-ut/SKILL.md
index 83805e26257..4372cc39cf7 100644
--- a/.codex/skills/gen-ut/SKILL.md
+++ b/.codex/skills/gen-ut/SKILL.md
@@ -23,6 +23,7 @@ Handling missing input:
 
 Test class placeholder convention:
 - `<ResolvedTestClass>` can be one fully qualified test class or a 
comma-separated list.
+- `<ResolvedTestFileSet>` is the concrete test file path list resolved from 
`<ResolvedTestClass>` (space-separated in shell commands).
 
 ## Mandatory Constraints (Single Source of Rules)
 
@@ -57,6 +58,21 @@ Test class placeholder convention:
     - after test implementation (early fail-fast gate);
     - before final delivery (final release gate).
   - If any match is found, task state is "not complete" until all violations 
are fixed and scan is rerun clean.
+- `R14`: Change scope must be strictly limited to tests resolved from input 
target classes.
+  - Allowed edit files: only `<ResolvedTestClass>` mapped source files under 
`src/test/java` or `src/test/resources`.
+  - Forbidden: modifying any other test file to fix unrelated build/lint/gate 
failures.
+  - Exception: only if the user explicitly approves scope expansion in the 
current turn.
+- `R15`: No meaningless test code; each line must have direct necessity for 
the scenario.
+  - Do not add redundant mocks/stubs/assertions that do not change branch 
selection, object behavior, or verification result.
+  - Prefer Mockito default return values unless explicit stubbing is required 
by the scenario.
+  - If a line is only cosmetic and removing it does not affect scenario setup 
or assertions, remove it.
+- `R16`: Inline single-use local variables in tests.
+  - If a local variable is used exactly once, inline it at the call site.
+  - Exception: keep the variable only when it is required for additional 
stubbing/verification or shared assertions.
+- `R17`: Test-method necessity hard gate; no coverage-equivalent duplicates.
+  - Each test method must add at least one unique value: either an uncovered 
branch/path from `R4/R5`, or a distinct externally observable behavior 
assertion not already covered.
+  - If removing a test method keeps target-class line/branch coverage 
unchanged and does not lose unique behavior verification, remove that method.
+  - For factory/route fallback scenarios, keep only one representative method 
per branch outcome unless the user explicitly requests an extra regression 
guard.
 
 ## Execution Boundary
 
@@ -64,6 +80,7 @@ Test class placeholder convention:
 - Do not edit generated directories (such as `target/`).
 - Do not use destructive git operations (such as `git reset --hard`, `git 
checkout --`).
 - If module name is missing, module-less command templates are allowed; if 
module is specified, module-scoped commands are mandatory.
+- If failures come from out-of-scope test files under `R14`, report blockers 
and wait for user decision instead of editing those files.
 
 ## Workflow
 
@@ -72,11 +89,12 @@ Test class placeholder convention:
 3. Output branch-path inventory according to `R4`.
 4. Output branch-to-test mapping according to `R5`.
 5. Perform dead-code analysis according to `R8` and record findings.
-6. Implement or extend tests according to `R2/R3/R6/R7/R10/R12/R13`.
-7. Run the first `R13` hard-gate scan (early fail-fast) and fix all hits.
-8. Run verification commands and iterate.
-9. Run the second `R13` hard-gate scan (final release gate) and ensure clean.
-10. Deliver results using the output structure.
+6. Implement or extend tests according to 
`R2/R3/R6/R7/R10/R12/R13/R14/R15/R16/R17`.
+7. Run necessity self-check according to `R15/R16/R17` and remove redundant 
mocks/stubs/assertions/single-use locals/coverage-equivalent test methods.
+8. Run the first `R13` hard-gate scan (early fail-fast) and fix all hits.
+9. Run verification commands and iterate.
+10. Run the second `R13` hard-gate scan (final release gate) and ensure clean.
+11. Deliver results using the output structure.
 
 ## Verification and Commands
 
@@ -97,9 +115,9 @@ With module input:
 ./mvnw -pl <module> -am -Pcheck checkstyle:check -DskipTests
 ```
 
-4. `R13` hard-gate scan (must be clean, run in step 7 and step 9):
+4. `R13` hard-gate scan (must be clean, run in step 8 and step 10):
 ```bash
-bash -lc 'if rg -n 
"assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,"
 <module>/src/test/java; then echo "[R13] forbidden boolean assertion found"; 
exit 1; fi'
+bash -lc 'if rg -n 
"assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,"
 <ResolvedTestFileSet>; then echo "[R13] forbidden boolean assertion found"; 
exit 1; fi'
 ```
 
 Without module input:
@@ -119,9 +137,9 @@ Without module input:
 ./mvnw -Pcheck checkstyle:check -DskipTests
 ```
 
-4. `R13` hard-gate scan (must be clean, run in step 7 and step 9):
+4. `R13` hard-gate scan (must be clean, run in step 8 and step 10):
 ```bash
-bash -lc 'if rg -n 
"assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,"
 . --glob "**/src/test/java/**"; then echo "[R13] forbidden boolean assertion 
found"; exit 1; fi'
+bash -lc 'if rg -n 
"assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,"
 <ResolvedTestFileSet>; then echo "[R13] forbidden boolean assertion found"; 
exit 1; fi'
 ```
 
 Command execution rules:
@@ -133,7 +151,7 @@ Command execution rules:
 
 Follow this order:
 
-1. Goal and constraints (mapped to `R1-R13`)
+1. Goal and constraints (mapped to `R1-R17`)
 2. Plan and implementation (including branch mapping result)
 3. Dead-code and coverage results (according to `R8/R9`)
 4. Verification commands and exit codes
@@ -143,7 +161,7 @@ Follow this order:
 ## Quality Self-Check
 
 - Rule definitions must exist only in "Mandatory Constraints"; other sections 
should reference rule IDs only.
-- Final state must satisfy `R9`, and all applicable rules (including `R10`, 
`R11`, `R12`, and `R13`) must be met, with complete command and exit-code 
records.
+- Final state must satisfy `R9`, and all applicable rules (including `R10`, 
`R11`, `R12`, `R13`, `R14`, `R15`, `R16`, and `R17`) must be met, with complete 
command and exit-code records.
 - `R13` command is mandatory evidence; missing `R13` command record or 
non-clean scan means not complete.
 
 ## Maintenance Rules
diff --git a/AGENTS.md b/AGENTS.md
index e37047451fa..2543f5d07b6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -20,6 +20,7 @@ This guide is written **for AI coding agents only**. Follow 
it literally; improv
     - Use clear naming and reasonable abstractions.
     - Delete unused code; when changing functionality, remove legacy 
compatibility shims.
     - Keep variable declarations adjacent to first use; if a value must be 
retained, declare it `final` to satisfy Checkstyle 
VariableDeclarationUsageDistance.
+    - Single-use local variables must be inlined by default; keep a local 
variable only when it is reused (for stubbing/verification/assertions) or 
materially improves readability.
 - **Complete Implementation**: no MVPs/placeholders/TODOs—deliver fully 
runnable solutions.
 
 ### Performance Standards
@@ -88,7 +89,7 @@ Dangerous operation detected! Operation type: [specific 
action] Scope of impact:
 - **Pre-task checklist (do before planning/coding):** re-read AGENTS.md and 
`CODE_OF_CONDUCT.md`; restate user goal, constraints, forbidden tools/APIs, 
coverage expectations, sandbox/network/approval limits; prefer 
`rg`/`./mvnw`/`apply_patch`; avoid destructive commands (`git reset --hard`, 
`git checkout --`, bulk deletes) and generated paths like `target/`.
 - **Risk gate:** if any action fits the Dangerous Operation Checklist, pause 
and use the confirmation template before proceeding.
 - **Planning rules:** use Sequential Thinking with 3-10 actionable steps (no 
single-step plans) via the plan tool for non-trivial tasks; convert all hard 
requirements (SPI usage, mocking rules, coverage/test naming, forbidden APIs) 
into a checklist inside the plan and do not code until each item is addressed 
or explicitly waived.
-- **Execution discipline:** inspect existing code before edits; keep changes 
minimal; default to mocks and SPI loaders; keep variable declarations near 
first use and mark retained values `final`; delete dead code and avoid 
placeholders/TODOs.
+- **Execution discipline:** inspect existing code before edits; keep changes 
minimal; default to mocks and SPI loaders; keep variable declarations near 
first use and mark retained values `final`; inline single-use locals by default 
unless reuse/readability justifies retention; delete dead code and avoid 
placeholders/TODOs.
 - **Post-task self-check (before replying):** confirm all instructions were 
honored; verify no placeholders/unused code; ensure Checkstyle/Spotless gates 
for touched modules are satisfied or explain why not run and what to run; list 
commands with exit codes; call out risks and follow-ups.
 - **Final response template:** include intent/why, changed files with paths, 
rationale per file/section, commands run (with exit codes), verification 
status, and remaining risks/next actions (if tests skipped, state reason and 
the exact command to run).
 
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/DatabaseProxyBackendHandlerFactoryTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/DatabaseProxyBackendHandlerFactoryTest.java
index 44d8deb589c..1361735eb6d 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/DatabaseProxyBackendHandlerFactoryTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/DatabaseProxyBackendHandlerFactoryTest.java
@@ -17,24 +17,26 @@
 
 package org.apache.shardingsphere.proxy.backend.handler.data;
 
-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.CommonSQLStatementContext;
 import org.apache.shardingsphere.infra.hint.HintValueContext;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
 import org.apache.shardingsphere.infra.session.query.QueryContext;
-import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import 
org.apache.shardingsphere.proxy.backend.connector.ProxyDatabaseConnectionManager;
 import 
org.apache.shardingsphere.proxy.backend.connector.StandardDatabaseProxyConnector;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import 
org.apache.shardingsphere.proxy.backend.handler.data.type.UnicastDatabaseProxyBackendHandler;
+import 
org.apache.shardingsphere.proxy.backend.response.header.update.UpdateResponseHeader;
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.DatabaseSelectRequiredSQLStatementAttribute;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.DALStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.SetStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DoStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.ConstructionMockSettings;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
@@ -43,10 +45,12 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
 
+import java.sql.SQLException;
 import java.util.Collections;
 import java.util.Optional;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.isA;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
@@ -58,8 +62,6 @@ import static org.mockito.Mockito.when;
 @MockitoSettings(strictness = Strictness.LENIENT)
 class DatabaseProxyBackendHandlerFactoryTest {
     
-    private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
-    
     @Test
     void assertNewInstanceReturnedUnicastDatabaseProxyBackendHandlerWithDAL() {
         String sql = "DESC tbl";
@@ -71,6 +73,41 @@ class DatabaseProxyBackendHandlerFactoryTest {
         assertThat(actual, isA(UnicastDatabaseProxyBackendHandler.class));
     }
     
+    @Test
+    void assertNewInstanceReturnedUpdateResponseHeaderWithSetAndNoDatabase() 
throws SQLException {
+        String sql = "SET autocommit=1";
+        SetStatement setStatement = mock(SetStatement.class);
+        SQLStatementContext sqlStatementContext = 
mock(SQLStatementContext.class, RETURNS_DEEP_STUBS);
+        when(sqlStatementContext.getSqlStatement()).thenReturn(setStatement);
+        DatabaseProxyBackendHandler actual = 
DatabaseProxyBackendHandlerFactory.newInstance(
+                new QueryContext(sqlStatementContext, sql, 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
mock()), mock(ConnectionSession.class), false);
+        UpdateResponseHeader actualResponseHeader = (UpdateResponseHeader) 
actual.execute();
+        assertThat(actualResponseHeader, isA(UpdateResponseHeader.class));
+        assertThat(actualResponseHeader.getSqlStatement(), is(setStatement));
+    }
+    
+    @Test
+    void 
assertNewInstanceReturnedUnicastDatabaseProxyBackendHandlerWithSetAndDatabaseSelected()
 {
+        String sql = "SET autocommit=1";
+        SetStatement setStatement = mock(SetStatement.class, 
RETURNS_DEEP_STUBS);
+        
when(setStatement.getAttributes().findAttribute(DatabaseSelectRequiredSQLStatementAttribute.class)).thenReturn(Optional.empty());
+        SQLStatementContext sqlStatementContext = 
mock(SQLStatementContext.class, RETURNS_DEEP_STUBS);
+        when(sqlStatementContext.getSqlStatement()).thenReturn(setStatement);
+        DatabaseProxyBackendHandler actual = 
DatabaseProxyBackendHandlerFactory.newInstance(
+                new QueryContext(sqlStatementContext, sql, 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
mock()), mockConnectionSession(), false);
+        assertThat(actual, isA(UnicastDatabaseProxyBackendHandler.class));
+    }
+    
+    @Test
+    void assertNewInstanceReturnedUnicastDatabaseProxyBackendHandlerWithDo() {
+        String sql = "DO 1";
+        SQLStatementContext sqlStatementContext = 
mock(SQLStatementContext.class, RETURNS_DEEP_STUBS);
+        
when(sqlStatementContext.getSqlStatement()).thenReturn(mock(DoStatement.class));
+        DatabaseProxyBackendHandler actual = 
DatabaseProxyBackendHandlerFactory.newInstance(
+                new QueryContext(sqlStatementContext, sql, 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
mock()), mock(), false);
+        assertThat(actual, isA(UnicastDatabaseProxyBackendHandler.class));
+    }
+    
     private ConnectionContext mockConnectionContext() {
         ConnectionContext result = mock(ConnectionContext.class);
         
when(result.getCurrentDatabaseName()).thenReturn(Optional.of("foo_db"));
@@ -80,31 +117,42 @@ class DatabaseProxyBackendHandlerFactoryTest {
     @Test
     void 
assertNewInstanceReturnedUnicastDatabaseProxyBackendHandlerWithQueryWithoutFrom()
 {
         String sql = "SELECT 1";
-        SelectStatement selectStatement = new SelectStatement(databaseType);
-        selectStatement.buildAttributes();
-        SQLStatementContext sqlStatementContext = new 
CommonSQLStatementContext(selectStatement);
+        SelectStatement selectStatement = mock(SelectStatement.class);
+        when(selectStatement.getFrom()).thenReturn(Optional.empty());
+        SQLStatementContext sqlStatementContext = 
mock(SQLStatementContext.class, RETURNS_DEEP_STUBS);
+        
when(sqlStatementContext.getSqlStatement()).thenReturn(selectStatement);
         DatabaseProxyBackendHandler actual = 
DatabaseProxyBackendHandlerFactory.newInstance(
                 new QueryContext(sqlStatementContext, sql, 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
mock()), mock(), false);
         assertThat(actual, isA(UnicastDatabaseProxyBackendHandler.class));
     }
     
     @Test
-    void assertNewInstanceReturnedSchemaAssignedDatabaseProxyBackendHandler() {
-        String sql = "SELECT 1 FROM user WHERE id = 1";
-        SQLStatementContext sqlStatementContext = mockSQLStatementContext();
+    void 
assertNewInstanceReturnedStandardDatabaseProxyConnectorWithDALRequiringDatabase()
 {
+        DALStatement dalStatement = mock(DALStatement.class, 
RETURNS_DEEP_STUBS);
+        
when(dalStatement.getAttributes().findAttribute(DatabaseSelectRequiredSQLStatementAttribute.class)).thenReturn(Optional.of(mock(DatabaseSelectRequiredSQLStatementAttribute.class)));
+        assertNewInstanceReturnedStandardDatabaseProxyConnector(dalStatement, 
"SHOW PROCESSLIST");
+    }
+    
+    @Test
+    void 
assertNewInstanceReturnedStandardDatabaseProxyConnectorWithSelectFrom() {
+        SelectStatement selectStatement = mock(SelectStatement.class);
+        
when(selectStatement.getFrom()).thenReturn(Optional.of(mock(TableSegment.class)));
+        
assertNewInstanceReturnedStandardDatabaseProxyConnector(selectStatement, 
"SELECT 1 FROM t_order");
+    }
+    
+    private void assertNewInstanceReturnedStandardDatabaseProxyConnector(final 
SQLStatement sqlStatement, final String sql) {
+        SQLStatementContext sqlStatementContext = 
mockSQLStatementContext(sqlStatement);
         ConnectionSession connectionSession = mockConnectionSession();
         ContextManager contextManager = mockContextManager();
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
-        DatabaseProxyBackendHandler actual =
-                DatabaseProxyBackendHandlerFactory.newInstance(
-                        new QueryContext(sqlStatementContext, sql, 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
mock(ShardingSphereMetaData.class)),
-                        connectionSession, false);
+        DatabaseProxyBackendHandler actual = 
DatabaseProxyBackendHandlerFactory.newInstance(
+                new QueryContext(sqlStatementContext, sql, 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
mock(ShardingSphereMetaData.class)), connectionSession, false);
         assertThat(actual, isA(StandardDatabaseProxyConnector.class));
     }
     
-    private SQLStatementContext mockSQLStatementContext() {
+    private SQLStatementContext mockSQLStatementContext(final SQLStatement 
sqlStatement) {
         SQLStatementContext result = mock(SQLStatementContext.class, 
RETURNS_DEEP_STUBS);
-        when(result.getSqlStatement()).thenReturn(mock(SQLStatement.class));
+        when(result.getSqlStatement()).thenReturn(sqlStatement);
         
when(result.getTablesContext().getSchemaNames()).thenReturn(Collections.emptyList());
         
when(result.getTablesContext().getDatabaseName()).thenReturn(Optional.empty());
         return result;
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/variable/SetDistVariableExecutorTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/variable/SetDistVariableExecutorTest.java
index 00875272eb7..00e982adfae 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/variable/SetDistVariableExecutorTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/variable/SetDistVariableExecutorTest.java
@@ -47,6 +47,7 @@ import java.util.Properties;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
@@ -69,7 +70,7 @@ class SetDistVariableExecutorTest {
         SetDistVariableStatement statement = new 
SetDistVariableStatement("proxy_meta_data_collector_enabled", "false");
         ContextManager contextManager = mockContextManager();
         executor.executeUpdate(statement, contextManager);
-        
assertThat(contextManager.getMetaDataContexts().getMetaData().getTemporaryProps().getValue(TemporaryConfigurationPropertyKey.PROXY_META_DATA_COLLECTOR_ENABLED),
 is(false));
+        assertFalse((Boolean) 
contextManager.getMetaDataContexts().getMetaData().getTemporaryProps().getValue(TemporaryConfigurationPropertyKey.PROXY_META_DATA_COLLECTOR_ENABLED));
     }
     
     @Test

Reply via email to