Files

342 lines
12 KiB
TypeScript
Raw Permalink Normal View History

import test from "node:test";
import assert from "node:assert/strict";
import {
getParentPath,
getFileName,
getSftpBreadcrumbSegments,
getSftpFilterAfterPathChange,
getSftpFilterAfterPathChangeError,
getSftpPathRoot,
isConcreteTransferTargetPath,
isSftpDescendantPath,
isWindowsRoot,
joinTransferTargetPath,
normalizeSftpNavigationPath,
normalizeSftpPaneNavigationPath,
resolveSftpWindowsPathOptions,
shouldClearSftpFilterForPathChange,
} from "./utils";
test("directory targets cannot escape the selected local root", () => {
const windowsRoot = "C:\\Users\\alice\\Downloads\\folder";
for (const relativePath of [
"..\\outside.txt",
"../outside.txt",
"C:\\outside.txt",
"C:/outside.txt",
"\\\\server\\share\\outside.txt",
"/absolute/outside.txt",
".. ",
". ",
"safe.txt:alternate-stream",
]) {
assert.throws(
() => joinTransferTargetPath(windowsRoot, relativePath),
/unsafe transfer path/i,
relativePath,
);
}
assert.equal(
joinTransferTargetPath(windowsRoot, "nested/report.txt"),
"C:\\Users\\alice\\Downloads\\folder\\nested\\report.txt",
);
assert.throws(
() => joinTransferTargetPath("/Users/alice/Downloads/folder", "../outside.txt"),
/unsafe transfer path/i,
);
const uncRoot = "\\\\server\\share\\Downloads\\folder";
assert.throws(
() => joinTransferTargetPath(uncRoot, "..\\outside.txt"),
/unsafe transfer path/i,
);
assert.equal(
joinTransferTargetPath(uncRoot, "nested/report.txt"),
"\\\\server\\share\\Downloads\\folder\\nested\\report.txt",
);
// Forward-slash UNC destinations stay POSIX-shaped for remote panes, but
// transfer joins must still apply Windows relative-path guards so a
// server-controlled `\` / ADS name cannot escape the selected folder.
const forwardSlashUncRoot = "//server/share/Downloads/folder";
for (const relativePath of [
"..\\outside.txt",
"safe.txt:alternate-stream",
"nested\\escape.txt",
"trailing. ",
]) {
assert.throws(
() => joinTransferTargetPath(forwardSlashUncRoot, relativePath),
/unsafe transfer path/i,
relativePath,
);
}
assert.equal(
joinTransferTargetPath(forwardSlashUncRoot, "nested/report.txt"),
"//server/share/Downloads/folder/nested/report.txt",
);
assert.equal(
getParentPath("\\\\server\\share\\Downloads\\folder"),
"\\\\server\\share\\Downloads",
);
assert.equal(
getParentPath("\\\\server\\share"),
"\\\\server\\share",
);
});
test("path bar keeps WSL UNC paths instead of forcing a Unix root", () => {
const windowsPane = { acceptForwardSlashUnc: true } as const;
assert.equal(
normalizeSftpNavigationPath("\\\\wsl.localhost\\Ubuntu-22.04\\home\\aaa"),
"\\\\wsl.localhost\\Ubuntu-22.04\\home\\aaa",
);
assert.equal(
normalizeSftpNavigationPath("//wsl.localhost/Ubuntu-22.04/home/aaa", windowsPane),
"\\\\wsl.localhost\\Ubuntu-22.04\\home\\aaa",
);
assert.equal(
normalizeSftpNavigationPath("\\\\wsl$\\Ubuntu-22.04\\home\\aaa"),
"\\\\wsl$\\Ubuntu-22.04\\home\\aaa",
);
assert.equal(normalizeSftpNavigationPath("C:"), "C:\\");
assert.equal(normalizeSftpNavigationPath("c:/"), "C:\\");
assert.equal(normalizeSftpNavigationPath("home/aaa"), "/home/aaa");
assert.equal(normalizeSftpNavigationPath("/var/log"), "/var/log");
assert.equal(normalizeSftpNavigationPath(" "), "/");
});
test("path bar keeps POSIX double-slash paths outside Windows panes", () => {
assert.equal(normalizeSftpNavigationPath("//srv/share"), "//srv/share");
assert.equal(
normalizeSftpNavigationPath("//srv/share/logs"),
"//srv/share/logs",
);
assert.equal(
normalizeSftpNavigationPath("//wsl.localhost/Ubuntu-22.04/home/aaa"),
"//wsl.localhost/Ubuntu-22.04/home/aaa",
);
assert.equal(
normalizeSftpNavigationPath("//srv/share\\logs"),
"//srv/share\\logs",
);
});
test("getParentPath preserves POSIX double-slash prefixes", () => {
assert.equal(getParentPath("//srv/share/logs"), "//srv/share");
assert.equal(getParentPath("//srv/share"), "//srv");
assert.equal(getParentPath("//srv"), "/");
assert.equal(getParentPath("//"), "/");
assert.equal(getParentPath("/srv/share/logs"), "/srv/share");
});
test("POSIX paths with three or more leading slashes stay in the root namespace", () => {
assert.equal(getParentPath("///srv/share/logs"), "/srv/share");
assert.deepEqual(
getSftpBreadcrumbSegments("///srv/share/logs"),
{
segments: [
{ label: "srv", path: "/srv" },
{ label: "share", path: "/srv/share" },
{ label: "logs", path: "/srv/share/logs" },
],
isWindowsDrive: false,
},
);
});
test("getFileName preserves backslashes in POSIX path components", () => {
assert.equal(getFileName("//srv/share\\logs"), "share\\logs");
assert.equal(getFileName("\\\\server\\share\\logs"), "logs");
});
test("descendant checks use the path kind instead of literal backslashes", () => {
assert.equal(isSftpDescendantPath("//srv/share\\logs/child", "//srv/share\\logs"), true);
assert.equal(isSftpDescendantPath("//srv/share\\logs-old", "//srv/share\\logs"), false);
assert.equal(isSftpDescendantPath("C:\\Users\\alice\\child", "C:\\Users\\alice"), true);
});
test("Windows UNC share roots are treated as path roots", () => {
assert.equal(isWindowsRoot("\\\\wsl.localhost\\Ubuntu-22.04"), true);
assert.equal(isWindowsRoot("\\\\wsl.localhost\\Ubuntu-22.04\\"), true);
assert.equal(isWindowsRoot("\\\\wsl.localhost\\Ubuntu-22.04\\home"), false);
assert.equal(isWindowsRoot("C:\\"), true);
assert.equal(isWindowsRoot("C:\\Users"), false);
});
test("Windows panes treat forward-slash UNC share roots as roots for Up navigation", () => {
const windowsPane = { acceptForwardSlashUnc: true } as const;
assert.equal(isWindowsRoot("//server/share"), false);
assert.equal(isWindowsRoot("//server/share", windowsPane), true);
assert.equal(isWindowsRoot("//server/share/", windowsPane), true);
assert.equal(isWindowsRoot("//server/share/logs", windowsPane), false);
assert.equal(getParentPath("//server/share"), "//server");
assert.equal(getParentPath("//server/share", windowsPane), "\\\\server\\share");
assert.equal(
getParentPath("//server/share/logs/app", windowsPane),
"\\\\server\\share\\logs",
);
});
test("pane context normalizes bookmark and initialPath forward-slash UNC ingress", () => {
assert.deepEqual(resolveSftpWindowsPathOptions("C:\\Users\\alice"), {
acceptForwardSlashUnc: true,
});
assert.deepEqual(resolveSftpWindowsPathOptions("/home/alice"), {});
assert.deepEqual(
resolveSftpWindowsPathOptions("//server/share", "C:\\Users\\alice"),
{ acceptForwardSlashUnc: true },
);
assert.equal(
normalizeSftpPaneNavigationPath("//server/share", "C:\\Users\\alice"),
"\\\\server\\share",
);
assert.equal(
normalizeSftpPaneNavigationPath("//server/share", "/home/alice"),
"//server/share",
);
assert.equal(
normalizeSftpPaneNavigationPath("//wsl.localhost/Ubuntu-22.04/home", "\\\\wsl.localhost\\Ubuntu-22.04"),
"\\\\wsl.localhost\\Ubuntu-22.04\\home",
);
});
test("pane navigation preserves trailing whitespace; path bar still trims", () => {
assert.equal(
normalizeSftpPaneNavigationPath("/home/alice/dir ", "/home/alice"),
"/home/alice/dir ",
);
assert.equal(
normalizeSftpPaneNavigationPath("//server/share/logs ", "C:\\Users\\alice"),
"\\\\server\\share\\logs ",
);
assert.equal(normalizeSftpNavigationPath("/home/alice/dir "), "/home/alice/dir");
assert.equal(
normalizeSftpNavigationPath("/home/alice/dir ", { trimWhitespace: false }),
"/home/alice/dir ",
);
});
test("breadcrumb segments keep WSL UNC roots intact", () => {
assert.deepEqual(
getSftpBreadcrumbSegments("\\\\wsl.localhost\\Ubuntu-22.04\\home\\aaa"),
{
segments: [
{ label: "\\\\wsl.localhost\\Ubuntu-22.04", path: "\\\\wsl.localhost\\Ubuntu-22.04" },
{ label: "home", path: "\\\\wsl.localhost\\Ubuntu-22.04\\home" },
{ label: "aaa", path: "\\\\wsl.localhost\\Ubuntu-22.04\\home\\aaa" },
],
isWindowsDrive: false,
},
);
assert.deepEqual(
getSftpBreadcrumbSegments("C:\\Users\\alice"),
{
segments: [
{ label: "C:", path: "C:\\" },
{ label: "Users", path: "C:\\Users" },
{ label: "alice", path: "C:\\Users\\alice" },
],
isWindowsDrive: true,
},
);
assert.deepEqual(
getSftpBreadcrumbSegments("/home/aaa"),
{
segments: [
{ label: "home", path: "/home" },
{ label: "aaa", path: "/home/aaa" },
],
isWindowsDrive: false,
},
);
assert.deepEqual(
getSftpBreadcrumbSegments("//srv/share/logs"),
{
segments: [
{ label: "srv", path: "//srv" },
{ label: "share", path: "//srv/share" },
{ label: "logs", path: "//srv/share/logs" },
],
isWindowsDrive: false,
},
);
assert.deepEqual(
getSftpBreadcrumbSegments("//srv/share\\logs"),
{
segments: [
{ label: "srv", path: "//srv" },
{ label: "share\\logs", path: "//srv/share\\logs" },
],
isWindowsDrive: false,
},
);
assert.deepEqual(
getSftpBreadcrumbSegments("//wsl.localhost/Ubuntu-22.04/home/aaa", {
acceptForwardSlashUnc: true,
}),
{
segments: [
{ label: "\\\\wsl.localhost\\Ubuntu-22.04", path: "\\\\wsl.localhost\\Ubuntu-22.04" },
{ label: "home", path: "\\\\wsl.localhost\\Ubuntu-22.04\\home" },
{ label: "aaa", path: "\\\\wsl.localhost\\Ubuntu-22.04\\home\\aaa" },
],
isWindowsDrive: false,
},
);
});
test("concrete transfer target paths exclude temporary placeholders", () => {
assert.equal(isConcreteTransferTargetPath({ targetPath: "/Users/alice/Downloads/report.pdf" }), true);
assert.equal(isConcreteTransferTargetPath({ targetPath: "C:\\Users\\alice\\Downloads\\report.pdf" }), true);
assert.equal(isConcreteTransferTargetPath({ targetPath: "(temp)" }), false);
assert.equal(isConcreteTransferTargetPath({ targetPath: " " }), false);
});
test("SFTP filter clears when entering a different directory", () => {
assert.equal(shouldClearSftpFilterForPathChange("/srv", "/srv/app"), true);
assert.equal(getSftpFilterAfterPathChange("/srv", "/srv/app", "log"), "");
});
test("SFTP filter clears when navigating to the parent directory", () => {
assert.equal(shouldClearSftpFilterForPathChange("/srv/app", getParentPath("/srv/app")), true);
assert.equal(getSftpFilterAfterPathChange("/srv/app", getParentPath("/srv/app"), "dist"), "");
});
test("SFTP filter stays when refreshing the same directory", () => {
assert.equal(shouldClearSftpFilterForPathChange("/srv/app", "/srv/app"), false);
assert.equal(shouldClearSftpFilterForPathChange("/srv/app", "/srv/app/"), false);
assert.equal(shouldClearSftpFilterForPathChange("C:\\Users\\alice", "c:/Users/alice"), false);
assert.equal(
shouldClearSftpFilterForPathChange("\\\\Server\\share\\folder", "\\\\server\\share\\folder\\"),
false,
);
assert.equal(getSftpFilterAfterPathChange("/srv/app", "/srv/app", "log"), "log");
assert.equal(getSftpFilterAfterPathChange("/srv/app", "/srv/app/", "log"), "log");
assert.equal(getSftpFilterAfterPathChange("C:\\Users\\alice", "c:/Users/alice", "docs"), "docs");
});
test("SFTP filter restores when changed-directory navigation fails", () => {
assert.equal(getSftpFilterAfterPathChangeError(true, "log", "typed-while-loading"), "log");
});
test("SFTP filter preserves in-flight edits when same-directory refresh fails", () => {
assert.equal(getSftpFilterAfterPathChangeError(false, "log", "typed-while-loading"), "typed-while-loading");
});
test("getSftpPathRoot resolves the filesystem root for breadcrumb navigation", () => {
assert.equal(getSftpPathRoot("/var/www"), "/");
assert.equal(getSftpPathRoot("/"), "/");
assert.equal(getSftpPathRoot("//srv/share/logs"), "/");
assert.equal(getSftpPathRoot("C:\\Users\\alice"), "C:\\");
assert.equal(getSftpPathRoot("D:/"), "D:\\");
assert.equal(getSftpPathRoot("\\\\server\\share\\folder"), "\\\\server\\share");
// Forward-slash //host/share stays POSIX unless UNC is explicitly accepted.
assert.equal(getSftpPathRoot("//srv/share/logs"), "/");
assert.equal(
getSftpPathRoot("//srv/share/logs", { acceptForwardSlashUnc: true }),
"\\\\srv\\share",
);
});