Skip to content

Commit af601c2

Browse files
committed
add regression tests for duplicate login teardown ownership
(cherry picked from commit 916fff9)
1 parent 3f84857 commit af601c2

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (C) 2026 Velocity Contributors
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.velocitypowered.proxy.connection.client;
19+
20+
import static org.mockito.ArgumentMatchers.any;
21+
import static org.mockito.Mockito.doAnswer;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.never;
24+
import static org.mockito.Mockito.verify;
25+
import static org.mockito.Mockito.when;
26+
27+
import com.velocitypowered.api.network.HandshakeIntent;
28+
import com.velocitypowered.api.network.ProtocolVersion;
29+
import com.velocitypowered.api.util.GameProfile;
30+
import com.velocitypowered.proxy.VelocityServer;
31+
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
32+
import com.velocitypowered.proxy.config.VelocityConfiguration;
33+
import com.velocitypowered.proxy.connection.ConnectionTypes;
34+
import com.velocitypowered.proxy.connection.MinecraftConnection;
35+
import com.velocitypowered.proxy.event.MockEventManager;
36+
import com.velocitypowered.proxy.protocol.StateRegistry;
37+
import io.netty.channel.EventLoop;
38+
import java.lang.reflect.Field;
39+
import java.util.Optional;
40+
import java.util.UUID;
41+
import org.junit.jupiter.api.Test;
42+
43+
class AuthSessionHandlerTest {
44+
45+
@Test
46+
void duplicateLoginRejectedBeforeOwnershipDoesNotTeardown() {
47+
TestHarness harness = new TestHarness();
48+
when(harness.server.canRegisterConnection(any())).thenReturn(false);
49+
50+
harness.handler.activated();
51+
harness.handler.disconnected();
52+
53+
verify(harness.server, never()).unregisterConnection(any());
54+
verify(harness.inbound).cleanup();
55+
}
56+
57+
@Test
58+
void disconnectedWithOwnedPlayerStillTeardowns() throws ReflectiveOperationException {
59+
TestHarness harness = new TestHarness();
60+
ConnectedPlayer player = mock(ConnectedPlayer.class);
61+
setConnectedPlayer(harness.handler, player);
62+
63+
harness.handler.disconnected();
64+
65+
verify(player).teardown();
66+
verify(harness.inbound).cleanup();
67+
}
68+
69+
private static void setConnectedPlayer(AuthSessionHandler handler, ConnectedPlayer player)
70+
throws ReflectiveOperationException {
71+
Field field = AuthSessionHandler.class.getDeclaredField("connectedPlayer");
72+
field.setAccessible(true);
73+
field.set(handler, player);
74+
}
75+
76+
private static EventLoop immediateEventLoop() {
77+
EventLoop eventLoop = mock(EventLoop.class);
78+
doAnswer(invocation -> {
79+
((Runnable) invocation.getArgument(0)).run();
80+
return null;
81+
}).when(eventLoop).execute(any(Runnable.class));
82+
return eventLoop;
83+
}
84+
85+
private static final class TestHarness {
86+
87+
private final VelocityServer server = mock(VelocityServer.class);
88+
private final VelocityConfiguration configuration = mock(VelocityConfiguration.class);
89+
private final MockEventManager eventManager = new MockEventManager();
90+
private final LoginInboundConnection inbound = mock(LoginInboundConnection.class);
91+
private final MinecraftConnection connection = mock(MinecraftConnection.class);
92+
private final AuthSessionHandler handler;
93+
94+
private TestHarness() {
95+
EventLoop eventLoop = immediateEventLoop();
96+
when(server.getConfiguration()).thenReturn(configuration);
97+
when(server.getEventManager()).thenReturn(eventManager);
98+
when(server.getPlayer(any(UUID.class))).thenReturn(Optional.empty());
99+
when(configuration.getPlayerInfoForwardingMode()).thenReturn(PlayerInfoForwarding.NONE);
100+
when(configuration.isLogPlayerConnections()).thenReturn(false);
101+
102+
when(inbound.delegatedConnection()).thenReturn(connection);
103+
when(inbound.getVirtualHost()).thenReturn(Optional.empty());
104+
when(inbound.getRawVirtualHost()).thenReturn(Optional.empty());
105+
when(inbound.getHandshakeIntent()).thenReturn(HandshakeIntent.LOGIN);
106+
when(inbound.getIdentifiedKey()).thenReturn(null);
107+
108+
when(connection.getType()).thenReturn(ConnectionTypes.VANILLA);
109+
when(connection.getProtocolVersion()).thenReturn(ProtocolVersion.MINECRAFT_1_20_2);
110+
when(connection.eventLoop()).thenReturn(eventLoop);
111+
when(connection.isClosed()).thenReturn(false);
112+
when(connection.getState()).thenReturn(StateRegistry.LOGIN);
113+
114+
handler = new AuthSessionHandler(
115+
server,
116+
inbound,
117+
GameProfile.forOfflinePlayer("test-player"),
118+
false,
119+
null);
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)