fix(java): picocli ParameterException for browse --depth; warn on --parent 0

Replaces the raw IllegalArgumentException thrown by GalaxyBrowseCommand for
--depth < 0 with a CommandLine.ParameterException so picocli surfaces a clean
single-line error instead of an unhandled stack trace. Adds an upper bound of
50 (matching the Python client) so --depth > 50 is also rejected cleanly.

Emits a stderr warning when --parent 0 is supplied explicitly, matching
Go/Rust client behaviour, because gobject id 0 is the server's root-walk
sentinel and passing it via --parent is almost always a mistake.

Adds three new tests: negative depth, depth > 50, and the --parent 0 warning path.
This commit is contained in:
Joseph Doherty
2026-06-15 11:08:07 -04:00
parent 0d5b488c11
commit b298ca74be
5 changed files with 104 additions and 77 deletions
@@ -207,7 +207,7 @@ public sealed class FakeWorkerHarnessTests
CreateCommand(MxCommandKind.Ping, cmd => cmd.Ping = new PingCommand { Message = "hello-ping" }),
TestTimeout,
CancellationToken.None);
await fakeWorker.RespondToControlCommandAsync();
await fakeWorker.RespondToControlCommandAsync().WaitAsync(TestTimeout);
WorkerCommandReply reply = await invokeTask.WaitAsync(TestTimeout);
@@ -231,7 +231,7 @@ public sealed class FakeWorkerHarnessTests
CreateCommand(MxCommandKind.GetSessionState),
TestTimeout,
CancellationToken.None);
await fakeWorker.RespondToControlCommandAsync();
await fakeWorker.RespondToControlCommandAsync().WaitAsync(TestTimeout);
WorkerCommandReply reply = await invokeTask.WaitAsync(TestTimeout);
@@ -256,7 +256,7 @@ public sealed class FakeWorkerHarnessTests
CreateCommand(MxCommandKind.GetWorkerInfo),
TestTimeout,
CancellationToken.None);
await fakeWorker.RespondToControlCommandAsync();
await fakeWorker.RespondToControlCommandAsync().WaitAsync(TestTimeout);
WorkerCommandReply reply = await invokeTask.WaitAsync(TestTimeout);
@@ -265,7 +265,8 @@ public sealed class FakeWorkerHarnessTests
Assert.NotNull(reply.Reply.WorkerInfo);
Assert.Equal(FakeWorkerHarness.DefaultWorkerProcessId, reply.Reply.WorkerInfo.WorkerProcessId);
Assert.Equal("LMXProxy.LMXProxyServer.1", reply.Reply.WorkerInfo.MxaccessProgid);
Assert.False(string.IsNullOrEmpty(reply.Reply.WorkerInfo.MxaccessClsid));
Assert.Equal("{C30B52F5-2CB5-4760-AF0A-3A344A7EB5DC}", reply.Reply.WorkerInfo.MxaccessClsid);
Assert.Equal("fake-worker", reply.Reply.WorkerInfo.WorkerVersion);
}
/// <summary>
@@ -283,7 +284,7 @@ public sealed class FakeWorkerHarnessTests
CreateCommand(MxCommandKind.DrainEvents, cmd => cmd.DrainEvents = new DrainEventsCommand { MaxEvents = 32 }),
TestTimeout,
CancellationToken.None);
await fakeWorker.RespondToControlCommandAsync();
await fakeWorker.RespondToControlCommandAsync().WaitAsync(TestTimeout);
WorkerCommandReply reply = await invokeTask.WaitAsync(TestTimeout);
@@ -314,7 +315,7 @@ public sealed class FakeWorkerHarnessTests
// The harness reads the ShutdownWorker WorkerCommand and replies with
// OK + ShutdownAck — the WorkerClient's read loop processes the ack and
// transitions to Closed.
await fakeWorker.RespondToControlCommandAsync();
await fakeWorker.RespondToControlCommandAsync().WaitAsync(TestTimeout);
WorkerCommandReply reply = await invokeTask.WaitAsync(TestTimeout);