mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-04 11:01:38 +00:00
fix(voice-call): share runtime stop completion
This commit is contained in:
@@ -282,6 +282,13 @@ describe("createVoiceCallRuntime lifecycle", () => {
|
||||
|
||||
it("returns an idempotent stop handler", async () => {
|
||||
const tunnelStop = vi.fn().mockResolvedValue(undefined);
|
||||
let releaseWebhookStop: (() => void) | undefined;
|
||||
mocks.webhookStop.mockImplementation(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
releaseWebhookStop = resolve;
|
||||
}),
|
||||
);
|
||||
mocks.startTunnel.mockResolvedValue({
|
||||
publicUrl: "https://public.example/voice/webhook",
|
||||
provider: "ngrok",
|
||||
@@ -294,8 +301,22 @@ describe("createVoiceCallRuntime lifecycle", () => {
|
||||
agentRuntime: {} as never,
|
||||
});
|
||||
|
||||
await runtime.stop();
|
||||
await runtime.stop();
|
||||
const firstStop = runtime.stop();
|
||||
const secondStop = runtime.stop();
|
||||
let stopped = false;
|
||||
void secondStop.then(() => {
|
||||
stopped = true;
|
||||
});
|
||||
|
||||
expect(secondStop).toBe(firstStop);
|
||||
await vi.waitFor(() => {
|
||||
expect(mocks.webhookStop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
expect(stopped).toBe(false);
|
||||
|
||||
releaseWebhookStop?.();
|
||||
await firstStop;
|
||||
expect(stopped).toBe(true);
|
||||
|
||||
expect(tunnelStop).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.cleanupTailscaleExposure).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -131,7 +131,7 @@ function createRuntimeResourceLifecycle(params: {
|
||||
stop: (opts?: { suppressErrors?: boolean }) => Promise<void>;
|
||||
} {
|
||||
let tunnelResult: TunnelResult | null = null;
|
||||
let stopped = false;
|
||||
let stopPromise: Promise<void> | null = null;
|
||||
|
||||
const runStep = async (step: () => Promise<void>, suppressErrors: boolean) => {
|
||||
if (suppressErrors) {
|
||||
@@ -145,23 +145,25 @@ function createRuntimeResourceLifecycle(params: {
|
||||
setTunnelResult: (result) => {
|
||||
tunnelResult = result;
|
||||
},
|
||||
stop: async (opts) => {
|
||||
if (stopped) {
|
||||
return;
|
||||
stop: (opts) => {
|
||||
if (stopPromise) {
|
||||
return stopPromise;
|
||||
}
|
||||
stopped = true;
|
||||
const suppressErrors = opts?.suppressErrors ?? false;
|
||||
await runStep(async () => {
|
||||
if (tunnelResult) {
|
||||
await tunnelResult.stop();
|
||||
}
|
||||
}, suppressErrors);
|
||||
await runStep(async () => {
|
||||
await cleanupTailscaleExposure(params.config);
|
||||
}, suppressErrors);
|
||||
await runStep(async () => {
|
||||
await params.webhookServer.stop();
|
||||
}, suppressErrors);
|
||||
stopPromise = (async () => {
|
||||
await runStep(async () => {
|
||||
if (tunnelResult) {
|
||||
await tunnelResult.stop();
|
||||
}
|
||||
}, suppressErrors);
|
||||
await runStep(async () => {
|
||||
await cleanupTailscaleExposure(params.config);
|
||||
}, suppressErrors);
|
||||
await runStep(async () => {
|
||||
await params.webhookServer.stop();
|
||||
}, suppressErrors);
|
||||
})();
|
||||
return stopPromise;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -556,7 +558,7 @@ export async function createVoiceCallRuntime(params: {
|
||||
|
||||
await manager.initialize(provider, webhookUrl);
|
||||
|
||||
const stop = async () => await lifecycle.stop();
|
||||
const stop = () => lifecycle.stop();
|
||||
|
||||
log.info("[voice-call] Runtime initialized");
|
||||
log.info(`[voice-call] Webhook URL: ${webhookUrl}`);
|
||||
|
||||
Reference in New Issue
Block a user