Skip to content

Commit bdd5ea5

Browse files
committed
test(qwp): drive JvmErrorCleanup tests through public newReconnectFactory() instead of reflection
Replace the Class.forName("...$ReconnectSupplier") + private-constructor + getDeclaredMethod("buildAndConnect", ...) invocation with the existing public production accessor: newReconnectFactory().reconnect() is a one-line delegation to the private buildAndConnect(this), so the tests exercise the identical code path while staying compile-time safe against renames. Failures now surface unwrapped: the Error-path tests catch OutOfMemoryError directly (not InvocationTargetException.getCause()), and the Exception-path test catches LineSenderException directly. Unused reflection imports dropped; setField wiring for the bare Unsafe-allocated sender is unchanged.
1 parent ed908c5 commit bdd5ea5

1 file changed

Lines changed: 23 additions & 29 deletions

File tree

core/src/test/java/io/questdb/client/test/cutlass/qwp/client/QwpWebSocketSenderJvmErrorCleanupTest.java

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@
3232
import org.junit.Assert;
3333
import org.junit.Test;
3434

35-
import java.lang.reflect.Constructor;
3635
import java.lang.reflect.Field;
37-
import java.lang.reflect.InvocationTargetException;
38-
import java.lang.reflect.Method;
3936
import java.util.ArrayList;
4037
import java.util.Arrays;
4138
import java.util.List;
@@ -61,7 +58,9 @@
6158
* {@code CursorWebSocketSendLoopJvmErrorTest}: {@code Unsafe.allocateInstance}
6259
* plus reflective wiring of the fields the connect walk dereferences, with the
6360
* {@code setClientFactoryOverride} test seam substituting a stub client whose
64-
* {@code connect()} throws.
61+
* {@code connect()} throws. The walk itself is driven without reflection:
62+
* the public {@code newReconnectFactory().reconnect()} is a one-line
63+
* delegation to the private {@code buildAndConnect}.
6564
*/
6665
public class QwpWebSocketSenderJvmErrorCleanupTest {
6766

@@ -85,8 +84,8 @@ public void testErrorDuringConnectClosesClientAndStopsWalk() throws Exception {
8584
try {
8685
invokeBuildAndConnect(sender);
8786
Assert.fail("a JVM Error must propagate out of buildAndConnect");
88-
} catch (InvocationTargetException ite) {
89-
Assert.assertSame("the original Error must surface", oom, ite.getCause());
87+
} catch (OutOfMemoryError e) {
88+
Assert.assertSame("the original Error must surface", oom, e);
9089
}
9190
Assert.assertEquals("Error must stop the walk on the first attempt", 1, built.size());
9291
Assert.assertEquals("half-built client must be closed exactly once",
@@ -112,9 +111,9 @@ public void testCloseFailureDoesNotMaskOriginalError() throws Exception {
112111
try {
113112
invokeBuildAndConnect(sender);
114113
Assert.fail("a JVM Error must propagate out of buildAndConnect");
115-
} catch (InvocationTargetException ite) {
114+
} catch (OutOfMemoryError e) {
116115
Assert.assertSame("close() failure must not mask the original Error",
117-
oom, ite.getCause());
116+
oom, e);
118117
}
119118
Assert.assertEquals("close must have been attempted", 1, stub.closeCalls);
120119
}
@@ -141,8 +140,8 @@ public void testErrorInSuccessTailClosesConnectedClient() throws Exception {
141140
try {
142141
invokeBuildAndConnect(sender);
143142
Assert.fail("a JVM Error must propagate out of buildAndConnect");
144-
} catch (InvocationTargetException ite) {
145-
Assert.assertSame("the original Error must surface", oom, ite.getCause());
143+
} catch (OutOfMemoryError e) {
144+
Assert.assertSame("the original Error must surface", oom, e);
146145
}
147146
Assert.assertEquals("Error must stop the walk on the first attempt", 1, built.size());
148147
Assert.assertEquals("connected client must be closed exactly once",
@@ -169,9 +168,9 @@ public void testSuccessTailCloseFailureDoesNotMaskOriginalError() throws Excepti
169168
try {
170169
invokeBuildAndConnect(sender);
171170
Assert.fail("a JVM Error must propagate out of buildAndConnect");
172-
} catch (InvocationTargetException ite) {
171+
} catch (OutOfMemoryError e) {
173172
Assert.assertSame("close() failure must not mask the original Error",
174-
oom, ite.getCause());
173+
oom, e);
175174
}
176175
Assert.assertEquals("close must have been attempted", 1, stub.closeCalls);
177176
}
@@ -194,8 +193,8 @@ public void testErrorAtSuccessTailEntryClosesConnectedClient() throws Exception
194193
try {
195194
invokeBuildAndConnect(sender);
196195
Assert.fail("a JVM Error must propagate out of buildAndConnect");
197-
} catch (InvocationTargetException ite) {
198-
Assert.assertSame("the original Error must surface", oom, ite.getCause());
196+
} catch (OutOfMemoryError e) {
197+
Assert.assertSame("the original Error must surface", oom, e);
199198
}
200199
Assert.assertEquals("connected client must be closed exactly once",
201200
1, stub.closeCalls);
@@ -222,9 +221,8 @@ public void testExceptionPathStillClosesAndWalksAllEndpoints() throws Exception
222221
try {
223222
invokeBuildAndConnect(sender);
224223
Assert.fail("an exhausted round must surface LineSenderException");
225-
} catch (InvocationTargetException ite) {
226-
Assert.assertTrue("expected LineSenderException, got " + ite.getCause(),
227-
ite.getCause() instanceof LineSenderException);
224+
} catch (LineSenderException expected) {
225+
// expected: the walk exhausted the round and surfaced the failure
228226
}
229227
Assert.assertEquals("an Exception must keep the walk going", 2, built.size());
230228
for (StubClient c : built) {
@@ -269,20 +267,16 @@ private static void installFactory(QwpWebSocketSender sender,
269267
}
270268

271269
/**
272-
* Drives the private connect walk through its private foreground
273-
* {@code ReconnectSupplier} (no-arg: abortCheck null means foreground;
274-
* the bare sender's null {@code cursorSendLoop} and false {@code closed}
275-
* make {@code isAborted()} false).
270+
* Drives the private connect walk through its foreground
271+
* {@code ReconnectSupplier}, obtained via the public production accessor
272+
* {@link QwpWebSocketSender#newReconnectFactory()} (no abort gate: the
273+
* bare sender's null {@code cursorSendLoop} and false {@code closed}
274+
* make {@code isAborted()} false). {@code ReconnectFactory.reconnect()}
275+
* is a one-line delegation to {@code buildAndConnect(this)}, so failures
276+
* surface unwrapped.
276277
*/
277278
private static void invokeBuildAndConnect(QwpWebSocketSender sender) throws Exception {
278-
Class<?> supplierClass = Class.forName(
279-
"io.questdb.client.cutlass.qwp.client.QwpWebSocketSender$ReconnectSupplier");
280-
Constructor<?> ctor = supplierClass.getDeclaredConstructor(QwpWebSocketSender.class);
281-
ctor.setAccessible(true);
282-
Object ctx = ctor.newInstance(sender);
283-
Method m = QwpWebSocketSender.class.getDeclaredMethod("buildAndConnect", supplierClass);
284-
m.setAccessible(true);
285-
m.invoke(sender, ctx);
279+
sender.newReconnectFactory().reconnect();
286280
}
287281

288282
private static StubClient newStubClient() {

0 commit comments

Comments
 (0)