با تست تصادفی (fuzzing) تو کلاینت HTTP خودش ۱۱ تا باگ پیدا کرد
خلاصهٔ کاملتر
نویسنده برای کتابخونهی HTTP کلاینت خودش به اسم ffetch (که قابلیتهایی مثل retry، timeout، hedging، کنسلکردن درخواست و circuit breaking داره) ۱۹ فایل و ۲۰۶ تا تست مثالمحور نوشته بود، همشونم پاس میشدن. ولی به یه سناریو تو پلاگین hedge شک داشت: این پلاگین یه کپی دوم از درخواست رو با تأخیر میفرسته و هر کدوم زودتر جواب مفید بده همونو برمیگردونه؛ نگرانی این بود که وقتی جواب دومی زودتر با خطای قابلتلاشمجدد (مثل ۴۲۹) برسه، جواب موفق اولی که هنوز تو راهه نادیده گرفته بشه.
بهجای نوشتن یه تست دقیق برای همین سناریو، نویسنده به سراغ property-based testing (که بهش fuzzing هم میگن) رفت: یعنی بهجای اینکه یه ورودی و جواب دقیقشو بنویسی، یه قانون کلی رو توصیف میکنی («هر وقت یکی از تلاشها بتونه جواب قابلقبول بده، پلاگین باید همونو برگردونه») و میذاری ابزار (اینجا fast-check) صدها یا هزاران ورودی تصادفی بسازه و قانونو روشون تست کنه. وقتی یه مورد رد بشه، ابزار با تکنیک shrinking کوچیکترینِ حالت خرابکننده رو پیدا میکنه تا فهمیدنش راحت باشه.
همین روش تو چهارمین ورودی تصادفی یه نقض پیدا کرد: یه تایملاین سهمیلیثانیهای که توش hedge با کد ۴۲۹ زودتر جواب میداد و درخواست اصلی که قرار بود دو میلیثانیه بعد با کد ۲۰۰ برگرده، همون لحظه لغو میشد. علتش این بود که کد فقط بر اساس اینکه کدوم درخواست آخر شروع شده تصمیم میگرفت، نه اینکه درخواست دیگهای هنوز در حال اجراست یا نه. راهحل این بود که یه جواب قابلتلاشمجدد فقط بهعنوان جایگزین ذخیره بشه، نه برنده قطعی، مگه اینکه همهی تلاشها تموم شده باشن.
همین تکنیک رو یه هفته روی کل کلاینت اجرا کرد و ده باگ دیگه هم پیدا شد. یکیش جالب بود: وقتی کاربر درخواست رو لغو میکرد یا تایماوت میشد، بهجای اینکه خطای Abort برگرده، جواب قدیمی ذخیرهشده (مثلاً یه ۵۰۳) برمیگشت و کاربر اصلاً نمیفهمید لغوش واقعاً اتفاق افتاده.
یه باگ دیگه هم زیر تستی به اسم «abortAll همهی درخواستها رو لغو میکنه» مخفی بود؛ اون تست پاس میشد ولی به دلیل اشتباه، نه به این خاطر که واقعاً کار میکرد. سیگنال لغو داخلی هیچوقت به هندلر واقعی وصل نشده بود و درخواست همچنان ادامه پیدا میکرد.
نکات کلیدی:
- ffetch یه کلاینت HTTP جاوااسکریپتی/تایپاسکریپتیه با قابلیتهای retry، hedging و circuit breaking
- ابزار استفادهشده fast-check بود، بهعنوان dev dependency داخل همون تستهای Vitest، بدون CI یا رانر جدا
- از یازده باگ پیداشده، هیچکدوم رو ۲۰۶ تست مثالمحور قبلی نگرفته بودن
- یکی از باگها همونجایی بود که یه تست به اسم دقیق همون رفتار وجود داشت ولی اشتباه پاس میشد




