fix: reconcile physique targeting for queued posts

This commit is contained in:
jjbrunton committed 2026-08-10 18:23:23 +01:00
1 parent d0a8bd0e2f
commit d8d635e1ee
10 files changed
+250 -12

No files matched your search

+52 -2
View File
@@ -26,13 +26,13 @@ function post(id: string, author: string, created = 100): RedditPost {
};
}
function prepared(candidate: RedditPost): ProcessedPost {
function prepared(candidate: RedditPost, physiqueScore: number | null = 5): ProcessedPost {
return {
post: candidate,
imageAnalysis: 'analysis',
detectedGender: 'female',
attractivenessScore: 64,
physiqueScore: 5,
physiqueScore,
physiqueTargeting: { enabled: true, minScore: 3, maxScore: 7 },
sourceImageUrl: 'https://i.redd.it/example.jpg',
generatedMessage: 'prepared message',
@@ -180,6 +180,56 @@ describe('CandidateQueueRepository', () => {
});
});
it('reconciles ready and retryable images when the physique range changes', () => {
queue.enqueue([
post('inside', 'alice', 300),
post('outside', 'bob', 200),
post('retry', 'carol', 100),
], 'image', 1, 1_000);
for (const worker of ['screen-1', 'screen-2', 'screen-3']) {
const candidate = queue.claimForScreening(worker, 100, 1_010)!;
const score = candidate.postId === 'outside' ? 7 : 4;
queue.markReady(candidate.postId, worker, prepared(candidate.post, score), 1_011);
}
const retry = queue.claimReady('account-a', 'send', 100, 1_012)!;
expect(retry.postId).toBe('inside');
queue.deferDelivery(retry.postId, 'send', 'rate limited', 2_000, 1_013);
expect(queue.reconcilePhysiqueTargeting(
{ enabled: true, minScore: 1, maxScore: 6 },
1_020,
)).toEqual({ retained: 2, rejected: 1 });
const active = queue.listActive();
expect(active.map((candidate) => candidate.postId)).toEqual(['inside', 'retry']);
expect(active.every((candidate) => (
candidate.prepared?.physiqueTargeting?.enabled === true
&& candidate.prepared.physiqueTargeting.maxScore === 6
))).toBe(true);
expect(queue.getStats().byStatus.rejected).toBe(1);
});
it('fails closed for an unscored prepared image but does not touch a leased send', () => {
queue.enqueue([
post('unscored', 'alice', 200),
post('sending', 'bob', 100),
], 'image', 1, 1_000);
for (const worker of ['screen-1', 'screen-2']) {
const candidate = queue.claimForScreening(worker, 100, 1_010)!;
queue.markReady(candidate.postId, worker, prepared(candidate.post, null), 1_011);
}
const sending = queue.claimReady('account-a', 'send', 100, 1_012)!;
expect(queue.reconcilePhysiqueTargeting(
{ enabled: true, minScore: 1, maxScore: 6 },
1_020,
)).toEqual({ retained: 0, rejected: 1 });
expect(queue.listActive()).toEqual([
expect.objectContaining({ postId: sending.postId, status: 'sending' }),
]);
});
it('lists only active candidates in newest-post order across stages', () => {
queue.enqueue([
post('waiting', 'alice', 100),