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:
@@ -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);
|
||||
|
||||
|
||||
@@ -409,6 +409,37 @@ public sealed class FakeWorkerHarness : IAsyncDisposable
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
WorkerEnvelope commandEnvelope = await ReadCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
return await RespondToControlCommandAsync(commandEnvelope, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accepts an already-read command envelope and, if it is one of the five control
|
||||
/// command kinds (Ping, GetSessionState, GetWorkerInfo, DrainEvents, ShutdownWorker),
|
||||
/// writes a canned reply that mirrors the real worker's reply shape. For ShutdownWorker
|
||||
/// the method additionally sends a <see cref="WorkerShutdownAck"/> after the OK reply.
|
||||
/// Use this overload when the caller has already consumed the envelope from the pipe
|
||||
/// (e.g., to inspect the kind before routing) to avoid re-reading.
|
||||
/// </summary>
|
||||
/// <param name="commandEnvelope">The already-read command envelope to respond to.</param>
|
||||
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
||||
/// <returns>The command envelope that was handled.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="commandEnvelope"/> does not contain a <c>WorkerCommand</c>.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when the command kind is not one of the five control command kinds.
|
||||
/// </exception>
|
||||
public async Task<WorkerEnvelope> RespondToControlCommandAsync(
|
||||
WorkerEnvelope commandEnvelope,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (commandEnvelope.BodyCase != WorkerEnvelope.BodyOneofCase.WorkerCommand)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Expected WorkerCommand envelope but received {commandEnvelope.BodyCase}.",
|
||||
nameof(commandEnvelope));
|
||||
}
|
||||
|
||||
MxCommand command = commandEnvelope.WorkerCommand.Command;
|
||||
|
||||
switch (command.Kind)
|
||||
|
||||
Reference in New Issue
Block a user