198 lines
15 KiB
Markdown
198 lines
15 KiB
Markdown
# ممیزی کامل 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 همهٔ نقشها |
|
||
|
||
---
|
||
|
||
## پیشنیازها (قبل از شروع تست)
|
||
|
||
```bash
|
||
# سرویسها بالا باشند
|
||
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` بخوان (حدس نزن)، سپس:
|
||
|
||
```bash
|
||
# قالب — پارامترهای دقیق (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
|
||
|
||
```bash
|
||
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 از هر نوع:
|
||
|
||
```bash
|
||
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`). مثال بحرانی:
|
||
|
||
```bash
|
||
# دکتر نباید به 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
|
||
|
||
```bash
|
||
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` (اگر پوشه نیست بساز). ساختار طبق «قالب گزارش» پایین.
|
||
|
||
---
|
||
|
||
## قالب گزارش (برای هر یافته)
|
||
|
||
```markdown
|
||
### [شناسه] عنوان مشکل
|
||
- **شدت:** 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 .`.
|