اگر آپ کے ای میل ٹیسٹ آپ کے لیپ ٹاپ پر بالکل ٹھیک چلتے ہیں لیکن CI پر پہنچتے ہی ناکام ہو جاتے ہیں، تو آپ اکیلے نہیں ہیں۔ عام جواب یہ ہوتا ہے کہ ٹیسٹ کوڈ میں sleep کالز ڈال دی جائیں یا ری ٹرائی (retry) کی تعداد بڑھا دی جائے جب تک کہ بلڈ پاس نہ ہو جائے۔ یہ شاید ایک دن کے لیے شور کو خاموش کر دے، لیکن یہ بگ (bug) کو ٹھیک نہیں کرتا۔ یہ صرف اسے چھپاتا ہے۔
اصل مسئلہ یہ ہے کہ آپ کا ٹیسٹ یہ کیسے پہچانتا ہے کہ کون سا ای میل کھولنا ہے۔
The Shared Inbox Problem
اپنی لوکل مشین پر، آپ ایک وقت میں ایک ہی ٹیسٹ چلاتے ہیں۔ ایک ای میل آتی ہے۔ آپ اسے پکڑ لیتے ہیں۔ سادہ سی بات ہے۔
CI ایک بالکل مختلف ماحول ہے۔ ایک ہی پل ریکوسٹ (pull request) چار، آٹھ، یا سولہ متوازی (parallel) جابز کو ٹرگر کر سکتی ہے۔ اگر وہ سب ایک ہی ٹیسٹ ان باکس شیئر کرتے ہیں—چاہے وہ Mailosaur سرور ہو، Mailtrap ان باکس ہو، یا اسٹیجنگ ڈومین پر کوئی حقیقی اکاؤنٹ—تو وہ سب ایک ہی وقت میں ایک ہی جگہ (bucket) میں لکھ رہے ہوتے ہیں۔ جاب A پاس ورڈ ری سیٹ بھیجتی ہے۔ جاب B دعوت نامہ (invite) بھیجتی ہے۔ جاب C ایک ناکام ویلکم فلو کو دوبارہ کوشش کرتی ہے۔ اس دوران، بیک گراؤنڈ ورکرز اور ڈیلیوری کیوز (queues) وہ جیٹر (jitter) پیدا کرتے ہیں جسے آپ کنٹرول نہیں کر سکتے۔
جب ہر جاب اس مشترکہ ان باکس میں پہنچتی ہے اور سبجیکٹ "Reset your password" والا تازہ ترین پیغام مانگتی ہے، تو یہ ایک ریس (race) بن جاتی ہے۔ جو ٹیسٹ جیت جاتا ہے اسے صحیح ای میل ملتی ہے۔ جو ٹیسٹ ہار جاتا ہے وہ کسی دوسری جاب کے لیے بنائی گئی لنک پر کلک کرتا ہے، غلط مواد کے خلاف اسسرشن (assertion) کرتا ہے، اور ایک ایسے ایرر کے ساتھ ناکام ہو جاتا ہے جو ٹائمنگ کا مسئلہ لگتا ہے۔ یہ ٹائمنگ کا مسئلہ نہیں ہے۔ یہ شناخت (identity) کا مسئلہ ہے۔
Why "Newest Message" Fails
یہ کمزور پیٹرن (brittle pattern) اپنا لینا آسان ہے کیونکہ یہ فطری محسوس ہوتا ہے:
- یوزر فلو کو ٹرگر کریں۔
- ہر چند سیکنڈ بعد ان باکس کو پول (poll) کریں۔
- سبجیکٹ لائن سے میچ کرنے والا تازہ ترین پیغام کھولیں۔
- پہلے لنک پر کلک کریں اور اسسرشنز چلائیں۔
یہ سادہ پیرا للیزم (parallelism) کے علاوہ کئی وجوہات کی بنا پر ناکام ہو جاتا ہے۔ پچھلے ناکام رن کا ایک ری ٹرائی دیر سے آ سکتا ہے، اور اچانک آپ کے موجودہ ٹیسٹ کے پول کرنے کے وقت ہی تازہ ترین پیغام بن سکتا ہے۔ آپ کی ایپلی کیشن کے اندر بیک گراؤنڈ ورکرز دو ای میلز کو قطار میں لگا سکتے ہیں اور پہلی سے پہلے دوسری ای میل ڈیلیور کر سکتے ہیں۔ صرف سبجیکٹ لائنز کمزور شناختی علامات ہیں؛ آپ کی اسٹیجنگ ایپلی کیشن مختلف راستوں سے ملتے جلتے ای میلز بھیج سکتی ہے۔ ٹائم اسٹیمپ (timestamp) کے ذریعے ترتیب دینا اس سے بھی زیادہ خراب ہے جتنا یہ نظر آتا ہے کیونکہ CI رنر اور میل فراہم کنندہ کے درمیان کلاک سکو (clock skew) ایک حقیقت ہے، اور میل APIs اکثر اپنے انڈیکس کو کیش (cache) یا بیچ (batch) کرتے ہیں۔
مصروف ماحول میں ٹائم اسٹیمپ دھندلے ہو جاتے ہیں۔ آپ کو کسی براہ راست چیز کی ضرورت ہے۔
What a Run Token Actually Is
رن ٹوکن (run token) ایک منفرد اسٹرنگ (unique string) کے سوا کچھ نہیں ہے جو آپ کے ٹیسٹ کے آغاز پر تیار کی جاتی ہے اور اس ای میل میں شامل کر دی جاتی ہے جو آپ کی ایپلی کیشن بھیجتی ہے۔ اسے صارف کے سامنے دکھانے کی ضرورت نہیں ہے، اور اسے خوبصورت نظر آنے کی بھی ضرورت نہیں ہے۔ اسے صرف اس بات کی ضمانت دینی چاہیے کہ آپ ثابت کر سکیں کہ یہ مخصوص پیغام اسی مخصوص ٹیسٹ کے عمل (execution) سے تعلق رکھتا ہے۔
ٹھوس مثالیں بہترین کام کرتی ہیں۔ ٹیسٹ شروع ہونے سے پہلے، ایک ٹوکن تیار کریں جیسے کہ:
- ایک UUID:
550e8400-e29b-41d4-a716-446655440001 - ایک بلڈ اسکوپڈ ریکوسٹ آئی ڈی:
req_ci_build_4821_a7f3 - ایک انوائٹ سلگ (invite slug) یا میٹا ڈیٹا کافکس:
signup-token-8k2m9n - ٹیسٹ رنر کے ذریعے تیار کردہ ایک رینڈم ہیکسا ڈیسیمل اسٹرنگ:
test-run-a4f9c2d1
اگر آپ بیک اینڈ کوڈ کو کنٹرول کرتے ہیں، تو ٹوکن کو ای میل کانٹیکسٹ میں پاس کریں اور اسے باڈی میں کہیں رینڈر کریں۔ اگر آپ کسی بلیک باکس ایپلی کیشن کے خلاف ٹیسٹ کر رہے ہیں، تو دیکھیں کہ کیا ایپ پہلے سے ہی کوئی ایسا ریفرنس فیلڈ قبول کرتی ہے جسے آپ استعمال کر سکیں۔ اگر نہیں، تو آپ کبھی کبھی وصول کنندہ کے لوکل پارٹ (local-part) میں پلس ایڈریسنگ کا استعمال کرتے ہوئے ٹوکن کو ایمبیڈ کر سکتے ہیں—testuser+a4f9c2d1@example.com—اگرچہ یہ صرف اسی صورت میں کام کرتا ہے اگر آپ کی ایپلی کیشن اسے محفوظ رکھے اور ای میل میں واپس دکھائے۔
مقصد اس میٹا ڈیٹا پر میچ کرنا بند کرنا ہے جس پر میل سسٹم کا پہلے سے قبضہ ہے۔ اس ڈیٹا پر میچ کریں جس پر آپ کے ٹیسٹ کا قبضہ ہے۔
The Reliable Pattern
"تازہ ترین پیغام" کے الگورتھم کو ایک محدود، ٹوکن پر مبنی تلاش سے بدل دیں:
- کسی بھی فلو کو ٹرگر کرنے سے پہلے رن ٹوکن تیار کریں۔
- یوزر ایکشن شروع کریں، اس بات کو یقینی بنائیں کہ ایپلی کیشن آؤٹ باؤنڈ ای میل میں ٹوکن شامل کرے گی۔
- اس ٹوکن تک محدود فلٹرز کے ساتھ میل فراہم کنندہ کو پول کریں۔ اگر API باڈی سرچ کی اجازت دیتی ہے، تو اسے استعمال کریں۔ اگر نہیں، تو امیدوار پیغامات (candidate messages) حاصل کریں اور کلائنٹ سائیڈ پر ان کی باڈی کو
grepکریں۔ - کسی بھی لنک، بٹن، یا ویریفیکیشن کوڈ کو چھونے سے پہلے اس بات کی تصدیق (assert) کریں کہ ٹوکن پیغام کی باڈی میں موجود ہے۔
- اس کے بعد ہی کنفرمیشن URL یا کوڈ نکالیں اور آگے بڑھیں۔
یہ ترتیب اہم ہے۔ اگر آپ پہلے لنک نکالتے ہیں اور دوسرے نمبر پر ٹوکن چیک کرتے ہیں، تو آپ پہلے ہی غلط ای میل پر کلک کر چکے ہوں گے۔ اسسرشن (assertion) آپ کا گیٹ کیپر ہے۔
عملی طور پر، آپ کے ہیلپر کو Subject:"Welcome to AppName" sort:-received کے بجائے Subject:"Welcome to AppName" AND Body:"a4f9c2d1" تلاش کرنا چاہیے۔ بہت سی میل ٹیسٹنگ سروسز ایسی سرچ APIs فراہم کرتی ہیں جو باڈی مواد کے فلٹرز کو قبول کرتی ہیں۔ انہیں استعمال کریں۔ اگر آپ کسی سادہ فراہم کنندہ (provider) کے ساتھ کام کر رہے ہیں، تو اپنی پولنگ لاجک کو ایک ہی جگہ رکھیں تاکہ آپ ہر ٹیسٹ میں مستقل طور پر کلائنٹ سائیڈ فلٹرنگ شامل کر سکیں۔
سسٹم کو درست رکھنے کے تین اصول
ایک رن ٹوکن (run token) انتخاب کو یقینی بناتا ہے، لیکن آپ کو اس بات پر نظم و ضبط برقرار رکھنے کی ضرورت ہے کہ آپ پولنگ کیسے کرتے ہیں اور جب چیزیں غلط ہو جائیں تو آپ کیا کرتے ہیں۔
ناکامی کی صورت میں ان باکس کی حالت کو لاگ (log) کریں۔ جب کوئی ٹیسٹ فیل ہو جائے، تو ان باکس آئیڈنٹیفائر، وہ سبجیکٹ لائن جو آپ نے مطلوب کی تھی، درست ٹائم اسٹیمپ ونڈو، اور کتنے پیغامات آپ کے معیار پر پورا اترتے ہیں، یہ سب آؤٹ پٹ کے طور پر دکھائیں۔ یہ ایک مبہم "ای میل نہیں ملی" (email not found) کی غلطی کو ایک ٹھوس کہانی میں بدل دیتا ہے۔ اگر جاب 7823 نے جاب 7821 کا ری ٹرائی (retry) پیغام اس لیے اٹھا لیا کیونکہ وہ تین سیکنڈ بعد پہنچا تھا، تو آپ کے لاگز کو یہ بات واضح کرنی چاہیے۔ اس سیاق و سباق کے بغیر، آپ وقت (timing) کو قصوروار ٹھہرائیں گے اور ایک اور 'sleep' شامل کر دیں گے۔
تمام ای میل پولنگ کو ایک ہی ہیلپر فائل میں رکھیں۔ setTimeout اور cy.task کے کالز کو بیس میل ٹیسٹ فائلوں میں نہ بکھیریں۔ اس لاجک کو مرکزی حیثیت دیں جو پیغامات کا انتظار کرتی ہے، API کال کو دوبارہ کوشش (retry) کرتی ہے، اور بیک آف (backoff) لاگو کرتی ہے۔ اگر ہر ٹیسٹ ایک ہی ہیلپر استعمال کرتا ہے، تو آپ کے فلٹرنگ کے اصول مستقل رہیں گے، اور جب آپ سرچ لاجک کو بہتر بنائیں گے، تو ہر ٹیسٹ کو فائدہ ہوگا۔ اس سے ٹوکن چیک کو نافذ کرنا بھی آسان ہو جاتا ہے؛ اگر ہیلپر کو ٹوکن آرگیومنٹ کی ضرورت ہو، تو کوئی بھی غلطی سے "تازہ ترین پیغام" (latest message) کے سہارے پر واپس نہیں جا سکے گا۔
اپنے ری ٹرائیز (retries) پر نظر رکھیں۔ CI میں ٹیسٹ ری ٹرائیز عام ہیں، لیکن ہر ری ٹرائی ان باکس میں ایک اور ای میل پیدا کرتا ہے۔ اگر آپ کا ٹیسٹ تیسری کوشش میں پاس ہو جاتا ہے، تو آپ جشن منا کر آگے بڑھ سکتے ہیں۔ لیکن آپ یہ نظر انداز کر دیتے ہیں کہ پہلی اور دوسری کوششوں نے ایک حقیقی بگ (bug) کو بے نقاب کیا تھا—جیسے کہ ریس کنڈیشن (race condition)، ڈپلیکیٹ سینڈ، یا مسنگ انڈیکس—جسے اضافی پیغامات نے چھپا دیا۔ اگر آپ کو ری ٹرائیز استعمال کرنے ہی پڑیں، تو چیک کریں کہ کیا ناکامی کے بعد ان باکس میں غیر متوقع ڈپلیکیٹس موجود ہیں۔ اس سے بھی بہتر یہ ہے کہ اگر آپ کا فراہم کنندہ ڈائنامک ان باکسز کو سپورٹ کرتا ہے، تو ان باکس کو صاف کرنے یا ہر جاب کے لیے ایک منفرد ایڈریس استعمال کرنے پر غور کریں۔ ری ٹرائیز کو غیر قابل اعتماد انتخاب لاجک کو چھپانے کی حکمت عملی نہیں بننا چاہیے۔
اصل سبق
ان باکس کو تاریخ کے لحاظ سے ترتیب دینا اور پہلا نتیجہ حاصل کرنا ٹیسٹنگ نہیں ہے۔ یہ کوڈ کے لبادے میں چھپا ہوا ایک اندازہ ہے۔ ایک رن ٹوکن کی قیمت تقریباً کچھ بھی نہیں ہے—ایک اسٹرنگ ویری ایبل، ایک اضافی فلٹر پیرامیٹر، شاید ٹیمپلیٹ میں ایک چھوٹا سا بدلاؤ—اور یہ آپ کے ٹیسٹ کو ایک یقینی شناخت (deterministic identity) دیتا ہے۔ یہ ثابت کرتا ہے کہ آپ کے سامنے موجود پیغام اسی رن سے تعلق رکھتا ہے جسے آپ اس وقت چلا رہے ہیں۔
'sleeps' شامل کرنا اور نیٹ ورک کے درست طریقے سے کام کرنے کی امید کرنا بند کریں۔ ایک ٹوکن بنائیں، اسے ای میل میں ڈالیں، اور اسے براہ راست تلاش کریں۔ آپ کے CI رنز تیز ہوں گے، آپ کے لاگز پڑھنے کے قابل ہوں گے، اور آخر کار آپ اس بات پر بھروسہ کر سکیں گے جو آپ کا ای میل سویٹ (email suite) آپ کو بتا رہا ہے۔
