જો તમારા ઈમેલ ટેસ્ટ તમારા લેપટોપ પર બરાબર ચાલે છે અને CI પર આવતાની સાથે જ નિષ્ફળ જાય છે, તો તમે એકલા નથી. સામાન્ય પ્રતિસાદ એ છે કે ટેસ્ટ કોડમાં sleep કોલ્સ ઉમેરવા અથવા બિલ્ડ પાસ થાય ત્યાં સુધી રીટ્રાય કાઉન્ટ વધારવો. તે કદાચ એક દિવસ માટે ઘોંઘાટ શાંત કરી શકે છે, પરંતુ તે બગને ઠીક કરતું નથી. તે માત્ર તેને છુપાવે છે.

વાસ્તવિક સમસ્યા એ છે કે તમારો ટેસ્ટ કયો ઈમેલ ખોલવો તે કેવી રીતે ઓળખે છે.

શેર્ડ ઇનબોક્સની સમસ્યા (The Shared Inbox Problem)

તમારા લોકલ મશીન પર, તમે એક સમયે એક જ ટેસ્ટ ચલાવો છો. એક ઈમેલ આવે છે. તમે તેને મેળવો છો. સરળ.

CI એ તદ્દન અલગ વાતાવરણ છે. એક સિંગલ પુલ રિક્વેસ્ટ (pull request) ચાર, આઠ અથવા સોળ સમાંતર (parallel) જોબ્સ ટ્રિગર કરી શકે છે. જો તેઓ બધા એક જ ટેસ્ટ ઇનબોક્સ શેર કરે છે—પછી ભલે તે Mailosaur સર્વર હોય, Mailtrap ઇનબોક્સ હોય, અથવા સ્ટેજિંગ ડોમેન પરનું સાચું એકાઉન્ટ હોય—તેઓ બધા એક જ સમયે એક જ બકેટમાં લખી રહ્યા હોય છે. જોબ A પાસવર્ડ રીસેટ મોકલે છે. જોબ B આમંત્રણ (invite) મોકલે છે. જોબ C નિષ્ફળ ગયેલા વેલકમ ફ્લોને ફરીથી પ્રયાસ કરે છે. આ દરમિયાન, બેકગ્રાઉન્ડ વર્કર્સ અને ડિલિવરી ક્યુઝ એવો જિટર (jitter) ઉમેરે છે જેને તમે નિયંત્રિત કરી શકતા નથી.

જ્યારે દરેક જોબ તે શેર્ડ ઇનબોક્સમાં જાય છે અને "Reset your password" વિષય ધરાવતો સૌથી નવો મેસેજ માંગે છે, ત્યારે તે એક રેસ બની જાય છે. જે ટેસ્ટ જીતે છે તેને સાચો ઈમેલ મળે છે. જે ટેસ્ટ હારે છે તે બીજા જોબ માટે બનાવેલી લિંક પર ક્લિક કરે છે, ખોટા કન્ટેન્ટ સામે એસરટ (assert) કરે છે, અને એવી ભૂલ સાથે નિષ્ફળ જાય છે જે ટાઈમિંગની સમસ્યા જેવી લાગે છે. તે ટાઈમિંગની સમસ્યા નથી. તે ઓળખ (identity) ની સમસ્યા છે.

"સૌથી નવો મેસેજ" કેમ નિષ્ફળ જાય છે

આ નાજુક પેટર્નમાં પડવું સરળ છે કારણ કે તે સહજ લાગે છે:

  1. યુઝર ફ્લો ટ્રિગર કરો.
  2. દર થોડી સેકન્ડે ઇનબોક્સ તપાસો (poll).
  3. વિષય (subject line) સાથે મેચ થાય તેવો સૌથી તાજેતરનો મેસેજ ખોલો.
  4. પહેલી લિંક પર ક્લિક કરો અને એસરશન્સ (assertions) ચલાવો.

