Files
paseo/packages/app/src/components/direct-connection-host.test.ts
T
QTomandClaude Opus 5.5 2dd931c8bd fix(app): accept host:port and URLs in the direct connection host field (#8223)
The direct connection form treated any host containing a colon as a bare
IPv6 address, so entering "100.66.69.36:6767" or "tcp://host:6767" built
"tcp://[100.66.69.36:6767]:6767" and surfaced the browser's raw "Invalid URL"
error. Normalize the host input first: strip a leading scheme and any
path/query/fragment, split "host:port" (the inline port wins), and only treat
unbracketed hosts with two or more colons as IPv6.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 18:06:12 +00:00

69 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { buildDirectConnectionUri, normalizeDirectConnectionHost } from "./direct-connection-host";
const labels = {
hostRequired: "host required",
invalidPort: "invalid port",
};
function buildUri(host: string, port = "6767"): string {
return buildDirectConnectionUri({ host, port, useTls: false, password: "" }, labels);
}
describe("normalizeDirectConnectionHost", () => {
it.each([
["100.66.69.36", { host: "100.66.69.36", port: null, isIpv6: false }],
[" 100.66.69.36 ", { host: "100.66.69.36", port: null, isIpv6: false }],
["100.66.69.36:6767", { host: "100.66.69.36", port: "6767", isIpv6: false }],
["tcp://100.66.69.36:6767", { host: "100.66.69.36", port: "6767", isIpv6: false }],
["http://100.66.69.36:6767/path?x=1#y", { host: "100.66.69.36", port: "6767", isIpv6: false }],
["HTTPS://example.test", { host: "example.test", port: null, isIpv6: false }],
["my-host.local:7000", { host: "my-host.local", port: "7000", isIpv6: false }],
["[::1]", { host: "::1", port: null, isIpv6: true }],
["[::1]:7000", { host: "::1", port: "7000", isIpv6: true }],
["::1", { host: "::1", port: null, isIpv6: true }],
["fe80::1", { host: "fe80::1", port: null, isIpv6: true }],
["", { host: "", port: null, isIpv6: false }],
])("normalizes %j", (input, expected) => {
expect(normalizeDirectConnectionHost(input)).toEqual(expected);
});
});
describe("buildDirectConnectionUri", () => {
it.each([
["100.66.69.36", "tcp://100.66.69.36:6767"],
["100.66.69.36:6767", "tcp://100.66.69.36:6767"],
["tcp://100.66.69.36:6767", "tcp://100.66.69.36:6767"],
["http://100.66.69.36:6767/path?x", "tcp://100.66.69.36:6767"],
["my-host.local:6767", "tcp://my-host.local:6767"],
["[::1]", "tcp://[::1]:6767"],
["[::1]:7000", "tcp://[::1]:7000"],
["::1", "tcp://[::1]:6767"],
["fe80::1", "tcp://[fe80::1]:6767"],
])("builds a tcp URI from host %j", (host, expected) => {
expect(buildUri(host)).toBe(expected);
});
it("lets a port typed in the host field override the port field", () => {
expect(buildUri("100.66.69.36:7000", "6767")).toBe("tcp://100.66.69.36:7000");
});
it("keeps TLS and password settings", () => {
expect(
buildDirectConnectionUri(
{ host: "tcp://example.test:443", port: "6767", useTls: true, password: "secret" },
labels,
),
).toBe("tcp://example.test:443?ssl=true&password=secret");
});
it.each(["", " ", "tcp://", "http:///path"])("requires a host for %j", (host) => {
expect(() => buildUri(host)).toThrow("host required");
});
it("rejects an invalid port", () => {
expect(() => buildUri("100.66.69.36", "70000")).toThrow("invalid port");
expect(() => buildUri("100.66.69.36:0")).toThrow("invalid port");
});
});