From 5aae5c5bfd7e51150f13793b7066164fcea057a5 Mon Sep 17 00:00:00 2001 From: ss-dev-00 Date: Mon, 24 Aug 2026 14:26:25 -0700 Subject: [PATCH] test(desktop): supply the thread edit guard to the activity exclusivity harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Category:** test `#6575` made `requireThreadEditResolution` a required dependency of `useChannelAgentSessions`, which this branch's exclusivity harness predates — so after the rebase all five cases threw `requireThreadEditResolution is not a function` rather than exercising last-opened-wins. Production wiring was never affected; `ChannelScreen` supplies it from `useChannelPaneHandlers`. Rather than stub it silently, the harness takes it as an injectable so the guard's interaction with the replacement is itself covered: a refused open must leave the thread and its draft exactly as they were, with no half-applied replacement that cleared the thread before the guard turned the activity open away. Verified to fail with the guard's early return removed. Signed-off-by: ss-dev-00 Co-authored-by: Bradley Axen Signed-off-by: Bradley Axen --- ...useChannelAgentSessionExclusivity.test.mjs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/desktop/src/features/channels/ui/useChannelAgentSessionExclusivity.test.mjs b/desktop/src/features/channels/ui/useChannelAgentSessionExclusivity.test.mjs index b624a34617..066e7be605 100644 --- a/desktop/src/features/channels/ui/useChannelAgentSessionExclusivity.test.mjs +++ b/desktop/src/features/channels/ui/useChannelAgentSessionExclusivity.test.mjs @@ -36,7 +36,10 @@ const OTHER_THREAD_HEAD_ID = "thread-head-2"; * Last-opened-wins is a property of these two handlers clearing each other's * state, so the assertions are on the state writes, not on a rendered surface. */ -async function renderAgentSessionHandlers({ openThreadHeadId = null } = {}) { +async function renderAgentSessionHandlers({ + openThreadHeadId = null, + requireThreadEditResolution = () => true, +} = {}) { const React = await import("react"); const { act, renderHook } = await import("@testing-library/react"); const { useChannelAgentSessions } = await import( @@ -82,6 +85,7 @@ async function renderAgentSessionHandlers({ openThreadHeadId = null } = {}) { openAgentSessionPubkey: state.openAgentSessionPubkey, openThreadHeadId: state.openThreadHeadId, profilePanelPubkey: state.profilePanelPubkey, + requireThreadEditResolution, setChannelManagementOpen: (open) => write("channelManagementOpen", open), setExpandedThreadReplyIds: () => {}, setOpenAgentSessionChannelId: (value) => @@ -175,3 +179,19 @@ test("back never resurrects a thread from an earlier replacement", async () => { assert.equal(state.openThreadHeadId, OTHER_THREAD_HEAD_ID); }); + +test("an unresolved thread edit blocks the replacement entirely", async () => { + // Last-opened-wins runs *after* the thread edit guard `#6575` added, so a + // refused open must leave both surfaces exactly as they were — the thread + // still on screen with its draft, and no half-applied replacement that + // cleared the thread before the guard turned the activity open away. + const { run, state } = await renderAgentSessionHandlers({ + openThreadHeadId: THREAD_HEAD_ID, + requireThreadEditResolution: () => false, + }); + + await run((handlers) => handlers.openAgentSession(AGENT_PUBKEY, "channel-1")); + + assert.equal(state.openAgentSessionPubkey, null); + assert.equal(state.openThreadHeadId, THREAD_HEAD_ID); +});