773 lines
23 KiB
TypeScript
773 lines
23 KiB
TypeScript
|
|
import test from "node:test";
|
||
|
|
import assert from "node:assert/strict";
|
||
|
|
import type { SftpPane } from "../../application/state/sftp/types";
|
||
|
|
import {
|
||
|
|
connectionKeyMatchesHost,
|
||
|
|
findPendingSftpRebindTargetPane,
|
||
|
|
findReusableSftpSidePanelTab,
|
||
|
|
isPendingSameEndpointSshSession,
|
||
|
|
isRemoteSftpTabHealthy,
|
||
|
|
rememberSftpSidePanelSourceStatus,
|
||
|
|
resolvePendingSftpUploadCancellation,
|
||
|
|
resolveSftpSidePanelTrackedSourceStatusUpdate,
|
||
|
|
shouldAcceptPendingSftpUpload,
|
||
|
|
shouldBlockPendingSftpUploadForSourceRebind,
|
||
|
|
shouldCancelPendingSftpUpload,
|
||
|
|
shouldCancelSettledPendingSftpRebindWithoutTarget,
|
||
|
|
shouldDeferPendingSftpUploadForOriginFocus,
|
||
|
|
shouldDeferSftpSidePanelAutoConnectForSession,
|
||
|
|
shouldRebindSftpSidePanelSourceSession,
|
||
|
|
shouldResetSftpSidePanelSourceSession,
|
||
|
|
shouldSkipSftpSidePanelAutoConnect,
|
||
|
|
shouldStartPendingSftpUploadRebind,
|
||
|
|
shouldWaitForPendingSftpRebind,
|
||
|
|
} from "./sftpSidePanelAutoConnect";
|
||
|
|
|
||
|
|
const remoteConnectedTab = (overrides: Partial<SftpPane> = {}): SftpPane => ({
|
||
|
|
id: "tab-1",
|
||
|
|
connection: {
|
||
|
|
id: "conn-1",
|
||
|
|
hostId: "host-1",
|
||
|
|
hostLabel: "server",
|
||
|
|
isLocal: false,
|
||
|
|
status: "connected",
|
||
|
|
currentPath: "/var/www",
|
||
|
|
},
|
||
|
|
files: [],
|
||
|
|
loading: false,
|
||
|
|
reconnecting: false,
|
||
|
|
error: null,
|
||
|
|
connectionLogs: [],
|
||
|
|
selectedFiles: new Set(),
|
||
|
|
filter: "",
|
||
|
|
filenameEncoding: "auto",
|
||
|
|
showHiddenFiles: false,
|
||
|
|
transferMutationToken: 0,
|
||
|
|
...overrides,
|
||
|
|
});
|
||
|
|
|
||
|
|
test("isRemoteSftpTabHealthy rejects loading tabs", () => {
|
||
|
|
const tab = remoteConnectedTab({ loading: true });
|
||
|
|
assert.equal(isRemoteSftpTabHealthy(tab, true), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("isRemoteSftpTabHealthy rejects tabs without a backend SFTP session", () => {
|
||
|
|
const tab = remoteConnectedTab();
|
||
|
|
assert.equal(isRemoteSftpTabHealthy(tab, false), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("isRemoteSftpTabHealthy rejects connecting tabs", () => {
|
||
|
|
const tab = remoteConnectedTab({
|
||
|
|
connection: {
|
||
|
|
...remoteConnectedTab().connection!,
|
||
|
|
status: "connecting",
|
||
|
|
},
|
||
|
|
});
|
||
|
|
assert.equal(isRemoteSftpTabHealthy(tab, true), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldSkipSftpSidePanelAutoConnect returns false for stale connected keys", () => {
|
||
|
|
const tab = remoteConnectedTab({ loading: true });
|
||
|
|
assert.equal(
|
||
|
|
shouldSkipSftpSidePanelAutoConnect("host-key", "host-key", tab, true, "host-key"),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldSkipSftpSidePanelAutoConnect rejects a healthy tab mapped to another endpoint", () => {
|
||
|
|
const tab = remoteConnectedTab();
|
||
|
|
assert.equal(
|
||
|
|
shouldSkipSftpSidePanelAutoConnect("host-a-key", "host-a-key", tab, true, "host-b-key"),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldSkipSftpSidePanelAutoConnect rejects when the active tab has no endpoint map", () => {
|
||
|
|
const tab = remoteConnectedTab();
|
||
|
|
assert.equal(
|
||
|
|
shouldSkipSftpSidePanelAutoConnect("host-a-key", "host-a-key", tab, true, null),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("isPendingSameEndpointSshSession only waits for actively connecting SSH sessions", () => {
|
||
|
|
const host = {
|
||
|
|
id: "host-1",
|
||
|
|
hostname: "server.example",
|
||
|
|
port: 22,
|
||
|
|
username: "root",
|
||
|
|
};
|
||
|
|
const baseSession = {
|
||
|
|
hostId: "host-1",
|
||
|
|
hostname: "server.example",
|
||
|
|
port: 22,
|
||
|
|
username: "root",
|
||
|
|
protocol: "ssh",
|
||
|
|
};
|
||
|
|
|
||
|
|
assert.equal(
|
||
|
|
isPendingSameEndpointSshSession({ ...baseSession, status: "connecting" }, host),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
isPendingSameEndpointSshSession({ ...baseSession, status: "disconnected" }, host),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
isPendingSameEndpointSshSession({ ...baseSession, status: "connected" }, host),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("connectionKeyMatchesHost accepts host-id prefix keys", () => {
|
||
|
|
assert.equal(connectionKeyMatchesHost("host-1:server:22:ssh::root", "host-1"), true);
|
||
|
|
assert.equal(connectionKeyMatchesHost("host-2:server:22:ssh::root", "host-1"), false);
|
||
|
|
assert.equal(connectionKeyMatchesHost(null, "host-1"), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldAcceptPendingSftpUpload waits until the pane endpoint matches the drop", () => {
|
||
|
|
const connected = {
|
||
|
|
hostId: "host-1",
|
||
|
|
isLocal: false,
|
||
|
|
status: "connected",
|
||
|
|
};
|
||
|
|
assert.equal(
|
||
|
|
shouldAcceptPendingSftpUpload({
|
||
|
|
ownerPanelOpen: true,
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingConnectionKey: "host-1:b.example:22:ssh::root",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
connection: connected,
|
||
|
|
paneConnectionKey: "host-1:a.example:22:ssh::root",
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldAcceptPendingSftpUpload({
|
||
|
|
ownerPanelOpen: true,
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingConnectionKey: "host-1:b.example:22:ssh::root",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
connection: connected,
|
||
|
|
paneConnectionKey: null,
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldAcceptPendingSftpUpload({
|
||
|
|
ownerPanelOpen: true,
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingConnectionKey: "host-1:b.example:22:ssh::root",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
connection: connected,
|
||
|
|
paneConnectionKey: "host-1:b.example:22:ssh::root",
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldAcceptPendingSftpUpload waits for the terminal session that requested the upload", () => {
|
||
|
|
const endpointKey = "host-1:prod.internal:22:ssh::deploy:";
|
||
|
|
const connection = {
|
||
|
|
hostId: "host-1",
|
||
|
|
isLocal: false,
|
||
|
|
status: "connected",
|
||
|
|
sourceSessionId: "session-through-jump-a",
|
||
|
|
};
|
||
|
|
|
||
|
|
assert.equal(
|
||
|
|
shouldAcceptPendingSftpUpload({
|
||
|
|
ownerPanelOpen: true,
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingConnectionKey: endpointKey,
|
||
|
|
pendingSourceSessionId: "session-through-jump-b",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
connection,
|
||
|
|
paneConnectionKey: endpointKey,
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
|
||
|
|
assert.equal(
|
||
|
|
shouldAcceptPendingSftpUpload({
|
||
|
|
ownerPanelOpen: true,
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingConnectionKey: endpointKey,
|
||
|
|
pendingSourceSessionId: "session-through-jump-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
connection,
|
||
|
|
paneConnectionKey: endpointKey,
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("a closed owner panel never starts a pending terminal upload", () => {
|
||
|
|
assert.equal(shouldAcceptPendingSftpUpload({
|
||
|
|
ownerPanelOpen: false,
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingConnectionKey: "host-1:target.example:22:ssh::alice",
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
isLocal: false,
|
||
|
|
status: "connected",
|
||
|
|
sourceSessionId: "session-a",
|
||
|
|
},
|
||
|
|
paneConnectionKey: "host-1:target.example:22:ssh::alice",
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending terminal upload is cancelled when its source terminal changes", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "session-b",
|
||
|
|
connection: null,
|
||
|
|
}), "source-changed");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending drop does not cancel while waiting for its own origin focus", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingOriginSessionId: "session-b",
|
||
|
|
pendingSourceSessionId: "session-b",
|
||
|
|
originSessionStatus: "connected",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
focusedSessionId: "session-a",
|
||
|
|
panelVisible: true,
|
||
|
|
waitingForOriginFocus: true,
|
||
|
|
waitingForSourceSession: true,
|
||
|
|
connection: null,
|
||
|
|
}), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Mosh/ET drops wait for origin focus before binding an SSH route", () => {
|
||
|
|
assert.equal(shouldDeferPendingSftpUploadForOriginFocus({
|
||
|
|
originSessionId: "mosh-b",
|
||
|
|
focusedSessionId: "ssh-a",
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldDeferPendingSftpUploadForOriginFocus({
|
||
|
|
originSessionId: "mosh-b",
|
||
|
|
focusedSessionId: "mosh-b",
|
||
|
|
}), false);
|
||
|
|
assert.equal(shouldDeferPendingSftpUploadForOriginFocus({
|
||
|
|
originSessionId: undefined,
|
||
|
|
focusedSessionId: "ssh-a",
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending drop still cancels after origin focus landed and the user leaves", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingOriginSessionId: "session-b",
|
||
|
|
pendingSourceSessionId: "session-b",
|
||
|
|
originSessionStatus: "connected",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
focusedSessionId: "session-a",
|
||
|
|
panelVisible: true,
|
||
|
|
waitingForOriginFocus: false,
|
||
|
|
waitingForSourceSession: false,
|
||
|
|
connection: null,
|
||
|
|
}), "source-changed");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("visible panel cancels an SSH drop when focus moves to same-host mosh or ET", () => {
|
||
|
|
const params = {
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingOriginSessionId: "ssh-session",
|
||
|
|
pendingSourceSessionId: undefined,
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: null,
|
||
|
|
focusedSessionId: "mosh-session",
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
sourceSessionId: "ssh-session",
|
||
|
|
status: "connected",
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
...params,
|
||
|
|
panelVisible: true,
|
||
|
|
}), "source-changed");
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
...params,
|
||
|
|
panelVisible: false,
|
||
|
|
}), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending terminal upload tolerates a transient missing focused session", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: null,
|
||
|
|
connection: null,
|
||
|
|
}), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending Mosh/ET origin upload is cancelled when its origin session disconnects", () => {
|
||
|
|
const params = {
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingOriginSessionId: "mosh-session",
|
||
|
|
pendingSourceSessionId: undefined,
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: null,
|
||
|
|
focusedSessionId: "mosh-session",
|
||
|
|
panelVisible: true,
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
status: "connected",
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
...params,
|
||
|
|
originSessionStatus: "disconnected",
|
||
|
|
}), "source-changed");
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
...params,
|
||
|
|
originSessionStatus: null,
|
||
|
|
}), "source-changed");
|
||
|
|
// Still pending origin routes must not cancel.
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
...params,
|
||
|
|
originSessionStatus: "connected",
|
||
|
|
}), null);
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
...params,
|
||
|
|
originSessionStatus: "connecting",
|
||
|
|
}), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending SSH origin upload keeps waiting through a same-tab reconnect", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingOriginSessionId: "ssh-session",
|
||
|
|
pendingSourceSessionId: "ssh-session",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "ssh-session",
|
||
|
|
focusedSessionId: "ssh-session",
|
||
|
|
panelVisible: true,
|
||
|
|
originSessionStatus: "disconnected",
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
sourceSessionId: "ssh-session",
|
||
|
|
status: "connected",
|
||
|
|
},
|
||
|
|
}), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending terminal upload is cancelled after its matching connection fails", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
sourceSessionId: "session-a",
|
||
|
|
status: "error",
|
||
|
|
},
|
||
|
|
}), "connection-failed");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending terminal upload survives an old connection while strict reconnect is starting", () => {
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
sourceSessionId: "session-old",
|
||
|
|
status: "connected",
|
||
|
|
},
|
||
|
|
}), null);
|
||
|
|
assert.equal(resolvePendingSftpUploadCancellation({
|
||
|
|
pendingHostId: "host-1",
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
activeHostId: "host-1",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
connection: {
|
||
|
|
hostId: "host-1",
|
||
|
|
sourceSessionId: "session-a",
|
||
|
|
status: "connecting",
|
||
|
|
},
|
||
|
|
}), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("an old disconnected pane does not cancel a pending strict rebind", () => {
|
||
|
|
assert.equal(shouldCancelPendingSftpUpload("connection-failed", true), false);
|
||
|
|
assert.equal(shouldCancelPendingSftpUpload("connection-failed", false), true);
|
||
|
|
assert.equal(shouldCancelPendingSftpUpload("source-changed", true), true);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("pending terminal upload is blocked while the same terminal tab changes routes", () => {
|
||
|
|
assert.equal(shouldBlockPendingSftpUploadForSourceRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
previousSessionId: "session-a",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
previousStatus: "connecting",
|
||
|
|
activeStatus: "connected",
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldBlockPendingSftpUploadForSourceRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
previousSessionId: "session-a",
|
||
|
|
activeSessionId: "session-a",
|
||
|
|
previousStatus: "connected",
|
||
|
|
activeStatus: "connected",
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("terminal drop waits until the exact forced rebind settles", () => {
|
||
|
|
assert.equal(shouldWaitForPendingSftpRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: null,
|
||
|
|
connectionId: "old-connection",
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldWaitForPendingSftpRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
previousConnectionId: "old-connection",
|
||
|
|
connectionId: "old-connection",
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldWaitForPendingSftpRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
previousConnectionId: "old-connection",
|
||
|
|
connectionId: "unrelated-connection",
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldWaitForPendingSftpRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
settledRequestId: "drop-1",
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
targetTabId: "new-tab",
|
||
|
|
targetConnectionId: "new-connection",
|
||
|
|
tabId: "old-tab",
|
||
|
|
connectionId: "unrelated-connection",
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldWaitForPendingSftpRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
settledRequestId: "drop-1",
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
targetTabId: "new-tab",
|
||
|
|
targetConnectionId: "new-connection",
|
||
|
|
tabId: "new-tab",
|
||
|
|
connectionId: "new-connection",
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("a repeated terminal drop stops waiting when its shared strict connect settles", () => {
|
||
|
|
assert.equal(shouldWaitForPendingSftpRebind({
|
||
|
|
pendingSourceSessionId: "session-a",
|
||
|
|
requestId: "drop-2",
|
||
|
|
startedRequestId: "drop-2",
|
||
|
|
settledRequestId: "drop-2",
|
||
|
|
barrierRequestId: "drop-2",
|
||
|
|
previousConnectionId: "connecting-connection",
|
||
|
|
targetTabId: "connecting-tab",
|
||
|
|
targetConnectionId: "connecting-connection",
|
||
|
|
tabId: "connecting-tab",
|
||
|
|
connectionId: "connecting-connection",
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("a terminal drop is cancelled when its settled forced target was closed", () => {
|
||
|
|
assert.equal(shouldCancelSettledPendingSftpRebindWithoutTarget({
|
||
|
|
pendingRequiresRebind: true,
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
settledRequestId: "drop-1",
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
targetTabId: "closed-tab",
|
||
|
|
targetConnectionId: "closed-connection",
|
||
|
|
targetExists: false,
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldCancelSettledPendingSftpRebindWithoutTarget({
|
||
|
|
pendingRequiresRebind: true,
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
settledRequestId: null,
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
targetTabId: "closed-tab",
|
||
|
|
targetConnectionId: "closed-connection",
|
||
|
|
targetExists: false,
|
||
|
|
}), false);
|
||
|
|
assert.equal(shouldCancelSettledPendingSftpRebindWithoutTarget({
|
||
|
|
pendingRequiresRebind: true,
|
||
|
|
requestId: "drop-1",
|
||
|
|
startedRequestId: "drop-1",
|
||
|
|
settledRequestId: "drop-1",
|
||
|
|
barrierRequestId: "drop-1",
|
||
|
|
targetTabId: "live-tab",
|
||
|
|
targetConnectionId: "live-connection",
|
||
|
|
targetExists: true,
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("a forced upload target remains valid after moving to the other SFTP pane", () => {
|
||
|
|
const movedTarget = remoteConnectedTab({
|
||
|
|
id: "moved-tab",
|
||
|
|
connection: {
|
||
|
|
...remoteConnectedTab().connection!,
|
||
|
|
id: "moved-connection",
|
||
|
|
},
|
||
|
|
});
|
||
|
|
assert.equal(findPendingSftpRebindTargetPane(
|
||
|
|
[],
|
||
|
|
[movedTarget],
|
||
|
|
"moved-tab",
|
||
|
|
"moved-connection",
|
||
|
|
), movedTarget);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Mosh and ET drops force a fresh SFTP route even when an old tab is healthy", () => {
|
||
|
|
assert.equal(shouldStartPendingSftpUploadRebind({
|
||
|
|
pendingMatchesTarget: true,
|
||
|
|
requestId: "mosh-drop",
|
||
|
|
startedRequestId: null,
|
||
|
|
originSessionId: "mosh-session",
|
||
|
|
sourceSessionId: undefined,
|
||
|
|
}), true);
|
||
|
|
assert.equal(shouldStartPendingSftpUploadRebind({
|
||
|
|
pendingMatchesTarget: true,
|
||
|
|
requestId: "mosh-drop",
|
||
|
|
startedRequestId: "mosh-drop",
|
||
|
|
originSessionId: "mosh-session",
|
||
|
|
sourceSessionId: undefined,
|
||
|
|
}), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("findReusableSftpSidePanelTab ignores tabs stuck in loading after SSH disconnect", () => {
|
||
|
|
const tab = remoteConnectedTab({ loading: true });
|
||
|
|
const map = new Map([[tab.id, "host-key"]]);
|
||
|
|
assert.equal(
|
||
|
|
findReusableSftpSidePanelTab([tab], "host-1", "host-key", map, () => true),
|
||
|
|
null,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("findReusableSftpSidePanelTab returns healthy tabs", () => {
|
||
|
|
const tab = remoteConnectedTab();
|
||
|
|
const map = new Map([[tab.id, "host-key"]]);
|
||
|
|
assert.equal(
|
||
|
|
findReusableSftpSidePanelTab([tab], "host-1", "host-key", map, () => true),
|
||
|
|
tab,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldResetSftpSidePanelSourceSession detects terminal session changes", () => {
|
||
|
|
assert.equal(shouldResetSftpSidePanelSourceSession("sess-a", "sess-b"), true);
|
||
|
|
assert.equal(shouldResetSftpSidePanelSourceSession("sess-a", "sess-a"), false);
|
||
|
|
assert.equal(shouldResetSftpSidePanelSourceSession(null, "sess-a"), false);
|
||
|
|
assert.equal(shouldResetSftpSidePanelSourceSession("sess-a", null), false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldRebindSftpSidePanelSourceSession treats SSH start-over as a transport change", () => {
|
||
|
|
// Same terminal tab id after Start over - transport was replaced even though
|
||
|
|
// the session id did not change.
|
||
|
|
assert.equal(
|
||
|
|
shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-a",
|
||
|
|
previousStatus: "disconnected",
|
||
|
|
nextStatus: "connected",
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-a",
|
||
|
|
previousStatus: "connecting",
|
||
|
|
nextStatus: "connected",
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-a",
|
||
|
|
previousStatus: "connected",
|
||
|
|
nextStatus: "connected",
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-b",
|
||
|
|
previousStatus: "connected",
|
||
|
|
nextStatus: "connected",
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-a",
|
||
|
|
previousStatus: "disconnected",
|
||
|
|
nextStatus: "connecting",
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("shouldDeferSftpSidePanelAutoConnectForSession only waits during an active reconnect", () => {
|
||
|
|
assert.equal(
|
||
|
|
shouldDeferSftpSidePanelAutoConnectForSession({
|
||
|
|
activeSessionId: "sess-a",
|
||
|
|
sessionStatus: "disconnected",
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldDeferSftpSidePanelAutoConnectForSession({
|
||
|
|
activeSessionId: "sess-a",
|
||
|
|
sessionStatus: "connecting",
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldDeferSftpSidePanelAutoConnectForSession({
|
||
|
|
activeSessionId: "sess-a",
|
||
|
|
sessionStatus: "connected",
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldDeferSftpSidePanelAutoConnectForSession({
|
||
|
|
activeSessionId: null,
|
||
|
|
sessionStatus: "connecting",
|
||
|
|
}),
|
||
|
|
false,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("resolveSftpSidePanelTrackedSourceStatusUpdate remembers background disconnects", () => {
|
||
|
|
assert.deepEqual(
|
||
|
|
resolveSftpSidePanelTrackedSourceStatusUpdate({
|
||
|
|
trackedSessionId: "sess-a",
|
||
|
|
sessionStatus: "disconnected",
|
||
|
|
}),
|
||
|
|
{ sessionId: "sess-a", status: "disconnected" },
|
||
|
|
);
|
||
|
|
assert.deepEqual(
|
||
|
|
resolveSftpSidePanelTrackedSourceStatusUpdate({
|
||
|
|
trackedSessionId: "sess-a",
|
||
|
|
sessionStatus: "connecting",
|
||
|
|
}),
|
||
|
|
{ sessionId: "sess-a", status: "connecting" },
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
resolveSftpSidePanelTrackedSourceStatusUpdate({
|
||
|
|
trackedSessionId: "sess-a",
|
||
|
|
sessionStatus: "connected",
|
||
|
|
}),
|
||
|
|
null,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
resolveSftpSidePanelTrackedSourceStatusUpdate({
|
||
|
|
trackedSessionId: null,
|
||
|
|
sessionStatus: "disconnected",
|
||
|
|
}),
|
||
|
|
null,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("rememberSftpSidePanelSourceStatus keeps the linked SSH status across non-SSH focus", () => {
|
||
|
|
assert.equal(
|
||
|
|
rememberSftpSidePanelSourceStatus({
|
||
|
|
previousStatus: "connecting",
|
||
|
|
activeSessionId: null,
|
||
|
|
activeSessionStatus: null,
|
||
|
|
}),
|
||
|
|
"connecting",
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
rememberSftpSidePanelSourceStatus({
|
||
|
|
previousStatus: "disconnected",
|
||
|
|
activeSessionId: null,
|
||
|
|
activeSessionStatus: null,
|
||
|
|
}),
|
||
|
|
"disconnected",
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
rememberSftpSidePanelSourceStatus({
|
||
|
|
previousStatus: "disconnected",
|
||
|
|
activeSessionId: "sess-a",
|
||
|
|
activeSessionStatus: "connected",
|
||
|
|
}),
|
||
|
|
"connected",
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("failed terminal reconnect keeps a healthy standalone SFTP tab", () => {
|
||
|
|
const tab = remoteConnectedTab();
|
||
|
|
const sessionChanged = shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-a",
|
||
|
|
previousStatus: "connecting",
|
||
|
|
nextStatus: "disconnected",
|
||
|
|
});
|
||
|
|
assert.equal(sessionChanged, false);
|
||
|
|
assert.equal(
|
||
|
|
!sessionChanged && shouldSkipSftpSidePanelAutoConnect(
|
||
|
|
"host-1:key",
|
||
|
|
"host-1:key",
|
||
|
|
tab,
|
||
|
|
true,
|
||
|
|
"host-1:key",
|
||
|
|
),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
assert.equal(
|
||
|
|
shouldRebindSftpSidePanelSourceSession({
|
||
|
|
previousSessionId: "sess-a",
|
||
|
|
nextSessionId: "sess-a",
|
||
|
|
previousStatus: "connecting",
|
||
|
|
nextStatus: "connected",
|
||
|
|
}),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("session change still requires rebind even when the endpoint key matches", () => {
|
||
|
|
const tab = remoteConnectedTab();
|
||
|
|
// Callers must not skip auto-connect solely because the tab is healthy —
|
||
|
|
// a new focused terminal may share host/port/user while proxy/jump differs.
|
||
|
|
// Path stickiness is handled by remembered initialPath on reconnect.
|
||
|
|
assert.equal(shouldResetSftpSidePanelSourceSession("sess-a", "sess-b"), true);
|
||
|
|
assert.equal(
|
||
|
|
shouldSkipSftpSidePanelAutoConnect("host-key", "host-key", tab, true, "host-key"),
|
||
|
|
true,
|
||
|
|
);
|
||
|
|
// Reuse lookup still finds the tab, but callers pass sessionChanged and skip
|
||
|
|
// it so connect rebinds with the new sourceSessionId.
|
||
|
|
assert.equal(
|
||
|
|
findReusableSftpSidePanelTab(
|
||
|
|
[tab],
|
||
|
|
"host-1",
|
||
|
|
"host-key",
|
||
|
|
new Map([[tab.id, "host-key"]]),
|
||
|
|
() => true,
|
||
|
|
),
|
||
|
|
tab,
|
||
|
|
);
|
||
|
|
});
|