આ માત્ર સમાંતરતા (parallelism) સિવાયના અન્ય ઘણા કારણોસર તૂટી પડે છે. અગાઉના નિષ્ફળ રનમાંથી રીટ્રાય મોડો આવી શકે છે, જે તમારી વર્તમાન ટેસ્ટ પોલ (poll) કરતી વખતે અચાનક સૌથી નવો મેસેજ બની જાય છે. તમારા એપ્લિકેશનમાં બેકગ્રાઉન્ડ વર્કર્સ બે ઈમેલ ક્યુમાં રાખી શકે છે અને પ્રથમ કરતા બીજા ઈમેલને પહેલા ડિલિવર કરી શકે છે. વિષય (subject lines) એકલા હાથે નબળા ઓળખકર્તા છે; તમારું સ્ટેજિંગ એપ્લિકેશન અલગ અલગ પાથથી સમાન ઈમેલ મોકલી શકે છે. ટાઈમસ્ટેમ્પ દ્વારા સોર્ટિંગ જોવામાં આવતું હોય તેના કરતા વધુ ખરાબ છે કારણ કે CI રનર અને મેઈલ પ્રોવાઈડર વચ્ચેનો ક્લોક સ્કીવ (clock skew) વાસ્તવિક છે, અને મેઈલ API ઘણીવાર તેમના ઇન્ડેક્સને કેશ (cache) અથવા બેચ (batch) કરે છે.

વ્યસ્ત વાતાવરણમાં ટાઈમસ્ટેમ્પ અસ્પષ્ટ બની જાય છે. તમારે કંઈક સીધું જોઈએ છે.

રન ટોકન (Run Token) ખરેખર શું છે

રન ટોકન એ તમારા ટેસ્ટની શરૂઆતમાં જનરેટ કરવામાં આવેલ એક યુનિક સ્ટ્રિંગ છે જે તમારી એપ્લિકેશન દ્વારા મોકલવામાં આવતા ઈમેલમાં ઇન્જેક્ટ કરવામાં આવે છે. તેને યુઝર-ફેસિંગ હોવું જરૂરી નથી, અને તે દેખાવમાં સુંદર હોવું જરૂરી નથી. તેને ફક્ત એટલી જ ખાતરી કરવાની જરૂર છે કે તમે સાબિત કરી શકો કે આ ચોક્કસ મેસેજ આ ચોક્કસ ટેસ્ટ એક્ઝિક્યુશનનો છે.

નક્કર ઉદાહરણો શ્રેષ્ઠ રીતે કામ કરે છે. ટેસ્ટ શરૂ થાય તે પહેલાં, આ પ્રકારના ટોકન જનરેટ કરો:

  • એક UUID: 550e8400-e29b-41d4-a716-446655440001
  • બિલ્ડ-સ્કોપ્ડ રિક્વેસ્ટ ID: req_ci_build_4821_a7f3
  • ઇન્વાઇટ સ્લગ અથવા મેટાડેટા સફિક્સ: signup-token-8k2m9n
  • ટેસ્ટ રનર દ્વારા જનરેટ કરેલ રેન્ડમ હેક્સ સ્ટ્રિંગ: test-run-a4f9c2d1

જો તમે બેકએન્ડ કોડને નિયંત્રિત કરો છો, તો ટોકનને ઈમેલ કોન્ટેક્સ્ટમાં પાસ કરો અને તેને બોડીમાં ક્યાંક રેન્ડર કરો. જો તમે બ્લેક-બોક્સ એપ્લિકેશન સામે ટેસ્ટ કરી રહ્યા હોવ, તો જુઓ કે એપ પહેલેથી જ કોઈ રેફરન્સ ફીલ્ડ સ્વીકારે છે કે નહીં જેને તમે હાઇજેક કરી શકો. જો નહીં, તો તમે ક્યારેક પ્લસ એડ્રેસિંગનો ઉપયોગ કરીને રિસિપિયન્ટ લોકલ-પાર્ટમાં ટોકન એમ્બેડ કરી શકો છો—testuser+a4f9c2d1@example.com—જોકે તે ત્યારે જ કામ કરે છે જો તમારી એપ્લિકેશન તેને સાચવે અને ઈમેલમાં પાછું મોકલે.

