diff --git a/ui/src/pages/chat/chat-pane.test.ts b/ui/src/pages/chat/chat-pane.test.ts index c796e105c2c4..58d8d556ad61 100644 --- a/ui/src/pages/chat/chat-pane.test.ts +++ b/ui/src/pages/chat/chat-pane.test.ts @@ -180,6 +180,85 @@ describe("chat pane header state", () => { expect(patch).not.toHaveBeenCalled(); }); + it("refuses header rename when sessions.patch is unavailable or lacks write scope", () => { + for (const hello of [ + { + auth: { role: "operator", scopes: ["operator.write"] }, + features: { methods: ["sessions.create"] }, + }, + { + auth: { role: "operator", scopes: ["operator.read"] }, + features: { methods: ["sessions.patch"] }, + }, + ]) { + const patch = vi.fn(async () => ({})); + const sessions = { patch } as unknown as SessionCapability; + const { pane, state, requestUpdate } = createTestChatPane({ + client: {} as GatewayBrowserClient, + sessions, + }); + pane.context.gateway.snapshot.hello = + hello as ApplicationContext["gateway"]["snapshot"]["hello"]; + const session = { + key: "agent:main:current", + kind: "direct", + updatedAt: 0, + } satisfies GatewaySessionRow; + + pane.beginHeaderRename(session); + + expect(pane.headerEditing).toBe(false); + expect(state.chatError).toBeTruthy(); + expect(requestUpdate).toHaveBeenCalledOnce(); + expect(patch).not.toHaveBeenCalled(); + } + }); + + it("rechecks header rename access before committing", () => { + const patch = vi.fn(async () => ({})); + const sessions = { patch } as unknown as SessionCapability; + const { pane, state } = createTestChatPane({ + client: {} as GatewayBrowserClient, + sessions, + }); + const session = { + key: "agent:main:current", + kind: "direct", + updatedAt: 0, + } satisfies GatewaySessionRow; + + pane.beginHeaderRename(session); + pane.headerRenameValue = "Blocked rename"; + pane.context.gateway.snapshot.hello = { + auth: { role: "operator", scopes: ["operator.read"] }, + features: { methods: ["sessions.patch"] }, + } as ApplicationContext["gateway"]["snapshot"]["hello"]; + pane.commitHeaderRename(); + + expect(state.chatError).toBeTruthy(); + expect(patch).not.toHaveBeenCalled(); + }); + + it("refuses archived-session restore without exact sessions.patch access", async () => { + const patch = vi.fn(async () => ({})); + const sessions = { patch } as unknown as SessionCapability; + const { pane, state, requestUpdate } = createTestChatPane({ + client: {} as GatewayBrowserClient, + sessions, + }); + pane.context.gateway.snapshot.hello = { + auth: { role: "operator", scopes: ["operator.read"] }, + features: { methods: ["sessions.patch"] }, + } as ApplicationContext["gateway"]["snapshot"]["hello"]; + + await pane.restoreArchivedSession(state.sessionKey); + + expect(state.chatError).toBeTruthy(); + expect(state.lastError).toBe(state.chatError); + expect(requestUpdate).toHaveBeenCalledOnce(); + expect(patch).not.toHaveBeenCalled(); + }); + it("copies the resolved workspace path and branch", async () => { const { pane } = createTestChatPane({ client: {} as GatewayBrowserClient, diff --git a/ui/src/pages/chat/components/chat-session-sharing.test.ts b/ui/src/pages/chat/components/chat-session-sharing.test.ts index fd30fab519ab..9485d3ab75e8 100644 --- a/ui/src/pages/chat/components/chat-session-sharing.test.ts +++ b/ui/src/pages/chat/components/chat-session-sharing.test.ts @@ -145,7 +145,7 @@ describe("chat session sharing menu", () => { loading: false, result: { sessionKey: "agent:main:main", - members: [{ identityId: "alice", addedAt: 1 }], + members: [{ identityId: "alice", addedBy: "owner", addedAt: 1 }], identities: [ { type: "human", id: "alice", label: "Alice" }, { type: "human", id: "bob", label: "Bob" }, @@ -164,9 +164,9 @@ describe("chat session sharing menu", () => { ); const dropdown = root.querySelector("wa-dropdown"); expect(dropdown).not.toBeNull(); - expect(root.querySelector('wa-dropdown-item[value="visibility:read-only"]')?.title).toBe( - "Requires write", - ); + expect( + root.querySelector('wa-dropdown-item[value="visibility:read-only"]')?.title, + ).toBe("Requires write"); expect( root.querySelector('wa-dropdown-item[value="member:alice"]')?.hasAttribute("disabled"), ).toBe(true);