test(server): retain voice reply recovery after failed state reads

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-25 04:02:23 -05:00
1 parent fe5e251319
commit 439d0a86eb
3 files changed
+33

No files matched your search

@@ -1255,3 +1255,13 @@ was stopped after repeated embedded PostgreSQL setup failures, not counted as a
pass. Slice 22 owns voice reply delivery and begins with the multipart/outcome
policy and publication-settlement producer audit. See
[reply delivery audit](2026-10-25-voice-reply-delivery-sleep-audit.md).
Slice 22 now implements voice reply delivery from committed changes and saved
retry/dispatch deadlines. The periodic reply scan and pre-publication direct push
are removed. Real normal/operator publication tests prove the final-commit wake;
turning that notification off reproduces zero delivered replies. The branch has
786 selected passing tests across isolated voice, recovery, startup, and shared
consumers, plus workspace typecheck/build. See the reply audit for retained
fixture failures and local full-suite infrastructure limitations. Publication and
latest-head review/check acceptance remain outstanding. Inbound admission cleanup,
inbound history retention, and call reports remain separate next slices.
@@ -194,3 +194,12 @@ PR CI/reviews remain required. The local all-suite command has the previously
recorded embedded-PostgreSQL initialization limitation; targeted database suites
run on real isolated PostgreSQL instead. Do not count skipped or uninitialized
database tests as passes.
The parent recovery suite passed all 11 tests on its isolated database. The new
reply-state read failure case also passed: the worker keeps its retry, performs
no SQL during backoff, and sends once after recovery. Together with the full run,
this verifies 108 voice cases, 16 startup cases, 11 parent recovery cases, and 651
shared-consumer cases (786 selected tests). Workspace typecheck and build passed.
The final added failure case changed tests only after the typecheck; the workspace
build compiled the current tree. No production changes followed the full run.
Remote CI/review remains pending until this branch is published.
@@ -865,6 +865,20 @@ const support = external ? { supported: true } : await getEmbeddedPostgresTestSu
expect(f.transport.sendCallMessage).not.toHaveBeenCalled(); await worker.quiet(); expect(worker.onError).not.toHaveBeenCalled();
} finally { await worker.coordinator.stop(); vi.useRealTimers(); }
});
it("retains phone reply work after a failed state read and retries without intervening scans", async () => {
const f = await pushFixture();
vi.useFakeTimers({ toFake: ["Date", "setTimeout", "clearTimeout"], shouldAdvanceTime: true });
const transaction = vi.spyOn(db, "transaction").mockRejectedValueOnce(new Error("reply database unavailable"));
const worker = await replyWorker(f);
transaction.mockRestore();
try {
expect(worker.onError).toHaveBeenCalledOnce(); expect(f.transport.sendCallMessage).not.toHaveBeenCalled();
const retryAt = worker.coordinator.nextWakeAt()!;
expect(retryAt).toBeGreaterThan(Date.now() + 20_000); await worker.quiet(10_000);
await vi.advanceTimersByTimeAsync(retryAt - Date.now()); await worker.settled();
expect(f.transport.sendCallMessage).toHaveBeenCalledOnce(); expect(worker.onError).toHaveBeenCalledOnce(); await worker.quiet();
} finally { transaction.mockRestore(); await worker.coordinator.stop(); vi.useRealTimers(); }
});
it("notifies phone questions once and waits for authorized retrieval before later answers", async () => {
const f = await pushFixture(), interactionId = randomUUID();
await db.insert(issueThreadInteractions).values({id: interactionId, companyId: f.companyId, issueId: f.issueId, kind: "ask_user_questions", status: "pending", addresseeUserId: f.callerId, effectiveResolverPolicy: "addressee_only", payload: {version: 1, questions: [{id: "color", prompt: "Which color?", selectionMode: "single", required: true, options: [{id: "cobalt", label: "Cobalt"}]}]}});