test(java-cli): cover galaxy-discover/galaxy-watch over in-process harness

This commit is contained in:
Joseph Doherty
2026-06-16 17:32:08 -04:00
parent aa8ae6613b
commit 6f21d926d7
@@ -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;