feat(worker): remove a queued interactive message
StreamingClaudeSession.RemoveQueuedAsync drops the first occurrence of a queued message from _pending and re-broadcasts the updated queue. Wired through InteractiveSessionService + WorkerHub.RemoveQueuedInteractiveMessage + IWorkerClient.RemoveQueuedInteractiveMessageAsync. Removal by text (first match) is robust to a turn flushing mid-click. Fakes + ILiveSession impls updated.
This commit is contained in:
@@ -403,4 +403,67 @@ public sealed class StreamingClaudeSessionTests
|
||||
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// RemoveQueuedAsync tests
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveQueued_RemovesFirstOccurrence_SnapshotContainsOnlySecond_AndSecondDeliveredOnResult()
|
||||
{
|
||||
var transport = new FakeClaudeStreamTransport();
|
||||
var queueChanges = new List<IReadOnlyList<string>>();
|
||||
var sent = new List<string>();
|
||||
var session = BuildWithCallbacks(transport, queueChanges, sent);
|
||||
|
||||
await session.StartAsync([], "/tmp", "first", CancellationToken.None);
|
||||
sent.Clear();
|
||||
|
||||
// Enqueue two messages while turn is in flight.
|
||||
await session.SendUserMessageAsync("alpha", CancellationToken.None);
|
||||
await session.SendUserMessageAsync("beta", CancellationToken.None);
|
||||
queueChanges.Clear();
|
||||
|
||||
// Remove "alpha" from the queue.
|
||||
await session.RemoveQueuedAsync("alpha", CancellationToken.None);
|
||||
|
||||
// Snapshot emitted and contains only "beta".
|
||||
Assert.Single(queueChanges);
|
||||
Assert.Equal(new[] { "beta" }, queueChanges[0]);
|
||||
|
||||
// Push result → only "beta" is flushed, not "alpha".
|
||||
sent.Clear();
|
||||
queueChanges.Clear();
|
||||
await transport.PushLineAsync(ResultLine());
|
||||
|
||||
Assert.Single(sent);
|
||||
Assert.Equal("beta", sent[0]);
|
||||
|
||||
// Queue now empty; next result leaves us idle.
|
||||
await transport.PushLineAsync(ResultLine());
|
||||
Assert.False(session.IsTurnInFlight);
|
||||
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveQueued_NotFound_NoQueueChangedCallback()
|
||||
{
|
||||
var transport = new FakeClaudeStreamTransport();
|
||||
var queueChanges = new List<IReadOnlyList<string>>();
|
||||
var sent = new List<string>();
|
||||
var session = BuildWithCallbacks(transport, queueChanges, sent);
|
||||
|
||||
await session.StartAsync([], "/tmp", "first", CancellationToken.None);
|
||||
await session.SendUserMessageAsync("alpha", CancellationToken.None);
|
||||
queueChanges.Clear();
|
||||
|
||||
// Try to remove a message that is not in the queue.
|
||||
await session.RemoveQueuedAsync("nope", CancellationToken.None);
|
||||
|
||||
// No new snapshot emitted.
|
||||
Assert.Empty(queueChanges);
|
||||
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user