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 b7f9685c42c Add more test cases on FrontendChannelInboundHandlerTest
(#37892)
b7f9685c42c is described below
commit b7f9685c42c3e3f0229a0a4ce5d8afbd12e53aa3
Author: Liang Zhang <[email protected]>
AuthorDate: Fri Jan 30 00:41:09 2026 +0800
Add more test cases on FrontendChannelInboundHandlerTest (#37892)
* Add ignore pattern on dead links check
* Add more test cases on FrontendChannelInboundHandlerTest
---
.../netty/FrontendChannelInboundHandlerTest.java | 168 ++++++++++++++++++++-
1 file changed, 167 insertions(+), 1 deletion(-)
diff --git
a/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
b/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
index d6b7ebc4758..549fb9cacb6 100644
---
a/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
+++
b/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
@@ -17,7 +17,9 @@
package org.apache.shardingsphere.proxy.frontend.netty;
+import com.google.common.util.concurrent.MoreExecutors;
import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import lombok.SneakyThrows;
@@ -25,16 +27,24 @@ import
org.apache.shardingsphere.authentication.result.AuthenticationResult;
import
org.apache.shardingsphere.authentication.result.AuthenticationResultBuilder;
import org.apache.shardingsphere.authority.rule.AuthorityRule;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import
org.apache.shardingsphere.database.exception.core.exception.SQLDialectException;
import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
import org.apache.shardingsphere.database.protocol.payload.PacketPayload;
+import org.apache.shardingsphere.infra.executor.sql.process.ProcessEngine;
import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
import org.apache.shardingsphere.infra.metadata.user.Grantee;
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.jdbc.connection.ConnectionResourceLock;
import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
import
org.apache.shardingsphere.proxy.frontend.authentication.AuthenticationEngine;
+import org.apache.shardingsphere.proxy.frontend.exception.ExpectedExceptions;
+import
org.apache.shardingsphere.proxy.frontend.executor.ConnectionThreadExecutorGroup;
+import org.apache.shardingsphere.proxy.frontend.executor.UserExecutorGroup;
import
org.apache.shardingsphere.proxy.frontend.spi.DatabaseProtocolFrontendEngine;
+import org.apache.shardingsphere.proxy.frontend.state.ProxyStateContext;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
import org.apache.shardingsphere.transaction.rule.TransactionRule;
@@ -43,16 +53,28 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock;
+import org.mockito.MockedStatic;
import org.mockito.internal.configuration.plugins.Plugins;
+import java.sql.SQLException;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
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.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -78,7 +100,7 @@ class FrontendChannelInboundHandlerTest {
void setup() {
when(frontendEngine.getAuthenticationEngine()).thenReturn(authenticationEngine);
when(frontendEngine.getType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
"MySQL"));
-
when(authenticationEngine.handshake(any(ChannelHandlerContext.class))).thenReturn(CONNECTION_ID);
+
lenient().when(authenticationEngine.handshake(any(ChannelHandlerContext.class))).thenReturn(CONNECTION_ID);
channel = new EmbeddedChannel(false, true);
ContextManager contextManager = mock(ContextManager.class,
RETURNS_DEEP_STUBS);
when(contextManager.getMetaDataContexts().getMetaData().getGlobalRuleMetaData()).thenReturn(new
RuleMetaData(Arrays.asList(mock(TransactionRule.class),
mock(AuthorityRule.class))));
@@ -110,6 +132,40 @@ class FrontendChannelInboundHandlerTest {
assertThat(connectionSession.getUsedDatabaseName(), is("database"));
}
+ @Test
+ void assertChannelReadWithUnfinishedAuthentication() throws Exception {
+ channel.register();
+
when(authenticationEngine.authenticate(any(ChannelHandlerContext.class),
any(PacketPayload.class))).thenReturn(AuthenticationResultBuilder.continued());
+ channel.writeInbound(Unpooled.EMPTY_BUFFER);
+ assertFalse(getAuthenticated().get());
+ assertNull(connectionSession.getConnectionContext());
+ }
+
+ @Test
+ void assertChannelReadWhenAuthenticated() throws Exception {
+ channel.register();
+ getAuthenticated().set(true);
+ Object message = new Object();
+ ChannelHandlerContext context =
channel.pipeline().context(frontendChannelInboundHandler);
+ try (MockedStatic<ProxyStateContext> mockedStatic =
mockStatic(ProxyStateContext.class)) {
+ channel.writeInbound(message);
+ mockedStatic.verify(() -> ProxyStateContext.execute(context,
message, frontendEngine, connectionSession));
+ }
+ }
+
+ @Test
+ void assertChannelReadWithExpectedException() throws Exception {
+ channel.register();
+ SQLDialectException cause = new
SQLDialectException("assertChannelReadWithExpectedException") {
+ };
+
doThrow(cause).when(authenticationEngine).authenticate(any(ChannelHandlerContext.class),
any(PacketPayload.class));
+ DatabasePacket expectedPacket = mock(DatabasePacket.class);
+
when(frontendEngine.getCommandExecuteEngine().getErrorPacket(cause)).thenReturn(expectedPacket);
+ channel.writeInbound(Unpooled.EMPTY_BUFFER);
+ assertThat(channel.readOutbound(), is(expectedPacket));
+ assertFalse(channel.isActive());
+ }
+
@Test
void assertChannelReadNotAuthenticatedAndExceptionOccur() throws Exception
{
channel.register();
@@ -120,4 +176,114 @@ class FrontendChannelInboundHandlerTest {
channel.writeInbound(Unpooled.EMPTY_BUFFER);
assertThat(channel.readOutbound(), is(expectedPacket));
}
+
+ @Test
+ void assertChannelInactiveWithUnexpectedException() throws Exception {
+ ProxyDatabaseConnectionManager databaseConnectionManager =
mock(ProxyDatabaseConnectionManager.class);
+
when(databaseConnectionManager.closeAllResources()).thenReturn(Collections.singleton(new
SQLException("assertChannelInactiveWithUnexpectedException")));
+ setDatabaseConnectionManager(databaseConnectionManager);
+ ProcessEngine processEngine = mock(ProcessEngine.class);
+ setProcessEngine(processEngine);
+ connectionSession.setProcessId("process-id");
+ ExecutorService executorService =
MoreExecutors.newDirectExecutorService();
+ UserExecutorGroup userExecutorGroup = mock(UserExecutorGroup.class);
+
when(userExecutorGroup.getExecutorService()).thenReturn(executorService);
+ ConnectionThreadExecutorGroup connectionThreadExecutorGroup =
mock(ConnectionThreadExecutorGroup.class);
+ try (
+ MockedStatic<UserExecutorGroup> mockedUserExecutorGroup =
mockStatic(UserExecutorGroup.class);
+ MockedStatic<ConnectionThreadExecutorGroup>
mockedConnectionThreadExecutorGroup =
mockStatic(ConnectionThreadExecutorGroup.class)) {
+
mockedUserExecutorGroup.when(UserExecutorGroup::getInstance).thenReturn(userExecutorGroup);
+
mockedConnectionThreadExecutorGroup.when(ConnectionThreadExecutorGroup::getInstance).thenReturn(connectionThreadExecutorGroup);
+ channel.register();
+ channel.pipeline().fireChannelInactive();
+
verify(connectionThreadExecutorGroup).unregisterAndAwaitTermination(CONNECTION_ID);
+ verify(databaseConnectionManager).closeAllResources();
+ verify(processEngine).disconnect("process-id");
+ verify(frontendEngine).release(connectionSession);
+ }
+ executorService.shutdownNow();
+ }
+
+ @Test
+ void assertChannelInactiveWithExpectedException() throws Exception {
+ Collection<Class<? extends Exception>> originalExpectedExceptions =
new HashSet<>(getExpectedExceptions());
+ Collection<Class<? extends Exception>> expectedExceptions =
getExpectedExceptions();
+ expectedExceptions.add(SQLException.class);
+ try {
+ ProxyDatabaseConnectionManager databaseConnectionManager =
mock(ProxyDatabaseConnectionManager.class);
+
when(databaseConnectionManager.closeAllResources()).thenReturn(Collections.singleton(new
SQLException("assertChannelInactiveWithExpectedException")));
+ setDatabaseConnectionManager(databaseConnectionManager);
+ ExecutorService executorService =
MoreExecutors.newDirectExecutorService();
+ UserExecutorGroup userExecutorGroup =
mock(UserExecutorGroup.class);
+
when(userExecutorGroup.getExecutorService()).thenReturn(executorService);
+ ConnectionThreadExecutorGroup connectionThreadExecutorGroup =
mock(ConnectionThreadExecutorGroup.class);
+ try (
+ MockedStatic<UserExecutorGroup> mockedUserExecutorGroup =
mockStatic(UserExecutorGroup.class);
+ MockedStatic<ConnectionThreadExecutorGroup>
mockedConnectionThreadExecutorGroup =
mockStatic(ConnectionThreadExecutorGroup.class)) {
+
mockedUserExecutorGroup.when(UserExecutorGroup::getInstance).thenReturn(userExecutorGroup);
+
mockedConnectionThreadExecutorGroup.when(ConnectionThreadExecutorGroup::getInstance).thenReturn(connectionThreadExecutorGroup);
+ channel.register();
+ channel.pipeline().fireChannelInactive();
+
verify(connectionThreadExecutorGroup).unregisterAndAwaitTermination(CONNECTION_ID);
+ verify(databaseConnectionManager).closeAllResources();
+ verify(frontendEngine).release(connectionSession);
+ }
+ executorService.shutdownNow();
+ } finally {
+ Collection<Class<? extends Exception>> expectedExceptionClasses =
getExpectedExceptions();
+ expectedExceptionClasses.clear();
+ expectedExceptionClasses.addAll(originalExpectedExceptions);
+ }
+ }
+
+ @Test
+ void assertChannelWritabilityChangedWhenWritable() throws Exception {
+ ProxyDatabaseConnectionManager databaseConnectionManager =
mock(ProxyDatabaseConnectionManager.class);
+ ConnectionResourceLock connectionResourceLock =
mock(ConnectionResourceLock.class);
+
when(databaseConnectionManager.getConnectionResourceLock()).thenReturn(connectionResourceLock);
+ channel.register();
+ setDatabaseConnectionManager(databaseConnectionManager);
+ assertThat(connectionSession.getDatabaseConnectionManager(),
is(databaseConnectionManager));
+ Channel channelMock = mock(Channel.class);
+ ChannelHandlerContext context = mock(ChannelHandlerContext.class);
+ when(channelMock.isWritable()).thenReturn(true);
+ when(context.channel()).thenReturn(channelMock);
+ frontendChannelInboundHandler.channelWritabilityChanged(context);
+ verify(connectionResourceLock).doNotify();
+ }
+
+ @Test
+ void assertChannelWritabilityChangedWhenNotWritable() throws Exception {
+ ProxyDatabaseConnectionManager databaseConnectionManager =
mock(ProxyDatabaseConnectionManager.class);
+ channel.register();
+ setDatabaseConnectionManager(databaseConnectionManager);
+ assertThat(connectionSession.getDatabaseConnectionManager(),
is(databaseConnectionManager));
+ Channel channelMock = mock(Channel.class);
+ ChannelHandlerContext context = mock(ChannelHandlerContext.class);
+ when(channelMock.isWritable()).thenReturn(false);
+ when(context.channel()).thenReturn(channelMock);
+ frontendChannelInboundHandler.channelWritabilityChanged(context);
+ verify(databaseConnectionManager, never()).getConnectionResourceLock();
+ }
+
+ @SneakyThrows(ReflectiveOperationException.class)
+ private AtomicBoolean getAuthenticated() {
+ return (AtomicBoolean)
Plugins.getMemberAccessor().get(FrontendChannelInboundHandler.class.getDeclaredField("authenticated"),
frontendChannelInboundHandler);
+ }
+
+ @SneakyThrows(ReflectiveOperationException.class)
+ private void setDatabaseConnectionManager(final
ProxyDatabaseConnectionManager databaseConnectionManager) {
+
Plugins.getMemberAccessor().set(ConnectionSession.class.getDeclaredField("databaseConnectionManager"),
connectionSession, databaseConnectionManager);
+ }
+
+ @SneakyThrows(ReflectiveOperationException.class)
+ private void setProcessEngine(final ProcessEngine processEngine) {
+
Plugins.getMemberAccessor().set(FrontendChannelInboundHandler.class.getDeclaredField("processEngine"),
frontendChannelInboundHandler, processEngine);
+ }
+
+ @SuppressWarnings("unchecked")
+ @SneakyThrows(ReflectiveOperationException.class)
+ private Collection<Class<? extends Exception>> getExpectedExceptions() {
+ return (Collection<Class<? extends Exception>>)
Plugins.getMemberAccessor().get(ExpectedExceptions.class.getDeclaredField("EXCEPTIONS"),
null);
+ }
}