From 1f4b1ff6a4ba6f1ffee9dc8c5c3ae83e958922f8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Wed, 30 Sep 2026 22:49:13 +0800 Subject: [PATCH] fix(hosts): preserve recording and sharing settings in the dev editor (#1509) --- .../tests/backend/service.test.ts | 46 ++++++++++++++ src/ui/sidebar/HostManagerData.ts | 1 - .../sidebar/host-recording-settings.test.tsx | 63 +++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/ui/tests/sidebar/host-recording-settings.test.tsx diff --git a/plugins/session-recording/tests/backend/service.test.ts b/plugins/session-recording/tests/backend/service.test.ts index 5f3b03004..5b8522729 100644 --- a/plugins/session-recording/tests/backend/service.test.ts +++ b/plugins/session-recording/tests/backend/service.test.ts @@ -96,6 +96,52 @@ describe("recordings.writer", () => { }); expect(sink).toBeNull(); + expect(await writer.enabledFor(7)).toBe(false); + expect(await fs.readdir(dataDir)).toEqual([]); + expect( + db!.sqlite + .prepare( + "SELECT COUNT(*) AS count FROM p_session_recording_session_recordings", + ) + .get(), + ).toEqual({ count: 0 }); + }); + + it("keeps a saved off switch after recreating the writer without affecting another host", async () => { + const { mock } = await setup(); + db!.sqlite.prepare("INSERT INTO ssh_data (id) VALUES (?)").run(8); + await mock.ctx.settings.setHost(7, "enableSessionRecording", false); + await mock.ctx.settings.setHost(8, "enableSessionRecording", true); + const table = await db!.database.define(sessionRecordings); + const repository = createSessionRecordingRepository( + db!.database, + table, + mock.ctx.hosts, + ); + const reopened = createRecordingsWriter(mock.ctx, repository); + for (const hostId of [7, 8]) { + const sink = await reopened.open({ + sessionId: `host-${hostId}`, + hostId, + userId: "user-1", + protocol: "ssh", + format: "asciicast", + startedAt: Date.now(), + }); + if (hostId === 7) expect(sink).toBeNull(); + else { + expect(sink).not.toBeNull(); + await sink!.append("marker\n"); + } + } + expect( + await fs.readdir(path.join(dataDir, "session_logs", "user-1")), + ).toEqual(["host-8.cast"]); + expect( + db!.sqlite + .prepare("SELECT host_id FROM p_session_recording_session_recordings") + .all(), + ).toEqual([{ host_id: 8 }]); }); it("writes the first append with writeFile and later ones with appendFile", async () => { diff --git a/src/ui/sidebar/HostManagerData.ts b/src/ui/sidebar/HostManagerData.ts index 3fa07c728..e4235f205 100644 --- a/src/ui/sidebar/HostManagerData.ts +++ b/src/ui/sidebar/HostManagerData.ts @@ -91,7 +91,6 @@ export function sshHostToHost(h: SSHHostWithStatus): Host { statusCheckEnabled: h.statusCheckEnabled !== false, statusCheckInterval: h.statusCheckInterval ?? null, forceKeyboardInteractive: h.forceKeyboardInteractive ?? false, - pluginSettings: h.pluginSettings, useSocks5: h.useSocks5, socks5Host: h.socks5Host, socks5Port: h.socks5Port, diff --git a/src/ui/tests/sidebar/host-recording-settings.test.tsx b/src/ui/tests/sidebar/host-recording-settings.test.tsx new file mode 100644 index 000000000..b9b0f73d4 --- /dev/null +++ b/src/ui/tests/sidebar/host-recording-settings.test.tsx @@ -0,0 +1,63 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import type { PluginSummary } from "@/api/plugins-api"; +import { sshHostToHost } from "@/sidebar/HostManagerData"; +import { createHostEditorForm } from "@/sidebar/HostEditorData"; +import { HostPluginSections } from "@/settings/HostPluginSections"; +import recording from "../../../../plugins/session-recording/manifest.json"; +import sharing from "../../../../plugins/session-sharing/manifest.json"; + +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); +afterEach(cleanup); + +describe.each([ + { manifest: recording, key: "enableSessionRecording" }, + { manifest: sharing, key: "allowSessionSharing" }, +])("$key after reopening a host", ({ manifest, key }) => { + it.each([false, true])( + "preserves the saved switch %s in the editor and its next change", + (saved) => { + const pluginSettings = { + "session-recording": { enableSessionRecording: saved }, + "session-sharing": { allowSessionSharing: saved }, + "ssh-terminal": { enableCommandHistory: false }, + }; + const host = sshHostToHost({ + id: 1, + name: "server", + ip: "192.0.2.1", + port: 22, + username: "alice", + folder: "", + tags: [], + pin: false, + authType: "password", + status: "online", + createdAt: "2026-09-29T00:00:00Z", + updatedAt: "2026-09-29T00:00:00Z", + pluginSettings, + }); + const form = createHostEditorForm(host); + const setValue = vi.fn(); + render( + , + ); + fireEvent.click(screen.getByRole("button")); + expect(setValue).toHaveBeenCalledWith(manifest.id, key, !saved); + // These are the values HostEditor writes through each plugin's host settings API. + expect(form.pluginSettings).toEqual(pluginSettings); + }, + ); +});