fix(app): stop connecting to a host removed during its first probe (#5722)

Closes #4815

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
paseo-bot[bot]
2026-09-30 02:12:03 +02:00
committed by GitHub
parent 3fea128382
commit 4e9a4582b7
2 changed files with 39 additions and 0 deletions
@@ -4017,6 +4017,38 @@ describe("HostRuntimeStore initial connection hint bootstrap", () => {
expect(store.getSnapshot("srv_deep")?.connectionStatus).toBe("error");
store.syncHosts([]);
});
it("stops connecting to a host removed while its first probe is pending", async () => {
useHostRuntimeClock();
const firstProbe = createDeferred<void>();
let connectCalls = 0;
const store = new HostRuntimeStore({
storage: createMemoryHostRuntimeStorage(),
deps: {
createClient: () => new FakeDaemonClient() as unknown as DaemonClient,
connectToDaemon: async ({ host }) => {
connectCalls += 1;
if (connectCalls === 1) await firstProbe.promise;
return {
client: makeConnectedProbeClient(5) as unknown as DaemonClient,
serverId: host.serverId,
hostname: host.label ?? null,
};
},
getClientId: async () => "cid_test_removed_host",
},
});
await store.upsertDirectConnection({ serverId: "srv_removed", endpoint: "lan:6767" });
await vi.waitFor(() => expect(connectCalls).toBe(1));
await store.removeHost("srv_removed");
firstProbe.resolve();
await vi.advanceTimersByTimeAsync(60_000);
expect(connectCalls).toBe(1);
expect(store.getSnapshot("srv_removed")).toBeNull();
expect(useSessionStore.getState().sessions["srv_removed"]).toBeUndefined();
});
it("saves and reconnects a rejected saved host after changing its password", async () => {
const store = new HostRuntimeStore({
storage: createMemoryHostRuntimeStorage(),
+7
View File
@@ -677,8 +677,15 @@ export class HostRuntimeController {
connectionId: options.initialConnection.connectionId,
existingClient: options.initialConnection.existingClient,
});
if (!this.started) {
return;
}
}
await this.runProbeCycleNow();
// stop() can run while startup is pending; a stopped controller never probes again.
if (!this.started) {
return;
}
if (options?.autoProbe !== false) {
this.probeIntervalHandle = setInterval(() => {
void this.runProbeCycleNow();