Files
clinicpro/.claude/prompt/qa-full-system-audit.md
T

15 KiB
Raw Blame History

ممیزی کامل QA سیستم — بک‌اند Symfony API + پنل ادمین React

پروژه

clinicpro (Backend Symfony 7.4 + پنل ادمین React 19 داخل همان repo)

نقش اجراکننده

Senior QA Engineer. خروجی این پرامپت کد جدید محصول نیست؛ یک گزارش باگ/ممیزی واقعی و قابل‌بازتولید است، به‌علاوهٔ در صورت لزوم افزودن/اصلاح تست‌های خودکار (phpunit / vitest) برای اثبات باگ‌ها. حدس ممنوع — هر یافته باید با خروجی واقعی دستور/تست یا ارجاع file:line مستند شود.

زمینه

سیستم منبع واحد داده و auth برای سه کلاینت است (پنل ادمین خودش، nobat724_front، clinic-pro-tauri). ۲۷۷ روت api/v1، ۷ دامنه اصلی، احراز هویت JWT بدون‌حالت (stateless). زیرساخت تست موجود است و باید استفاده شود، نه بازنویسی:

  • Backend: PHPUnit — پایه tests/ApiTestCase.php، ۳۸ فایل تست در tests/<Domain>/. اجرا: ddev exec php bin/phpunit
  • Frontend: Vitest — ۹ فایل *.test.tsx/*.test.ts در assets/admin/. اجرا: ddev exec yarn test
  • Static analysis: ddev exec php vendor/bin/phpstan analyse (level 5)

هدف

اجرای واقعی تست روی کل سیستم و تولید گزارش ساختاریافته از باگ‌ها، مشکلات طراحی، ناسازگاری قرارداد API با کلاینت، و ضعف‌های امنیتی. تمرکز روی رفتار واقعی مشاهده‌شده، نه بازبینی نظری کد.


فایل‌های مرتبط (نقشهٔ سیستم برای تست)

ناحیه مسیر نقش
پایه پاسخ‌ها src/Shared/Controller/BaseController.php قرارداد success/paginated/error/validationError
کدهای خطا src/Shared/Constant/ErrorCodes.php همه error codeها + پیام فارسی
Exception handler src/**/ExceptionSubscriber* تبدیل AppException به پاسخ خطا
امنیت config/packages/security.yaml firewalls، public_endpoints، access_control
احراز هویت src/Auth/ PasswordAuthenticator، oauth/token، refresh، otp-login
کنترلرهای admin src/Admin/Controller/AdminApiController.php لیست/آمار admin (DQL array hydration)
دامنه‌ها src/{Appointment,Doctor,Clinic,Payment,Rating,Blog,Sms,Representation,Secretary,Settlement,Category,Billing,Subscription}/ business logic
تست‌های موجود tests/<Domain>/، tests/ApiTestCase.php نقطهٔ شروع افزودن تست
پنل ادمین assets/admin/pages/ (۴۹ صفحه)، assets/admin/App.tsx routing + صفحات
API client فرانت assets/admin/lib/api.ts fetch wrapper، خواندن JWT از localStorage['clinicpro-auth']
state assets/admin/stores/authStore.ts، uiStore.ts Zustand
مستندات قرارداد docs/api/*.md مرجع مقایسهٔ رفتار واقعی با مستند
کاربران تست TEST_USERS.md credential همهٔ نقش‌ها

پیش‌نیازها (قبل از شروع تست)

# سرویس‌ها بالا باشند
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
ddev exec php create_test_users.php          # بازساخت کاربران تست در صورت نیاز
ddev exec php bin/console messenger:consume async --limit=0 &   # برای تست SMS/queue

# کاربران تست (رمز همه: Test@1234)
#  ادمین:   09100000001  ROLE_ADMIN
#  کلینیک:  09100100000  ROLE_CLINIC   (uuid: 9ac73318-d313-4772-bf1e-418d47f8f4bc)
#  دکتر:    09100100001  ROLE_DOCTOR   (uuid: 2a3a7ab9-8d34-4118-862f-b458bcd6d77f)
#  منشی:    09100100002  ROLE_SECRETARY

دریافت توکن برای تست زندهٔ API — پارامترهای دقیق grant را از docs/api/auth.md بخوان (حدس نزن)، سپس:

# قالب — پارامترهای دقیق (grant_type/client_id/scope) را از docs/api/auth.md تأیید کن
curl -sk -X POST https://clinic-pro.ddev.site/oauth/token \
  -H 'Content-Type: application/json' \
  -d '{"grant_type":"password","username":"09100000001","password":"Test@1234","scope":"clinicpro"}'
# توکن خروجی را در $TOKEN بگذار و برای endpointهای محافظت‌شده:
#   -H "Authorization: Bearer $TOKEN"

وظایف

اجرا فاز‌به‌فاز؛ هر فاز یک آیتم todo. در هر فاز اول تست خودکار موجود را اجرا کن، سپس تست زندهٔ هدفمند. یافته‌ها را در گزارش (بخش «قالب گزارش») ثبت کن.

فاز ۰ — Baseline

ddev exec php bin/phpunit 2>&1 | tail -30      # وضعیت فعلی سبز/قرمز
ddev exec yarn test 2>&1 | tail -30
ddev exec php vendor/bin/phpstan analyse 2>&1 | tail -30

هر تست شکست‌خورده یا خطای phpstan = یافتهٔ فاز ۰ (قبل از هر تغییری وضعیت پایه ثبت شود).

فاز ۱ — قرارداد پاسخ و Envelope

از BaseController قرارداد را استخراج کن و روی نمونهٔ واقعی از هر شکل تأیید کن:

  • success{ success:true, data }
  • paginated{ success, data:[], meta:{ totalRecords, totalPages, currentPage } } (تخت، نه nested)
  • error{ success:false, errors:[{code,message}] }
  • validationError → HTTP 422 با errors:[{code,field,message}]

تست زنده روی حداقل یک endpoint از هر نوع:

curl -sk "https://clinic-pro.ddev.site/api/v1/admin/doctors?page=1&limit=5" -H "Authorization: Bearer $TOKEN" | head
curl -sk "https://clinic-pro.ddev.site/api/v1/doctor/INVALID-UUID" -H "Authorization: Bearer $TOKEN"

دام شناخته‌شده (double-nested): endpointهایی که $this->success(['data' => ...]) می‌زنند خروجی data.data.data می‌دهند. هر endpoint را که این الگو را دارد پیدا کن و بررسی کن کلاینت (assets/admin/ و docs/api/) درست استخراج می‌کند یا نه. ناسازگاری = یافته.

فاز ۲ — احراز هویت و مجوزها (Auth / RBAC)

نقش‌ها: ROLE_ADMIN, ROLE_CLINIC, ROLE_DOCTOR, ROLE_SECRETARY, ROLE_REPRESENTATION.

  • login flows: POST /oauth/token، /oauth/token/refresh، /api/v1/user/login، /api/v1/user/otp-login، /oauth/logout. تست: توکن معتبر، توکن منقضی، refresh با توکن باطل، رمز اشتباه (نباید ۵۰۰ بدهد، باید خطای ساخت‌یافته).
  • ماتریس دسترسی: برای نمونه‌ای از endpointهای هر دامنه، با توکنِ نقشِ نامجاز درخواست بزن و انتظار 403 (نه 200، نه 500). مثال بحرانی:
# دکتر نباید به endpointهای admin دسترسی داشته باشد
curl -sk -o /dev/null -w "%{http_code}\n" \
  "https://clinic-pro.ddev.site/api/v1/admin/users" -H "Authorization: Bearer $DOCTOR_TOKEN"   # انتظار 403
  • IDOR / افقی: با توکن دکتر A به منبع دکتر B دسترسی بگیر (/api/v1/appointment/{uuid}، /api/v1/patient/{uuid}، /api/v1/settlement/...). دسترسی موفق = یافتهٔ Critical.
  • public_endpoints: الگوی regex در security.yaml را با روت‌های واقعی تطبیق بده؛ endpointی که باید محافظت‌شده باشد ولی زیر public افتاده = یافتهٔ Critical. برعکس، endpoint عمومی که ۴۰۱ می‌دهد = یافتهٔ High.

فاز ۳ — اعتبارسنجی ورودی و مدیریت خطا

برای endpointهای نوشتنی (POST/PUT/PATCH/DELETE — ۳۹ در admin، بقیه در دامنه‌ها):

  • بدنهٔ خالی، فیلد اجباری غایب، نوع اشتباه (string به‌جای int)، مقدار خارج از بازه، UUID نامعتبر، تاریخ نامعتبر.
  • انتظار: 422 با validationError ساخت‌یافته، نه 500 و نه پذیرش خاموش.
  • timestamp: تاریخ‌ها باید Unix timestamp صحیح باشند نه DateTime؛ ورودی/خروجی تاریخ را روی یک نمونهٔ واقعی (مثلاً ساخت نوبت) بررسی کن.

فاز ۴ — امنیت API

  • SQL Injection: روی endpointهای دارای فیلتر/جستجو (/api/v1/patient/search-user, /api/v1/admin/*?search=, categorys/{bundle}) payload تزریق بفرست؛ چون لیست‌های admin از DQL array hydration استفاده می‌کنند انتظار امن‌بودن است — اما تأیید عملی کن.
  • XSS ذخیره‌شده: در فیلدهای متنی (نام دکتر، متن بلاگ، کامنت، متن پیامک post-visit) <script> ذخیره کن و بررسی کن خروجی API خام برمی‌گرداند (escape سمت React است — مستند کن مسئولیت کجاست).
  • Mass assignment: در endpointهای update، فیلدهای غیرمجاز (مثل role, id, balance) را در بدنه بفرست و ببین اعمال می‌شوند یا نه.
  • Rate limiting / brute-force: روی oauth/token و otp-login چند درخواست پیاپی با رمز غلط بزن؛ نبود محدودیت = یافتهٔ Medium/High.
  • payment callback: ^/api/v1/(payment|subscription-payment)/callback/ عمومی است — بررسی کن امضا/توکن تراکنش اعتبارسنجی می‌شود (جعل callback = Critical).

فاز ۵ — پنل ادمین React SPA

ddev exec yarn test 2>&1 | tail -30      # vitest موجود
ddev exec npx tsc --noEmit --project tsconfig.json    # خطای تایپ = یافته
ddev exec yarn dev 2>&1 | tail -15       # بیلد موفق

سپس بازبینی هدفمند (۴۹ صفحه در assets/admin/pages/):

  • Routing / RBAC UI: App.tsx و SelectContextPage.tsx — آیا مسیرهای هر نقش درست guard می‌شوند؟ آیا دکتر می‌تواند مسیر admin را مستقیم در URL باز کند؟
  • استخراج پاسخ: در هر صفحهٔ paginated باید data?.data + data?.meta?.totalRecords؛ single: data?.data؛ Category: data?.data?.data ?? []. هر صفحه که این را اشتباه استخراج می‌کند (لیست خالی/کرش) = یافته. با فاز ۱ متقاطع کن.
  • مدیریت خطا در UI: حالت API failure و offline — آیا loading/error state دارد یا کامپوننت کرش می‌کند؟ (lib/api.ts رفتار روی ۴۰۱/۵۰۰/timeout).
  • فرم‌ها: React Hook Form + Zod — آیا اعتبارسنجی سمت کلاینت با اعتبارسنجی backend هم‌خوان است؟ (مثلاً فیلدی که Zod اجباری نمی‌داند ولی backend می‌داند).
  • auth token: انقضای JWT حین کار با پنل — آیا refresh خودکار یا logout تمیز رخ می‌دهد یا کاربر در حلقهٔ خطا می‌افتد؟

فاز ۶ — یکپارچگی و سناریوهای واقعی کاربر (E2E)

با credential واقعی از TEST_USERS.md، این flowها را سرتاسر (API + مشاهدهٔ نتیجه در پنل) اجرا کن:

  1. ورود/خروج هر ۴ نقش.
  2. CRUD نوبت: کلینیک/دکتر یک نوبت می‌سازد → منشی ویرایش می‌کند → لغو → بررسی timestamp و وضعیت مالی.
  3. دعوت کلینیک: ارسال دعوت به دکتر (/api/v1/admin/clinic/{uuid}/invitations) → accept/reject با token (/api/v1/clinic-invitation/{token}/accept).
  4. جریان پرداخت: ساخت پرداخت → callback → تسویه (Settlement). صحت مبالغ و کمیسیون.
  5. همزمانی: دو کاربر یک اسلات نوبت را همزمان رزرو کنند — آیا double-booking رخ می‌دهد؟ (یافتهٔ Critical در صورت وقوع).
  6. بازیابی خطای شبکه: قطع API وسط یک عملیات نوشتنی — آیا داده نیمه‌کاره یا ناسازگار می‌ماند؟

فاز ۷ — تدوین گزارش نهایی

گزارش را در این مسیر بنویس: docs/qa/full-system-audit-report.md (اگر پوشه نیست بساز). ساختار طبق «قالب گزارش» پایین.


قالب گزارش (برای هر یافته)

### [شناسه] عنوان مشکل
- **شدت:** Critical | High | Medium | Low
- **ناحیه:** Backend API | Admin SPA | Integration | Security
- **مسیر/کد مرتبط:** `src/...:line` یا endpoint
- **مراحل بازتولید:**
  1. ...
  2. ... (شامل دستور curl / تست دقیق)
- **نتیجهٔ مورد انتظار:** ...
- **نتیجهٔ واقعی:** ... (خروجی خام واقعی، نه توصیف)
- **پیشنهاد رفع:** ... (اشاره به فایل و الگوی درست پروژه)

جدول خلاصه در ابتدای گزارش: شمارش یافته‌ها به تفکیک شدت و ناحیه.


نکات مهم

  • حدس ممنوع: یافته بدون خروجی واقعی یا file:line ثبت نکن. اگر چیزی قابل‌بازتولید نبود، در بخش «نیازمند بررسی بیشتر» با ذکر دلیل بگذار، نه به‌عنوان باگ قطعی.
  • قرارداد پروژه: پاسخ‌ها فقط از طریق BaseController سنجیده شوند؛ کدهای خطا در ErrorCodes.php؛ لیست‌های admin از .getArrayResult().
  • این تست است، نه رفکتور: کد محصول را تغییر نده مگر برای نوشتن تست خودکار جدید (tests/ یا *.test.tsx) که یک باگ را اثبات می‌کند؛ رفع باگ‌ها در پرامپت‌های جداگانهٔ بعدی انجام شود.
  • cross-repo: هر ناسازگاری قرارداد که کلاینت‌های دیگر (nobat724_front, clinic-pro-tauri) را هم می‌شکند، در گزارش پرچم‌گذاری کن (این کلاینت‌ها در build خطا نمی‌دهند).
  • داده‌های تست: بعد از سناریوهای مخرب (فاز ۶)، در صورت آلوده‌شدن DB، ddev exec php create_test_users.php و در صورت نیاز reset مجدد.
  • حجم: ۲۷۷ روت را کامل نمی‌توان دستی زد؛ نمونه‌گیری نمایندهٔ حداقل یک endpoint از هر method در هر دامنه الزامی است و در گزارش صراحتاً بنویس چه چیزی پوشش داده نشد (silent-cap ممنوع).
  • بعد از پایان: graphify update ..