mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-03 17:51:58 +01:00
test(server): stop voice report waits from crossing refresh deadlines (#18628)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Voice calls retain reports that background work refreshes for a finite period. > - Tests must check backlog processing without starting later refreshes. > - The backlog test advances fake time while real database work is still running. > - On a slow runner, this starts refreshes and breaks the expected request count. > - This pull request advances time only to scheduled passes before the first refresh. > - The test can check each report once and still prove that the worker becomes quiet. ## Linked Issues or Issue Description Refs #18607. This is a separate test failure from that PR's task-list avatar changes. The test was added in #17586 and is unchanged by #18607. **What happened?** [Server shard 5/12](https://github.com/paperclipai/paperclip/actions/runs/41472540522/job/125369899753) failed in `voice-call-reports.test.ts`, in "bounds report reconstruction and resets a partial scan when the same owner restarts". The test expected zero active work but found two active holds. Its logs contain 170 requests for 105 sessions, including 65 second attempts. The final `ci / verify` failure follows from this failed shard. **Expected behavior** Process all 105 reports once. Wait for each pass to settle before advancing to another scheduled pass. Keep the clock before the first 60-second refresh deadline. **Steps to reproduce** 1. Run the named test against a disposable PostgreSQL database. 2. Temporarily delay the first provider response by 3,250 ms with `node:timers/promises`. 3. Use the same 15-second wait budget for the old and new versions. 4. The old polling makes 315 requests across three attempts per session and fails. The new polling makes 105 requests, one per session, and passes. **Paperclip version or commit** Observed at PR head `7f1d137a53489e2a45ba05e506b5ba17b51d5882`. **Deployment mode** GitHub Actions. Local verification uses disposable PostgreSQL 17. ## What Changed - Keep fake time fixed while a report pass waits on database work. - Advance to the coordinator's next scheduled pass only before the first report refresh. - Disable implicit fake-time advancement in the assertion polling. - Allow 15 seconds for the 105-report wait and 30 seconds for the full fixture test. - Retain all assertions for bounded reconstruction, restart, exact session IDs, one attempt per report, and no later database or provider activity. ## Verification - Controlled comparison with the same injected delay and 15-second wait: old polling fails with 315 requests; fixed polling passes with 105 requests. The temporary delay is removed from this change. - All 52 tests pass across `voice-call-reports.test.ts`, `scoped-deadline-work.test.ts`, and `delivery-work-coordinator.test.ts`. - `git diff --check` passes. - The initial embedded PostgreSQL run could not start because its macOS package lacks a `libzstd.1.dylib` alias. The passing tests use a separate disposable PostgreSQL 17 cluster. - The full 15-test voice-report suite passes again after rebasing onto current master. - `pnpm -r typecheck` passes. - `pnpm test:run` was started, then stopped during chat-suite startup after no tests completed in that invocation. It has no complete result. Its cause is not established. - `pnpm build` passes. - Greptile gives 5/5 with no inline findings on `caa6db568deb9df98437d1a532cc345e9f5b16dc`. Its check completed successfully for that exact commit. - The first CI attempt passed all 12 server shards, including the voice-report regression. Three other jobs were interrupted: Runner Vitest 2/2 received a runner shutdown signal; Typecheck + Release Registry and E2E shard 5/8 have GitHub annotations reporting lost runner communication. These are incomplete checks, not passing retries. [First attempt](https://github.com/paperclipai/paperclip/actions/runs/41488319181/attempts/1). - The single retry passed for all three interrupted jobs. The final `ci / e2e` and `ci / verify` gates passed. [Successful second attempt](https://github.com/paperclipai/paperclip/actions/runs/41488319181/attempts/2). - Final commit `caa6db568deb9df98437d1a532cc345e9f5b16dc`: all 56 reported checks are terminal and clean. The two optional Storybook checks are skipped. GitHub reports the PR as clean and mergeable. ## Risks - Low risk. The change affects one integration test. Production code and report refresh behavior are unchanged. - The test still has a finite wall-clock timeout. A database that cannot process the fixture within that budget will fail. ## Model Used OpenAI GPT-6 through Codex, with repository tools and code execution. The runtime does not expose a more specific model ID or context-window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
40b8075916
commit
d26628e35a
1 file changed
+8
-3
@@ -122,16 +122,21 @@ const support = external ? { supported: true } : await getEmbeddedPostgresTestSu
|
|||||||
await quiet(); expect(fetchReport).not.toHaveBeenCalled();
|
await quiet(); expect(fetchReport).not.toHaveBeenCalled();
|
||||||
await vi.advanceTimersByTimeAsync(deadline - Date.now());
|
await vi.advanceTimersByTimeAsync(deadline - Date.now());
|
||||||
await vi.waitFor(async () => {
|
await vi.waitFor(async () => {
|
||||||
await vi.advanceTimersByTimeAsync(1000);
|
// Keep time fixed during database I/O. Advancing it on every poll can
|
||||||
|
// make the first reports due again before the backlog has settled.
|
||||||
|
const next = coordinator!.nextWakeAt();
|
||||||
|
if (next !== null && next < deadline + 60_000) {
|
||||||
|
await vi.advanceTimersByTimeAsync(Math.max(0, next - Date.now()));
|
||||||
|
}
|
||||||
expect(idleWorkSnapshot().active).toBe(0);
|
expect(idleWorkSnapshot().active).toBe(0);
|
||||||
expect(fetchReport).toHaveBeenCalledTimes(105);
|
expect(fetchReport).toHaveBeenCalledTimes(105);
|
||||||
}, { timeout: 5000 });
|
}, { interval: 0, timeout: 15_000 });
|
||||||
expect(fetchReport.mock.calls.map(([call]) => call.id).sort()).toEqual(ids.sort());
|
expect(fetchReport.mock.calls.map(([call]) => call.id).sort()).toEqual(ids.sort());
|
||||||
expect((await db.select().from(chatVoiceReports).where(eq(chatVoiceReports.companyId, companyId)))
|
expect((await db.select().from(chatVoiceReports).where(eq(chatVoiceReports.companyId, companyId)))
|
||||||
.every(row => row.attempts === 1)).toBe(true);
|
.every(row => row.attempts === 1)).toBe(true);
|
||||||
await quiet();
|
await quiet();
|
||||||
} finally { reads.mockRestore(); }
|
} finally { reads.mockRestore(); }
|
||||||
});
|
}, 30_000);
|
||||||
it("leaves configured idle, live calls, old history and unidentified calls quiet", async () => {
|
it("leaves configured idle, live calls, old history and unidentified calls quiet", async () => {
|
||||||
await session({ state: "active", endedAt: null });
|
await session({ state: "active", endedAt: null });
|
||||||
await session({ endedAt: new Date(Date.now() - day - 1000) });
|
await session({ endedAt: new Date(Date.now() - day - 1000) });
|
||||||
|
|||||||
Reference in new issue
Block a user