મુદ્દો એ છે કે મેઈલ સિસ્ટમ પાસે પહેલેથી જ રહેલા મેટાડેટા પર મેચ કરવાનું બંધ કરો. એ ડેટા પર મેચ કરો જે તમારા ટેસ્ટના અધિકારમાં છે.

વિશ્વસનીય પેટર્ન (The Reliable Pattern)

"સૌથી નવો મેસેજ" અલ્ગોરિધમને બદલે સાંકડી, ટોકન-ડ્રિવન સર્ચનો ઉપયોગ કરો:

  1. કોઈપણ ફ્લો ટ્રિગર કરતા પહેલા રન ટોકન જનરેટ કરો.
  2. યુઝર એક્શન શરૂ કરો, ખાતરી કરો કે એપ્લિકેશન આઉટબાઉન્ડ ઈમેલમાં ટોકન સામેલ કરશે.
  3. તે ટોકન સુધી મર્યાદિત ફિલ્ટર્સ સાથે મેઈલ પ્રોવાઈડરને પોલ (poll) કરો. જો API બોડી સર્ચને સપોર્ટ કરે છે, તો તેનો ઉપયોગ કરો. જો નહીં, તો ઉમેદવાર મેસેજ મેળવો અને ક્લાયન્ટ-સાઇડ પર તેમની બોડી grep કરો.
  4. કોઈપણ લિંક, બટન અથવા વેરિફિકેશન કોડને અડતા પહેલા મેસેજ બોડીમાં ટોકન છે તેની ખાતરી (assert) કરો.
  5. ત્યારબાદ જ કન્ફર્મેશન URL અથવા કોડ એક્સટ્રેક્ટ કરો અને આગળ વધો.

આ ક્રમ મહત્વનો છે. જો તમે પહેલા લિંક એક્સટ્રેક્ટ કરો છો અને પછી ટોકન તપાસો છો, તો તમે પહેલેથી જ ખોટા ઈમેલ પર ક્લિક કરી દીધું છે. એસરશન (Assertion) એ તમારો ગેટકીપર છે.

Practically, your helper should look for Subject:"Welcome to AppName" AND Body:"a4f9c2d1" rather than Subject:"Welcome to AppName" sort:-received. Many mail testing services expose search APIs that accept body content filters. Use them. If you are working against a simpler provider, keep your polling logic in one place so you can add client-side filtering consistently across every test.

Three Rules to Keep the System Honest

A run token fixes selection, but you still need discipline around how you poll and what you do when things go wrong.

Log the inbox state on failure. When a test fails, output the inbox identifier, the subject line you queried, the exact timestamp window, and how many messages matched your criteria. This turns a vague "email not found" error into a concrete story. If job 7823 picked up a retry message from job 7821 because it arrived three seconds later, your logs should make that obvious. Without this context, you will blame timing and add another sleep.

Keep all email polling in one helper file. Do not scatter setTimeout and cy.task calls across twenty test files. Centralize the logic that waits for messages, retries the API call, and applies backoff. If every test uses the same helper, your filtering rules stay consistent, and when you improve the search logic, every test benefits. It also makes it easier to enforce the token check; if the helper requires a token argument, no one can accidentally fall back to the "latest message" crutch.

Watch your retries. Test retries are common in CI, but each retry creates another email in the inbox. If your test passes on attempt three, you might celebrate and move on. What you miss is that attempts one and two exposed a real bug—a race condition, a duplicate send, or a missing index—that extra messages masked. If you must use retries, check whether the inbox contains unexpected duplicates after a failure. Better yet, consider cleaning the inbox or using a unique address per job if your provider supports dynamic inboxes. Retries should not become a strategy for absorbing unreliable selection logic.

The Real Takeaway

Sorting an inbox by date and grabbing the top result is not testing. It is guessing dressed up in code. A run token costs almost nothing—one string variable, one extra filter parameter, maybe a small template change—and it gives your test deterministic identity. It proves the message in front of you belongs to the run you are executing right now.

Stop adding sleeps and hoping the network behaves. Generate a token, put it in the email, and search for it directly. Your CI runs will be faster, your logs will be readable, and you will finally trust what the email suite is telling you.