merge(application,backend,web-server,app-tauri,frontend): fix #369 — libère l'état busy avant le Final visible (QA verte)
This commit is contained in:
@ -1780,6 +1780,19 @@ describe("CustomAgentChatView", () => {
|
||||
nativeEvent: "turn.completed",
|
||||
},
|
||||
});
|
||||
workState._setProjectWorkState("project-1", {
|
||||
agents: [
|
||||
{
|
||||
agentId: "agent-1",
|
||||
name: "Worker",
|
||||
profileId: "structured",
|
||||
busy: { state: "idle" },
|
||||
tickets: [],
|
||||
},
|
||||
],
|
||||
conversations: [],
|
||||
});
|
||||
system.emit({ type: "agentBusyChanged", agentId: "agent-1", busy: false });
|
||||
onChunk({ kind: "final", content: "Next answer" });
|
||||
}),
|
||||
stopLiveAgent: vi.fn(),
|
||||
@ -6324,6 +6337,81 @@ describe("CustomAgentChatView", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("waits for canonical work-state idle after Final before draining the next queued message in FIFO order (#369)", async () => {
|
||||
const harness = createStreamingAgentHarness();
|
||||
const system = new MockSystemGateway();
|
||||
const workState = new MockWorkStateGateway();
|
||||
const stateFor = (busy: boolean) => ({
|
||||
agents: [
|
||||
{
|
||||
agentId: "agent-1",
|
||||
name: "Worker",
|
||||
profileId: "structured",
|
||||
busy: busy
|
||||
? ({ state: "busy", ticket: "turn-369", sinceMs: 1 } as const)
|
||||
: ({ state: "idle" } as const),
|
||||
tickets: [],
|
||||
},
|
||||
],
|
||||
conversations: [],
|
||||
});
|
||||
workState._setProjectWorkState("project-1", stateFor(false));
|
||||
|
||||
render(
|
||||
<DIProvider
|
||||
gateways={{ agent: harness.agent, system, workState } as unknown as Gateways}
|
||||
>
|
||||
<CustomAgentChatView
|
||||
projectId="project-1"
|
||||
agentId="agent-1"
|
||||
agentName="Worker"
|
||||
profile={profile}
|
||||
cwd="/repo"
|
||||
layoutId="layout-1"
|
||||
nodeId="node-1"
|
||||
sessionId="chat-session-1"
|
||||
conversationId="conversation-1"
|
||||
onSessionId={vi.fn()}
|
||||
onConversationId={vi.fn()}
|
||||
/>
|
||||
</DIProvider>,
|
||||
);
|
||||
|
||||
const composer = await screen.findByLabelText(/Message CLI personnalisée/);
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Envoyer" })).toBeTruthy());
|
||||
fireEvent.change(composer, { target: { value: "active request" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||
await waitFor(() => expect(harness.sendAgentChat).toHaveBeenCalledTimes(1));
|
||||
|
||||
fireEvent.change(composer, { target: { value: "second queued" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Mettre en attente" }));
|
||||
fireEvent.change(composer, { target: { value: "third queued" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Mettre en attente" }));
|
||||
expect(screen.getByRole("button", { name: "En attente · 2" })).toBeTruthy();
|
||||
|
||||
workState._setProjectWorkState("project-1", stateFor(true));
|
||||
act(() => system.emit({ type: "agentBusyChanged", agentId: "agent-1", busy: true }));
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Mettre en attente" })).toBeTruthy());
|
||||
|
||||
act(() => harness.emit(0, { kind: "final", content: "first final" }));
|
||||
await screen.findByText("first final");
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Mettre en attente" })).toBeTruthy());
|
||||
expect(harness.sendAgentChat).toHaveBeenCalledTimes(1);
|
||||
expect(screen.getByRole("button", { name: "En attente · 2" })).toBeTruthy();
|
||||
|
||||
workState._setProjectWorkState("project-1", stateFor(false));
|
||||
act(() => system.emit({ type: "agentBusyChanged", agentId: "agent-1", busy: false }));
|
||||
|
||||
await waitFor(() => expect(harness.sendAgentChat).toHaveBeenCalledTimes(2));
|
||||
expect(harness.sendAgentChat).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
"chat-session-1",
|
||||
"second queued",
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(screen.getByRole("button", { name: "En attente · 1" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("edits, deletes, restores and clears queued messages while Stop preserves the queue", async () => {
|
||||
const harness = createStreamingAgentHarness();
|
||||
const cancelAgentChat = vi.fn(async () => ({
|
||||
|
||||
Reference in New Issue
Block a user