Files
Termix/src/backend/tests/database/routes/host-bulk-routes.test.ts
T
62ac931468 v26.10.1 (#1599)
* feat: AI translations at release, 7 new languages and regional fallback

* fix: proper plural forms for count strings and full translations

* fix: plural forms for selected, hidden and active counts

* fix: synced plugin updates failing on desktop and server-only plugins shown as stopped

* fix: restore rounded corners on app icons and favicon

* feat: desktop app auto updates with automatic, ask first or never modes

* feat: community and custom plugin registries

* feat: plugin guide for AI tools in the SDK with a lint check

* fix: keep the terminal WebSocket open past nginx's 60s default (#1588)

* chore: update changelog

* feat: show plugin download counts only and stop CI builds inflating them

* feat: hide installed plugins in the browse tab by default

* chore: pin bundled plugins to latest releases

* chore: update translations

* fix: focusing a saved password field and saving no longer erases it

* fix: tell desktop users when their 2.8 data needs 2.9 first

* fix: point windows update info at the x64 installer

* fix: keep desktop client tunnel sockets alive behind proxies

* chore: pin bundled plugins to latest releases

* fix: cover client tunnel keepalive in the c2s runtime tests

* fix: typing after opening a host no longer goes to the host search

* fix: accept tab and = separators in SSH config import (#1598)

* chore: update changelog

* chore: drop plugin versions from core changelog

* chore: update translations

* chore: lint, format, and bump version to 26.10.1

---------

Co-authored-by: Darnel Hunter <68697450+innotelinc@users.noreply.github.com>
Co-authored-by: Iain <Iain1970@protonmail.com>
2026-10-12 11:35:48 -05:00

156 lines
4.0 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
importedHostUsername,
notifyImportedHosts,
parseSSHConfig,
} from "../../../database/routes/host-bulk-routes.js";
import { pluginEvents, TOPICS } from "../../../plugins/events.js";
describe("parseSSHConfig", () => {
it("parses a basic Host block", () => {
const config = `
Host myserver
HostName 192.168.1.10
User alice
Port 2222
`;
const result = parseSSHConfig(config);
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
name: "myserver",
hostname: "192.168.1.10",
user: "alice",
port: 2222,
});
});
it("parses multiple Host blocks", () => {
const config = `
Host web
HostName web.example.com
User deploy
Host db
HostName db.example.com
User postgres
Port 5432
`;
const result = parseSSHConfig(config);
expect(result).toHaveLength(2);
expect(result[0].name).toBe("web");
expect(result[1].name).toBe("db");
expect(result[1].port).toBe(5432);
});
it("ignores comment lines", () => {
const config = `
# This is a comment
Host server
# Another comment
HostName 10.0.0.1
User root
`;
const result = parseSSHConfig(config);
expect(result).toHaveLength(1);
expect(result[0].hostname).toBe("10.0.0.1");
});
it("skips wildcard Host entries", () => {
const config = `
Host *
ServerAliveInterval 60
Host prod
HostName prod.example.com
User ubuntu
`;
const result = parseSSHConfig(config);
expect(result).toHaveLength(1);
expect(result[0].name).toBe("prod");
});
it("captures IdentityFile and ProxyJump", () => {
const config = `
Host bastion
HostName bastion.example.com
User ec2-user
IdentityFile ~/.ssh/id_rsa
ProxyJump jumphost.example.com
`;
const result = parseSSHConfig(config);
expect(result).toHaveLength(1);
expect(result[0].identityFile).toBe("~/.ssh/id_rsa");
expect(result[0].proxyJump).toBe("jumphost.example.com");
});
it("skips Host blocks without a HostName", () => {
const config = `
Host alias-only
User foo
`;
const result = parseSSHConfig(config);
expect(result).toHaveLength(0);
});
it("accepts tabs and = between keywords and values", () => {
const config = "Host\tweb\n\tHostName=10.0.0.5\n\tUser = deploy\n";
expect(parseSSHConfig(config)).toEqual([
{ name: "web", hostname: "10.0.0.5", user: "deploy" },
]);
});
it("defaults port to undefined when not specified", () => {
const config = `
Host server
HostName 1.2.3.4
User root
`;
const result = parseSSHConfig(config);
expect(result[0].port).toBeUndefined();
});
it("returns empty array for empty input", () => {
expect(parseSSHConfig("")).toHaveLength(0);
expect(parseSSHConfig(" \n\n ")).toHaveLength(0);
});
});
describe("importedHostUsername", () => {
it("lets credential-backed SSH hosts inherit the credential username", () => {
expect(importedHostUsername("ssh", "credential", "")).toBe("");
});
it("keeps requiring usernames for other SSH authentication modes", () => {
expect(importedHostUsername("ssh", "password", "")).toBeNull();
expect(importedHostUsername("ssh", "key", undefined)).toBeNull();
});
it("always returns a non-null database value for non-SSH hosts", () => {
expect(importedHostUsername("rdp", "password", undefined)).toBe("");
});
it("preserves an explicitly configured host username", () => {
expect(importedHostUsername("ssh", "credential", "guest-admin")).toBe(
"guest-admin",
);
});
});
describe("notifyImportedHosts", () => {
it("sends a host update for every imported host", () => {
const seen: unknown[] = [];
const off = pluginEvents.on(TOPICS.hostUpdated, (payload) => {
seen.push(payload);
});
try {
notifyImportedHosts("user-1", [4, 7]);
} finally {
off();
}
expect(seen).toEqual([
{ hostId: 4, userId: "user-1" },
{ hostId: 7, userId: "user-1" },
]);
});
});