From 6f21d926d70aaee50c9ae9480c9cbba883a98739 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 16 Jun 2026 17:32:08 -0400 Subject: [PATCH] test(java-cli): cover galaxy-discover/galaxy-watch over in-process harness --- .../ww/mxgateway/cli/MxGatewayCliTests.java | 127 +++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/clients/java/zb-mom-ww-mxgateway-cli/src/test/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCliTests.java b/clients/java/zb-mom-ww-mxgateway-cli/src/test/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCliTests.java index 70a0f10..8a40781 100644 --- a/clients/java/zb-mom-ww-mxgateway-cli/src/test/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCliTests.java +++ b/clients/java/zb-mom-ww-mxgateway-cli/src/test/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCliTests.java @@ -5,8 +5,8 @@ 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.DeployEvent; import galaxy_repository.v1.GalaxyRepositoryOuterClass.GalaxyObject; import io.grpc.stub.StreamObserver; import java.io.ByteArrayInputStream; @@ -749,6 +749,89 @@ final class MxGatewayCliTests { } } + // ---- galaxy-discover / galaxy-watch over the in-process harness (Task 6) ---- + + @Test + void galaxyDiscoverPrintsPagedHierarchyJson() { + // Drive the REAL GalaxyRepositoryClient.discoverHierarchy path over the + // in-process harness (Task 6), so the production galaxy-discover command + // exercises the real client paging loop against scripted objects instead + // of a hand-written seam. The harness's fake discoverHierarchy returns a + // single page carrying the scripted objects. + GalaxyObject area = GalaxyObject.newBuilder() + .setGobjectId(101) + .setTagName("Area001") + .setContainedName("Area001") + .setBrowseName("Area001") + .setIsArea(true) + .build(); + GalaxyObject pump = GalaxyObject.newBuilder() + .setGobjectId(202) + .setTagName("Pump001") + .setContainedName("Pump001") + .setBrowseName("Pump001") + .setParentGobjectId(101) + .build(); + + try (InProcessGatewayHarness harness = new InProcessGatewayHarness()) { + harness.setScriptedObjects(List.of(area, pump)); + CliRun run = executeGalaxy(new HarnessGalaxyClientFactory(harness), "galaxy-discover", "--json"); + + assertEquals(0, run.exitCode(), "errors:\n" + run.errors()); + String out = run.output(); + // galaxy-discover --json renders {"command":..,"options":..,"objects":[{galaxyObjectMap}]}. + assertTrue(out.contains("\"command\":\"galaxy-discover\""), out); + // Both scripted objects render with the flattened object fields. + assertTrue(out.contains("\"tagName\":\"Area001\""), out); + assertTrue(out.contains("\"tagName\":\"Pump001\""), out); + assertTrue(out.contains("\"gobjectId\":101"), out); + assertTrue(out.contains("\"gobjectId\":202"), out); + assertTrue(out.contains("\"parentGobjectId\":101"), out); + assertTrue(out.contains("\"isArea\":true"), out); + } + } + + @Test + void galaxyWatchRendersScriptedDeployEvents() { + // Drive the REAL GalaxyRepositoryClient.watchDeployEvents / DeployEventStream + // path over the in-process harness. The harness's fake watchDeployEvents + // streams the scripted deploy events then completes; the CLI's --limit + // option caps how many it prints before closing the stream. + DeployEvent first = DeployEvent.newBuilder() + .setSequence(7L) + .setObjectCount(12) + .setAttributeCount(34) + .build(); + DeployEvent second = DeployEvent.newBuilder() + .setSequence(8L) + .setObjectCount(13) + .setAttributeCount(35) + .build(); + DeployEvent third = DeployEvent.newBuilder() + .setSequence(9L) + .setObjectCount(14) + .setAttributeCount(36) + .build(); + + try (InProcessGatewayHarness harness = new InProcessGatewayHarness()) { + harness.setScriptedDeployEvents(List.of(first, second, third)); + // --limit 2 caps the feed at the first two scripted events. + CliRun run = executeGalaxy( + new HarnessGalaxyClientFactory(harness), "galaxy-watch", "--limit", "2", "--json"); + + assertEquals(0, run.exitCode(), "errors:\n" + run.errors()); + String out = run.output(); + // galaxy-watch --json prints one proto-JSON object per event; proto3 + // JSON renders uint64 sequence as a decimal string. + assertTrue(out.contains("\"sequence\":\"7\""), out); + assertTrue(out.contains("\"objectCount\":12"), out); + assertTrue(out.contains("\"attributeCount\":34"), out); + assertTrue(out.contains("\"sequence\":\"8\""), out); + // --limit 2 must stop before printing the third scripted event. + assertFalse(out.contains("\"sequence\":\"9\""), out); + } + } + @Test void batchCommandExecutesVersionAndEmitsEorMarker() { CliRun run = executeBatch(new FakeClientFactory(), "version --json\n"); @@ -863,6 +946,24 @@ final class MxGatewayCliTests { return new CliRun(exitCode, output.toString(), errors.toString()); } + /** + * Runs a galaxy subcommand against the supplied {@link + * MxGatewayCli.GalaxyClientFactory}, wiring it through the production + * {@code commandLine(gatewayFactory, galaxyFactory)} two-arg overload (Task 3 + * seam). The gateway factory slot is unused by galaxy commands, so a plain + * {@link FakeClientFactory} fills it. Mirrors {@link #execute} for the + * gateway commands. + */ + private static CliRun executeGalaxy(MxGatewayCli.GalaxyClientFactory galaxyFactory, String... args) { + StringWriter output = new StringWriter(); + StringWriter errors = new StringWriter(); + picocli.CommandLine commandLine = MxGatewayCli.commandLine(new FakeClientFactory(), galaxyFactory); + commandLine.setOut(new PrintWriter(output, true)); + commandLine.setErr(new PrintWriter(errors, true)); + int exitCode = commandLine.execute(args); + return new CliRun(exitCode, output.toString(), errors.toString()); + } + private record CliRun(int exitCode, String output, String errors) { } @@ -907,7 +1008,8 @@ final class MxGatewayCliTests { /** * Factory that wires the production {@link MxGatewayCli.GrpcMxGatewayCliClient} - * adapter around the harness's REAL {@link MxGatewayClient}, so the + * adapter around the harness's REAL + * {@link com.zb.mom.ww.mxgateway.client.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 @@ -927,6 +1029,27 @@ final class MxGatewayCliTests { } } + /** + * Galaxy factory that returns the harness's REAL {@link + * com.zb.mom.ww.mxgateway.client.GalaxyRepositoryClient} over the in-process + * scripted {@code GalaxyRepository} service, so galaxy-discover / galaxy-watch + * exercise the real client (paging loop, deploy-event stream wrapper) against + * scripted payloads. Mirrors the production {@code GrpcGalaxyClientFactory}, + * swapping only client construction for the harness-backed client. + */ + private static final class HarnessGalaxyClientFactory implements MxGatewayCli.GalaxyClientFactory { + private final InProcessGatewayHarness harness; + + private HarnessGalaxyClientFactory(InProcessGatewayHarness harness) { + this.harness = harness; + } + + @Override + public com.zb.mom.ww.mxgateway.client.GalaxyRepositoryClient connect(MxGatewayClientOptions options) { + return harness.galaxyClient(); + } + } + private static final class OverflowingFakeClient implements MxGatewayCli.MxGatewayCliClient { private final PrintWriter out;