test(java-cli): cover stream-events over in-process harness

This commit is contained in:
Joseph Doherty
2026-06-16 17:09:44 -04:00
parent 4ab3bd55e5
commit 3bb4d5a082
@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.zb.mom.ww.mxgateway.client.MxGatewayAlarmFeedSubscription;
import com.zb.mom.ww.mxgateway.client.MxGatewayClient;
import com.zb.mom.ww.mxgateway.client.MxGatewayClientOptions;
import galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject;
import io.grpc.stub.StreamObserver;
@@ -31,6 +32,7 @@ import mxaccess_gateway.v1.MxaccessGateway.CloseSessionRequest;
import mxaccess_gateway.v1.MxaccessGateway.MxCommandKind;
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
import mxaccess_gateway.v1.MxaccessGateway.MxEvent;
import mxaccess_gateway.v1.MxaccessGateway.MxEventFamily;
import mxaccess_gateway.v1.MxaccessGateway.MxValue;
import mxaccess_gateway.v1.MxaccessGateway.OnAlarmTransitionEvent;
import mxaccess_gateway.v1.MxaccessGateway.OpenSessionReply;
@@ -693,6 +695,60 @@ final class MxGatewayCliTests {
+ " out=\n" + run.output() + "\nerr=\n" + run.errors());
}
@Test
void streamEventsRendersScriptedEventsIncludingHighUint64Sequence() {
// Drive the REAL MxGatewayClient / MxGatewaySession / MxEventStream
// path over the in-process harness (Task 4), so the production
// stream-events command exercises the real streaming wrapper instead
// of a hand-written FakeSession seam.
//
// The high worker-sequence (-1L == 18446744073709551615 unsigned)
// covers the unsigned-rendering regression: worker_sequence is a
// proto uint64 carried as a Java long with the top bit set. The CLI's
// --json path renders it through protobuf's JsonFormat, which prints
// uint64 as an unsigned decimal STRING; a naive %d render would print
// a negative number instead.
MxEvent dataChange = MxEvent.newBuilder()
.setFamily(MxEventFamily.MX_EVENT_FAMILY_ON_DATA_CHANGE)
.setSessionId("session-cli")
.setServerHandle(7)
.setItemHandle(42)
.setWorkerSequence(5L)
.build();
MxEvent highSequence = MxEvent.newBuilder()
.setFamily(MxEventFamily.MX_EVENT_FAMILY_OPERATION_COMPLETE)
.setSessionId("session-cli")
.setServerHandle(9)
.setItemHandle(99)
// -1L unsigned == 18446744073709551615 (top bit set).
.setWorkerSequence(-1L)
.build();
try (InProcessGatewayHarness harness = new InProcessGatewayHarness()) {
harness.setScriptedEvents(List.of(dataChange, highSequence));
CliRun run = execute(
new HarnessClientFactory(harness),
"stream-events",
"--session-id",
"session-cli",
"--json");
assertEquals(0, run.exitCode(), "errors:\n" + run.errors());
String out = run.output();
// Scripted event fields surface in the JSON render.
assertTrue(out.contains("\"family\":\"MX_EVENT_FAMILY_ON_DATA_CHANGE\""), out);
assertTrue(out.contains("\"family\":\"MX_EVENT_FAMILY_OPERATION_COMPLETE\""), out);
assertTrue(out.contains("\"serverHandle\":7"), out);
assertTrue(out.contains("\"itemHandle\":42"), out);
// The low sequence renders as the unsigned decimal string "5".
assertTrue(out.contains("\"workerSequence\":\"5\""), out);
// The high sequence renders as the FULL unsigned decimal, not -1.
assertTrue(out.contains("\"workerSequence\":\"18446744073709551615\""), out);
assertFalse(out.contains("\"workerSequence\":\"-1\""), out);
assertFalse(out.contains("\"workerSequence\":-1"), out);
}
}
@Test
void batchCommandExecutesVersionAndEmitsEorMarker() {
CliRun run = executeBatch(new FakeClientFactory(), "version --json\n");
@@ -849,6 +905,28 @@ final class MxGatewayCliTests {
}
}
/**
* Factory that wires the production {@link MxGatewayCli.GrpcMxGatewayCliClient}
* adapter around the harness's REAL {@link MxGatewayClient}, so the
* stream-events command runs against the in-process scripted gateway over
* a real channel (exercising the real {@code MxEventStream}). Mirrors the
* production {@code GrpcMxGatewayCliClientFactory}, swapping only the
* client construction for the harness-backed client.
*/
private static final class HarnessClientFactory implements MxGatewayCli.MxGatewayCliClientFactory {
private final InProcessGatewayHarness harness;
private HarnessClientFactory(InProcessGatewayHarness harness) {
this.harness = harness;
}
@Override
public MxGatewayCli.MxGatewayCliClient connect(MxGatewayCli.CommonOptions options) {
return new MxGatewayCli.GrpcMxGatewayCliClient(
harness.gatewayClient(), options.spec.commandLine().getOut());
}
}
private static final class OverflowingFakeClient implements MxGatewayCli.MxGatewayCliClient {
private final PrintWriter out;