Dentolize · WhatsApp Conversation Race Fix Walkthrough
On this pageWhat to testCore scenariosEdges worth probing specificallySuggested automated test to request

For Quality

What to test

There is currently no automated test coverage for createOrUpdateOnlineConversation or findConversationByAddress (packages/whatsapp-official/src/services/online-conversation.service.ts) — no online-conversation.service.spec.ts exists, and messages.service.spec.ts doesn't exercise this path either. Anything you verify here today is manual/exploratory until tests are added.

Core scenarios

  1. Baseline (no race): a brand-new contact sends one message. Confirm a

conversation is created, the message is attached, and the bot replies. (Unchanged by this PR — regression check.)

  1. The race, by phone: simulate two webhook deliveries for the same new

phone number arriving concurrently (e.g., fire two requests to the messages webhook endpoint back-to-back with no await between them, for a phone number that has no existing conversation). Expect: one conversation row created, both messages attached to it, no error surfaced, no message silently dropped.

  1. The race, by bsuid: same as above but for a contact with no wa_id

(only a business-scoped ID / masked number — see glossary). Same expected outcome.

  1. where.id collision: a conversation already exists (matched by id via

a prior lookup or a phone merge), and the concurrent update's phone or bsuid value collides with a different existing conversation's address. Expect: the update still succeeds and the message still lands, but the colliding address field is not overwritten on either row (check both rows keep their original phone/bsuid afterward).

  1. Genuinely unrecoverable error: force a P2002 where

findConversationByAddress can't resolve anything (e.g., neither phone nor bsuid present in create). Expect the original error to propagate, not be swallowed.

  1. Non-P2002 errors: any other Prisma/DB error during the initial upsert

should still propagate unchanged — this PR should not widen the set of errors that get caught.

Edges worth probing specifically

  • Ordering of the two racing requests: does it matter which one "wins" the

insert? It shouldn't — verify both orderings land the same way.

  • More than two concurrent deliveries for the same brand-new contact (3+).

The fix's fallback re-queries by address rather than assuming a fixed winner, so this should generalize, but it's untested — worth a dedicated case given the low cost of adding one.

  • The known non-fix: confirm (and don't file as a regression) that when a

patient is legitimately known under two different addresses that already belong to two different conversation rows, this PR leaves both rows as they are rather than merging them (online-conversation.service.ts:131-132).

  • Message ordering after recovery: when the fallback path runs, confirm

messagesCount/unseenCount still increment correctly and lastMessage/lastActivity still update, since the retried update reuses the original update object minus phone/bsuid.

Suggested automated test to request

A unit test around createOrUpdateOnlineConversation with a mocked Prisma client that throws a P2002 on the first upsert call and asserts the fallback path calls findConversationByAddress (or uses where.id directly) and then issues a plain update with phone/bsuid stripped — covering scenarios 2–6 above without needing real concurrency.