mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-22 11:58:08 +00:00
fix(sdk): settle transport connect on close
This commit is contained in:
53
packages/sdk/src/transport.test.ts
Normal file
53
packages/sdk/src/transport.test.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// OpenClaw SDK tests cover transport behavior.
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { GatewayClientTransport } from "./transport.js";
|
||||
|
||||
type MockGatewayClientInstance = {
|
||||
opts: {
|
||||
onConnectError?: (error: Error) => void;
|
||||
onHelloOk?: (hello: unknown) => void;
|
||||
};
|
||||
request: ReturnType<typeof vi.fn>;
|
||||
start: ReturnType<typeof vi.fn>;
|
||||
stopAndWait: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
const gatewayClientMocks = vi.hoisted(() => ({
|
||||
instances: [] as MockGatewayClientInstance[],
|
||||
}));
|
||||
|
||||
vi.mock("@openclaw/gateway-client", () => ({
|
||||
GatewayClient: class {
|
||||
readonly opts: MockGatewayClientInstance["opts"];
|
||||
readonly request = vi.fn();
|
||||
readonly start = vi.fn();
|
||||
readonly stopAndWait = vi.fn(async () => {});
|
||||
|
||||
constructor(opts: MockGatewayClientInstance["opts"]) {
|
||||
this.opts = opts;
|
||||
gatewayClientMocks.instances.push(this);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe("GatewayClientTransport", () => {
|
||||
beforeEach(() => {
|
||||
gatewayClientMocks.instances.length = 0;
|
||||
});
|
||||
|
||||
it("rejects a pending connect when the transport closes before hello-ok", async () => {
|
||||
const transport = new GatewayClientTransport();
|
||||
|
||||
const connect = transport.connect();
|
||||
const connectExpectation = expect(connect).rejects.toThrow(
|
||||
"gateway transport closed before connect completed",
|
||||
);
|
||||
const client = gatewayClientMocks.instances[0];
|
||||
expect(client?.start).toHaveBeenCalledTimes(1);
|
||||
|
||||
await transport.close();
|
||||
|
||||
await connectExpectation;
|
||||
expect(client?.stopAndWait).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -78,6 +78,7 @@ export class GatewayClientTransport implements ConnectableOpenClawTransport {
|
||||
private readonly options: GatewayClientTransportOptions;
|
||||
private client: GatewayClientLike | null = null;
|
||||
private connectPromise: Promise<void> | null = null;
|
||||
private rejectPendingConnect: ((error: Error) => void) | null = null;
|
||||
private closePromise: Promise<void> | null = null;
|
||||
|
||||
constructor(options: GatewayClientTransportOptions = {}) {
|
||||
@@ -89,6 +90,7 @@ export class GatewayClientTransport implements ConnectableOpenClawTransport {
|
||||
return this.connectPromise;
|
||||
}
|
||||
this.connectPromise = new Promise<void>((resolve, reject) => {
|
||||
this.rejectPendingConnect = reject;
|
||||
const client = new GatewayClient({
|
||||
...this.options,
|
||||
onEvent: (event: unknown) => {
|
||||
@@ -98,6 +100,7 @@ export class GatewayClientTransport implements ConnectableOpenClawTransport {
|
||||
},
|
||||
onHelloOk: (_hello: unknown) => {
|
||||
this.options.onHelloOk?.(_hello);
|
||||
this.rejectPendingConnect = null;
|
||||
resolve();
|
||||
},
|
||||
onConnectError: (error: Error) => {
|
||||
@@ -109,6 +112,7 @@ export class GatewayClientTransport implements ConnectableOpenClawTransport {
|
||||
this.connectPromise = null;
|
||||
}
|
||||
void client.stopAndWait().catch(() => {});
|
||||
this.rejectPendingConnect = null;
|
||||
reject(error);
|
||||
},
|
||||
onReconnectPaused: this.options.onReconnectPaused,
|
||||
@@ -145,6 +149,9 @@ export class GatewayClientTransport implements ConnectableOpenClawTransport {
|
||||
this.eventsHub.close();
|
||||
const client = this.client;
|
||||
this.client = null;
|
||||
const rejectPendingConnect = this.rejectPendingConnect;
|
||||
this.rejectPendingConnect = null;
|
||||
rejectPendingConnect?.(new Error("gateway transport closed before connect completed"));
|
||||
this.connectPromise = null;
|
||||
this.closePromise = client?.stopAndWait() ?? Promise.resolve();
|
||||
await this.closePromise;
|
||||
|
||||
Reference in New Issue
Block a user