Skip to content

Commit 8863f9e

Browse files
committed
Snapshot stale SSH records during add rollback
1 parent 67f8e05 commit 8863f9e

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

apps/web/src/environments/runtime/service.addSavedEnvironment.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,52 @@ describe("addSavedEnvironment", () => {
421421
await resetEnvironmentServiceForTests();
422422
});
423423

424+
it("preserves an older ssh record when replacing it fails to persist credentials", async () => {
425+
mockWriteSavedEnvironmentBearerToken.mockResolvedValue(false);
426+
mockFetchSshEnvironmentDescriptor.mockResolvedValue({
427+
environmentId: EnvironmentId.make("environment-2"),
428+
label: "Remote environment",
429+
});
430+
const staleRecord = {
431+
environmentId: EnvironmentId.make("environment-1"),
432+
label: "Old ssh environment",
433+
httpBaseUrl: "http://127.0.0.1:3774/",
434+
wsBaseUrl: "ws://127.0.0.1:3774/",
435+
createdAt: "2026-04-14T00:00:00.000Z",
436+
lastConnectedAt: null,
437+
desktopSsh: {
438+
alias: "devbox",
439+
hostname: "devbox.example.com",
440+
username: "julius",
441+
port: 22,
442+
},
443+
};
444+
mockSavedRecords = [staleRecord];
445+
446+
const { addSavedEnvironment, resetEnvironmentServiceForTests } = await import("./service");
447+
448+
await expect(
449+
addSavedEnvironment({
450+
label: "Remote environment",
451+
host: "http://127.0.0.1:3774/",
452+
pairingCode: "ssh-pairing-code",
453+
desktopSsh: {
454+
alias: "devbox",
455+
hostname: "devbox.example.com",
456+
username: "julius",
457+
port: 22,
458+
},
459+
}),
460+
).rejects.toThrow("Unable to persist saved environment credentials.");
461+
462+
expect(mockSetSavedEnvironmentRegistry).toHaveBeenCalledWith([staleRecord]);
463+
expect(mockRemovePersistedSavedEnvironment).not.toHaveBeenCalled();
464+
expect(mockUpsert).not.toHaveBeenCalled();
465+
expect(mockSavedRecords).toEqual([staleRecord]);
466+
467+
await resetEnvironmentServiceForTests();
468+
});
469+
424470
it("retries desktop ssh session refresh when the forwarded endpoint returns ssh_http 401", async () => {
425471
mockWriteSavedEnvironmentBearerToken.mockResolvedValue(true);
426472
mockBootstrapSshBearerSession

apps/web/src/environments/runtime/service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1708,12 +1708,14 @@ export async function addSavedEnvironment(input: {
17081708
httpBaseUrl: resolvedTarget.httpBaseUrl,
17091709
});
17101710
const environmentId = descriptor.environmentId;
1711-
const registrySnapshot = snapshotSavedEnvironmentRegistry([environmentId]);
17121711
const existingRecord =
17131712
getSavedEnvironmentRecord(environmentId) ??
17141713
findSavedEnvironmentRecordByDesktopSshTarget(input.desktopSsh);
17151714
const staleDesktopSshRecord =
17161715
existingRecord && existingRecord.environmentId !== environmentId ? existingRecord : null;
1716+
const registrySnapshot = snapshotSavedEnvironmentRegistry(
1717+
staleDesktopSshRecord ? [environmentId, staleDesktopSshRecord.environmentId] : [environmentId],
1718+
);
17171719

17181720
const bearerSession = input.desktopSsh
17191721
? await bootstrapDesktopSshBearerSession(resolvedTarget.httpBaseUrl, resolvedTarget.credential)

0 commit comments

Comments
 (0)