feat(connection): update saved connections by ID through deep links
This commit is contained in:
1 parent
221683950d
commit
d66242508e
10 files changed
+818
-13
No files matched your search
+91
-1
@@ -1,6 +1,6 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { parseConnectionDeepLink } from "../../apps/desktop/src/lib/connection/connectionDeepLink.ts";
|
||||
import { parseConnectionDeepLink, parseConnectionDeepLinkUpdate } from "../../apps/desktop/src/lib/connection/connectionDeepLink.ts";
|
||||
|
||||
test("parses dbx connection deep link query fields", () => {
|
||||
const draft = parseConnectionDeepLink("dbx://connection/new?type=postgres&host=db.internal&port=15432&user=app&database=orders&name=Orders");
|
||||
@@ -84,3 +84,93 @@ test("ignores unsupported dbx deep link targets", () => {
|
||||
assert.equal(parseConnectionDeepLink("dbx://query/open?sql=select%201"), null);
|
||||
assert.equal(parseConnectionDeepLink("dbx://connections/new?type=postgres"), null);
|
||||
});
|
||||
|
||||
test("credential updates contain only explicit fields and do not apply new-connection defaults", () => {
|
||||
const update = parseConnectionDeepLinkUpdate("dbx://connection/new?id=saved-connection&user=new-token&password=new-token");
|
||||
|
||||
assert.deepEqual(update, {
|
||||
connectionId: "saved-connection",
|
||||
patch: { username: "new-token", password: "new-token" },
|
||||
});
|
||||
assert.deepEqual(parseConnectionDeepLinkUpdate("dbx://connection/new?id=saved-connection"), {
|
||||
connectionId: "saved-connection",
|
||||
patch: {},
|
||||
});
|
||||
});
|
||||
|
||||
test("update links preserve encoded credentials and explicit empty fields", () => {
|
||||
const link = new URL("dbx://connection/new");
|
||||
const username = " user + &=#/%中文 ";
|
||||
const password = " password + &=#/%中文\t ";
|
||||
for (const [key, value] of Object.entries({ id: "saved-connection", v: "1", name: " Orders ", host: " db.internal ", port: "15432", user: username, password, database: "", url_params: "", ssl: "false" })) {
|
||||
link.searchParams.set(key, value);
|
||||
}
|
||||
|
||||
assert.deepEqual(parseConnectionDeepLinkUpdate(link.toString()), {
|
||||
connectionId: "saved-connection",
|
||||
patch: { name: "Orders", host: "db.internal", port: 15432, username, password, database: "", urlParams: "", ssl: false },
|
||||
});
|
||||
assert.deepEqual(parseConnectionDeepLinkUpdate("dbx://connection/new?id=saved-connection&user=&password="), {
|
||||
connectionId: "saved-connection",
|
||||
patch: { username: "", password: "" },
|
||||
});
|
||||
});
|
||||
|
||||
test("update parser leaves links without an ID on the existing create path", () => {
|
||||
assert.equal(parseConnectionDeepLinkUpdate("dbx://connection/new?type=mysql&host=db.internal&one_time=true"), null);
|
||||
assert.equal(parseConnectionDeepLink("dbx://connection/new?type=mysql&host=db.internal&one_time=true")?.oneTime, true);
|
||||
});
|
||||
|
||||
test("ID-bearing links can never silently create new connections", () => {
|
||||
for (const query of ["id=saved-connection", "id=", "id=%20%20", "id=a&id=b", "id=%00"]) {
|
||||
assert.throws(() => parseConnectionDeepLink(`dbx://connection/new?${query}`), /cannot create a new connection/);
|
||||
}
|
||||
for (const query of ["id=", "id=%20%20", "id=%00", "id=%0Asaved-connection"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?${query}`), /valid connection ID/);
|
||||
}
|
||||
});
|
||||
|
||||
test("update links reject ambiguous parameters and profile or automatic-action controls", () => {
|
||||
for (const query of ["id=another", "password=one&password=two", "port=3306&port=3307", "ssl=true&ssl=false"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&${query}`), /Duplicate connection update parameter/);
|
||||
}
|
||||
for (const query of ["type=mysql", "url=mysql%3A%2F%2Froot%40other.internal", "one_time=true", "one_time=false", "auto_save=true", "auto_connect=true", "save_password=true", "read_only=false", "unknown=value"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&${query}`), /Unsupported connection update parameter/);
|
||||
}
|
||||
});
|
||||
|
||||
test("update links validate explicit port, SSL, version, and required display fields", () => {
|
||||
for (const port of ["", "0", "65536", "-1", "3306.5", "1e3", "0xff", "invalid"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&port=${port}`), /Invalid connection update port/);
|
||||
}
|
||||
for (const ssl of ["", "maybe", "2"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&ssl=${ssl}`), /Invalid connection update SSL/);
|
||||
}
|
||||
for (const version of ["", "2"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&v=${version}`), /Unsupported connection update version/);
|
||||
}
|
||||
for (const key of ["name", "host"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&${key}=%20`), /cannot be empty/);
|
||||
}
|
||||
assert.equal(parseConnectionDeepLinkUpdate("dbx://connection/new?id=saved-connection&ssl=YES")?.patch.ssl, true);
|
||||
assert.equal(parseConnectionDeepLinkUpdate("dbx://connection/new?id=saved-connection&ssl=off")?.patch.ssl, false);
|
||||
});
|
||||
|
||||
test("update links enforce the protocol target and reject ambiguous authority or fragments", () => {
|
||||
for (const link of ["not-a-url", "https://connection/new?id=saved-connection", "dbx://connection/edit?id=saved-connection", "dbx://connection/newer?id=saved-connection", "dbx://connections/new?id=saved-connection"]) {
|
||||
assert.equal(parseConnectionDeepLinkUpdate(link), null);
|
||||
}
|
||||
for (const link of ["dbx://user:secret@connection/new?id=saved-connection", "dbx://connection:123/new?id=saved-connection", "dbx://connection/new?id=saved-connection#password=secret"]) {
|
||||
assert.throws(() => parseConnectionDeepLinkUpdate(link), /Invalid connection update URL/);
|
||||
}
|
||||
});
|
||||
|
||||
test("update validation errors do not expose supplied parameter values", () => {
|
||||
const secret = "do-not-log-this-secret";
|
||||
for (const key of ["port", "ssl", "v", "type"]) {
|
||||
assert.throws(
|
||||
() => parseConnectionDeepLinkUpdate(`dbx://connection/new?id=saved-connection&${key}=${secret}`),
|
||||
(error: unknown) => error instanceof Error && !error.message.includes(secret),
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user