Pace candidate funnel to send capacity

This commit is contained in:
jjbrunton committed 2026-08-09 16:28:12 +01:00
1 parent 910576921b
commit 70c156e1f7
7 files changed
+113 -13

No files matched your search

+31 -1
View File
@@ -1,5 +1,9 @@
import { describe, expect, it, vi } from 'vitest';
import { currentHourlySendCount, startNonOverlappingSchedule } from '../src/main-loop.js';
import {
calculateCandidateFunnelTarget,
currentHourlySendCount,
startNonOverlappingSchedule,
} from '../src/main-loop.js';
describe('startNonOverlappingSchedule', () => {
it('does not begin a second response check until the prior check settles', async () => {
@@ -43,3 +47,29 @@ describe('currentHourlySendCount', () => {
expect(currentHourlySendCount({ getStats: () => ({ messagesThisHour: 1 }) })).toBe(1);
});
});
describe('calculateCandidateFunnelTarget', () => {
it('tracks remaining outbound capacity instead of growing to a fixed backlog', () => {
expect(calculateCandidateFunnelTarget(
[{ messagesRemaining: 3 }, { messagesRemaining: 5 }],
{ maxReadyCandidates: 25, recentWindowMinutes: 60, minDelayBetweenMessagesMs: 300_000 },
)).toBe(8);
});
it('retains the configured ceiling and caps capacity by the freshness window', () => {
expect(calculateCandidateFunnelTarget(
[{ messagesRemaining: 100 }, { messagesRemaining: 100 }],
{ maxReadyCandidates: 25, recentWindowMinutes: 30, minDelayBetweenMessagesMs: 300_000 },
)).toBe(12);
});
it('stops growing inventory when no running account can send', () => {
const options = {
maxReadyCandidates: 25,
recentWindowMinutes: 60,
minDelayBetweenMessagesMs: 300_000,
};
expect(calculateCandidateFunnelTarget([], options)).toBe(0);
expect(calculateCandidateFunnelTarget([{ messagesRemaining: 0 }], options)).toBe(0);
});
});