feat: move terminal settings, look and ssh tools into ssh-terminal plugin

This commit is contained in:
LukeGus committed 2026-09-29 02:38:37 -05:00
1 parent ffa872ebd3
commit d9bba10612
242 files changed
+25071 -7207

No files matched your search

@@ -0,0 +1,129 @@
/**
* The SSH connection options moving out of ssh_data.terminal_config into
* the core ssh_options column, against a real SQLite database through the
* same raw SQL helpers the boot path uses.
*/
import Database from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
import { beforeEach, describe, expect, it, vi } from "vitest";
const state = vi.hoisted(() => ({ db: null as unknown }));
vi.mock("../../../database/db/index.js", () => ({ getDb: () => state.db }));
vi.mock("../../../utils/logger.js", () => ({
databaseLogger: { info: vi.fn(), warn: vi.fn() },
}));
const { runSshOptionsMigration } =
await import("../../../utils/crypto-migration/ssh-options-migration.js");
let sqlite: Database.Database;
beforeEach(() => {
sqlite = new Database(":memory:");
sqlite.exec(`
CREATE TABLE ssh_data (
id INTEGER PRIMARY KEY,
terminal_config TEXT,
ssh_options TEXT
);
`);
state.db = drizzle(sqlite);
});
function insert(id: number, terminalConfig: unknown, sshOptions?: string) {
sqlite
.prepare(
"INSERT INTO ssh_data (id, terminal_config, ssh_options) VALUES (?, ?, ?)",
)
.run(
id,
terminalConfig === null ? null : JSON.stringify(terminalConfig),
sshOptions ?? null,
);
}
function options(id: number): unknown {
const row = sqlite
.prepare("SELECT ssh_options FROM ssh_data WHERE id = ?")
.get(id) as { ssh_options: string | null };
return row.ssh_options === null ? null : JSON.parse(row.ssh_options);
}
describe("runSshOptionsMigration", () => {
it("copies only the connection options, leaving the look and old tunnel keys", async () => {
insert(1, {
theme: "dracula",
fontSize: 16,
keepaliveInterval: 30,
keepaliveCountMax: 3,
allowLegacyAlgorithms: false,
agentSocketPath: "/run/agent.sock",
agentIdentity: "work",
agentForwarding: true,
environmentVariables: [{ key: "LANG", value: "C" }],
startupSnippetId: 4,
cfAccessClientId: "old",
cfTunnelHostname: "old.example.com",
});
const result = await runSshOptionsMigration();
expect(result.hostsMoved).toBe(1);
expect(options(1)).toEqual({
keepaliveInterval: 30,
keepaliveCountMax: 3,
allowLegacyAlgorithms: false,
agentSocketPath: "/run/agent.sock",
agentIdentity: "work",
agentForwarding: true,
environmentVariables: [{ key: "LANG", value: "C" }],
});
const terminalConfig = sqlite
.prepare("SELECT terminal_config FROM ssh_data WHERE id = 1")
.get() as { terminal_config: string };
expect(JSON.parse(terminalConfig.terminal_config).keepaliveInterval).toBe(
30,
);
});
it("marks a host with no options as done and skips hosts with no config", async () => {
insert(1, { theme: "dracula" });
insert(2, null);
const result = await runSshOptionsMigration();
expect(result.hostsMoved).toBe(0);
expect(options(1)).toEqual({});
expect(options(2)).toBeNull();
});
it("changes nothing on a second run, even after the options were edited", async () => {
insert(1, { keepaliveInterval: 30 });
insert(
2,
{ keepaliveInterval: 45 },
JSON.stringify({ keepaliveCountMax: 9 }),
);
await runSshOptionsMigration();
sqlite
.prepare("UPDATE ssh_data SET ssh_options = ? WHERE id = 1")
.run(JSON.stringify({ keepaliveInterval: 90 }));
const second = await runSshOptionsMigration();
expect(second.hostsMoved).toBe(0);
expect(options(1)).toEqual({ keepaliveInterval: 90 });
expect(options(2)).toEqual({ keepaliveCountMax: 9 });
});
it("does not throw on a database without the column", async () => {
sqlite = new Database(":memory:");
sqlite.exec("CREATE TABLE ssh_data (id INTEGER PRIMARY KEY)");
state.db = drizzle(sqlite);
await expect(runSshOptionsMigration()).resolves.toEqual({
hostsMoved: 0,
});
});
});