





















@@ -1,4 +1,4 @@
1-import { describe, expect, it } from "vitest";
1+import { afterEach, describe, expect, it, vi } from "vitest";
22import { VoiceCallConfigSchema } from "./config.js";
33import { CallManager } from "./manager.js";
44import {
@@ -7,6 +7,7 @@ import {
77makePersistedCall,
88writeCallsToStore,
99} from "./manager.test-harness.js";
10+import { flushPendingCallRecordWritesForTest, loadActiveCallsFromStore } from "./manager/store.js";
10111112function requireSingleActiveCall(manager: CallManager) {
1213const activeCalls = manager.getActiveCalls();
@@ -19,6 +20,10 @@ function requireSingleActiveCall(manager: CallManager) {
1920}
20212122describe("CallManager verification on restore", () => {
23+afterEach(() => {
24+vi.useRealTimers();
25+});
26+2227async function initializeManager(params?: {
2328callOverrides?: Parameters<typeof makePersistedCall>[0];
2429providerResult?: FakeProvider["getCallStatusResult"];
@@ -44,7 +49,7 @@ describe("CallManager verification on restore", () => {
4449const manager = new CallManager(config, storePath);
4550await manager.initialize(provider, "https://example.com/voice/webhook");
465147-return { call, manager };
52+return { call, manager, provider, storePath };
4853}
49545055it("skips stale calls reported terminal by provider", async () => {
@@ -75,7 +80,7 @@ describe("CallManager verification on restore", () => {
7580});
76817782it("skips calls older than maxDurationSeconds", async () => {
78-const { manager } = await initializeManager({
83+const { manager, provider, storePath } = await initializeManager({
7984callOverrides: {
8085startedAt: Date.now() - 600_000,
8186answeredAt: Date.now() - 590_000,
@@ -84,6 +89,14 @@ describe("CallManager verification on restore", () => {
8489});
85908691expect(manager.getActiveCalls()).toHaveLength(0);
92+expect(provider.hangupCalls).toEqual([
93+expect.objectContaining({
94+reason: "timeout",
95+}),
96+]);
97+98+await flushPendingCallRecordWritesForTest();
99+expect(loadActiveCallsFromStore(storePath).activeCalls.size).toBe(0);
87100});
8810189102it("skips calls without providerCallId", async () => {
@@ -108,6 +121,33 @@ describe("CallManager verification on restore", () => {
108121expect(activeCall.state).toBe(call.state);
109122});
110123124+it("uses only remaining max duration for restored answered calls", async () => {
125+vi.useFakeTimers();
126+const now = new Date("2026-03-17T03:07:00Z");
127+vi.setSystemTime(now);
128+const { manager, provider } = await initializeManager({
129+callOverrides: {
130+startedAt: now.getTime() - 290_000,
131+answeredAt: now.getTime() - 290_000,
132+state: "answered",
133+},
134+configOverrides: { maxDurationSeconds: 300 },
135+});
136+137+expect(manager.getActiveCalls()).toHaveLength(1);
138+await vi.advanceTimersByTimeAsync(9_000);
139+expect(manager.getActiveCalls()).toHaveLength(1);
140+expect(provider.hangupCalls).toHaveLength(0);
141+142+await vi.advanceTimersByTimeAsync(1_100);
143+expect(manager.getActiveCalls()).toHaveLength(0);
144+expect(provider.hangupCalls).toEqual([
145+expect.objectContaining({
146+reason: "timeout",
147+}),
148+]);
149+});
150+111151it("restores dedupe keys from terminal persisted calls so replayed webhooks stay ignored", async () => {
112152const storePath = createTestStorePath();
113153const persisted = makePersistedCall({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。