feat: enhance QA driver with detailed persona definitions for improved testing accuracy

This commit is contained in:
hamed
2026-07-19 13:41:16 +03:30
parent 773f9d4d16
commit b10b0813f3
2 changed files with 238 additions and 14 deletions
+222 -11
View File
@@ -1,6 +1,6 @@
--- ---
name: qa-clinicpro name: qa-clinicpro
description: تست QA اپلیکیشن ClinicPro مثل یک کاربر واقعی — اجرای اپ، ورود با هر نقش، پیمایش صفحات پنل ادمین، اسکرین‌شات، کشف خطاهای کنسول و شبکه، تست UI/UX و RTL، تست دسترسی نقش‌ها (authz)، تست قرارداد API و اندازه‌گیری کارایی، و تولید Bug Report. Use when asked to QA, test, smoke-test, find bugs in, screenshot, or verify ClinicPro's admin panel or API — «تست کن»، «باگ پیدا کن»، «QA کن»، «این صفحه را بررسی کن». description: تست QA اپلیکیشن ClinicPro مثل یک کاربر واقعی — ابتدا ساخت همهٔ نقش‌ها و پروفایل‌های کامل (پزشک مستقل، پزشک عضو کلینیک، کلینیک، منشی، نماینده، بیمار، …) و تعیین ماتریس سطح دسترسی، سپس تست ماتریس دسترسی با تک‌تک آن‌ها. هر مانعی سر راه تست را مثل یک دولوپر ارشد Symfony/React خودش رفع می‌کند و تست را ادامه می‌دهد. اجرای اپ، ورود با هر نقش، پیمایش صفحات پنل ادمین، اسکرین‌شات، کشف خطاهای کنسول و شبکه، تست UI/UX و RTL، تست دسترسی نقش‌ها (authz)، تست قرارداد API و اندازه‌گیری کارایی، و تولید Bug Report. Use when asked to QA, test, smoke-test, find bugs in, screenshot, or verify ClinicPro's admin panel or API — «تست کن»، «باگ پیدا کن»، «QA کن»، «این صفحه را بررسی کن».
--- ---
# QA ClinicPro # QA ClinicPro
@@ -33,17 +33,29 @@ ls "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
در دیتابیس وجود ندارند و همه `ERR_AUTH_005` می‌گیرند. اسکریپت‌های `create_test_users.php` در دیتابیس وجود ندارند و همه `ERR_AUTH_005` می‌گیرند. اسکریپت‌های `create_test_users.php`
و `seed_realistic_data.php` هم که آن فایل ارجاع می‌دهد در ریپو نیستند. و `seed_realistic_data.php` هم که آن فایل ارجاع می‌دهد در ریپو نیستند.
نقش‌های واقعیِ کارکننده در `ROLES` داخل درایور هاردکد شده‌اند: پرسوناهای QA در `ROLES` داخل درایور تعریف شده‌اند. **واحد کار «پرسونا» است، نه
`ROLE_*`** — پزشک مستقل و پزشک عضو کلینیک هر دو `ROLE_DOCTOR` دارند ولی دادهٔ متفاوتی
می‌بینند، پس هرکدام یک ردیف جداگانه‌اند.
| نقش | موبایل | پسورد | | پرسونا | موبایل | پسورد | نقش‌ها | تمایز |
|---|---|---| |---|---|---|---|---|
| `admin` | `09120671756` | `QaTest@1234` | | `admin` | `09120671756` | `QaTest@1234` | `ROLE_ADMIN` | — |
| `clinic` | `09127000000` | `QaTest@1234` | | `clinic` | `09127000000` | `QaTest@1234` | `ROLE_CLINIC` | مالک کلینیک |
| `secretary` | `09123456778` | `QaTest@1234` | | `secretary` | `09123456778` | `QaTest@1234` | `ROLE_SECRETARY` | منشیِ یک پزشک |
| `doctor` | `09390039833` | `09390039833` | | `doctor` | `09390039833` | `09390039833` | `ROLE_DOCTOR` | حساب قدیمی، وضعیت عضویتش نامعلوم |
| `representation` | `09124000001` | `09124000001` | | `representation` | `09124000001` | `09124000001` | `ROLE_REPRESENTATION` | نماینده شهر |
| `doctor_solo` | `09129000001` | `QaTest@1234` | `ROLE_DOCTOR` | **پزشک مستقل** — مطب شخصی، بدون کلینیک |
| `doctor_member` | `09129000002` | `QaTest@1234` | `ROLE_DOCTOR` | پزشک **عضو کلینیک** |
| `clinic_doctor` | `09129000003` | `QaTest@1234` | `ROLE_CLINIC`+`ROLE_DOCTOR` | چندنقشی |
| `secretary_clinic` | `09129000004` | `QaTest@1234` | `ROLE_SECRETARY` | منشیِ کلینیک (نه پزشک) |
| `unclaimed_doctor` | `09129000005` | `QaTest@1234` | `ROLE_UNCLAIMED_DOCTOR` | پروفایل ایمپورت‌شدهٔ تصاحب‌نشده |
| `patient` | `09129000006` | `QaTest@1234` | `ROLE_USER` | کاربر عادی سایت |
| `importer` | `09129000007` | `QaTest@1234` | `ROLE_IMPORTER` | — |
سه تای اول پسوردشان عمداً برای QA ست شده. اگر DB ریست شد، دوباره ست کن: پنج ردیف اول موجودند. **هفت ردیف آخر تا وقتی Phase 0 اجرا نشده وجود ندارند** و
`driver.mjs roles` برایشان `✗` می‌دهد — این دقیقاً چک آمادگی است.
اگر DB ریست شد، پسورد پنج‌تای اول را دوباره ست کن:
```bash ```bash
ddev exec php bin/console security:hash-password 'QaTest@1234' ddev exec php bin/console security:hash-password 'QaTest@1234'
@@ -74,6 +86,82 @@ representation 09124000001 ROLE_USER,ROLE_REPRESENTATION token 15min
## مسیر اجرا (agent path) ## مسیر اجرا (agent path)
### ۰. Phase 0 — ساخت نقش‌ها، پروفایل‌ها و ماتریس دسترسی (اجباری، قبل از هر تست)
هیچ تستی را قبل از تمام‌شدن این فاز شروع نکن. خروجی این فاز سه چیز است:
**همهٔ پرسوناها موجود** · **پروفایل هرکدام کامل** · **ماتریس دسترسی مکتوب**.
**۰.۱ — کشف نقش‌ها.** لیست بالا را دوباره از روی کد بساز، به آن استناد نکن؛ ممکن است
نقشی اضافه شده باشد:
```bash
grep -rhoE "ROLE_[A-Z_]+" src/ assets/admin/ config/ | sort -u
ddev mysql -e "SELECT roles, COUNT(*) c FROM users GROUP BY roles ORDER BY c DESC;"
grep -n "role_hierarchy" -A 10 config/packages/security.yaml
```
هر نقشی که در کد هست و در جدول پرسوناها نیست را به `ROLES` در `driver.mjs` اضافه کن.
**۰.۲ — چک آمادگی.** ببین کدام پرسونا هنوز نیست:
```bash
node .claude/skills/qa-clinicpro/driver.mjs roles
```
**۰.۳ — ساخت پرسوناهای ناموجود.** برای هرکدام، **اول مسیر واقعی ساخت را در خود اپ پیدا
کن** و از همان استفاده کن — دست‌کاری مستقیم SQL پروفایل ناقص می‌سازد و تست را دروغین
می‌کند. به این ترتیب بگرد:
```bash
ls src/*/Command/ # آیا کامند کنسولی برای ساخت کاربر هست؟
grep -rn "IsGranted" src/Admin/Controller/ # اندپوینت‌های ادمینِ ساخت کاربر
sed -n '1,80p' docs/api/admin.md
```
فقط برای چیزی که هیچ مسیر اپلیکیشنی ندارد (مثلاً ست‌کردن `ROLE_IMPORTER` یا ساختن
`ROLE_UNCLAIMED_DOCTOR`) به `ddev mysql` برگرد، و در گزارش بنویس که کدام پرسونا
دستی ساخته شد.
**ترتیب ساخت مهم است** — وابستگی دارند:
```
کلینیک → doctor_member (عضو همان کلینیک) → secretary_clinic (منشیِ همان کلینیک)
پزشک → secretary (منشیِ همان پزشک)
```
**۰.۴ — کامل‌کردن پروفایل.** یک حسابِ بدون پروفایل، صفحات را خالی نشان می‌دهد و
باگ‌های واقعی را پنهان می‌کند. برای هر پرسونا این‌ها باید پر باشند:
| پرسونا | حداقل پروفایل لازم |
|---|---|
| `doctor_solo` / `doctor_member` / `clinic_doctor` | نام، تخصص، آدرس مطب، برنامهٔ کاری هفتگی، حداقل یک خدمت با تعرفه، حداقل یک بیمه |
| `clinic` | نام کلینیک، شهر، آدرس، حداقل یک پزشک عضو، حداقل یک خدمت |
| `secretary` / `secretary_clinic` | اتصال به پزشک/کلینیک + سطح دسترسی‌اش |
| `representation` | شهر تخصیص‌یافته |
| `patient` | نام، و حداقل یک نوبت رزروشده (برای اینکه صفحات خالی نباشند) |
| `unclaimed_doctor` | پروفایل پزشک بدون کاربرِ تصاحب‌کننده |
بعد از ساخت، پرشدن را تأیید کن — نه با حدس، با درخواست:
```bash
node .claude/skills/qa-clinicpro/driver.mjs api GET /api/v1/doctor/profile --as doctor_solo
```
**۰.۵ — تعیین سطح دسترسی.** ماتریس را از کد دربیاور، نه از ذهنت:
```bash
grep -n "RoleRoute\|allowedRoles\|element=" assets/admin/App.tsx # مسیرهای فرانت
grep -rn "IsGranted" src/*/Controller/ | sed 's/.*IsGranted(//' # گاردهای بک‌اند
```
از این دو، جدول `مسیر → نقش‌های مجاز` را بساز و در گزارش بیاور. بعد برای هر اندپوینت
حساس با `authz` (بخش ۳) تأییدش کن. **اختلاف بین ماتریسِ کد و خروجی `authz` = باگ**،
حتی اگر خروجی `authz` سخت‌گیرانه‌تر باشد.
**۰.۶ — دروازهٔ خروج.** تا وقتی `roles` برای همهٔ پرسوناها توکن برمی‌گرداند و ماتریس
نوشته شده، به فاز بعد نرو. اگر پرسونایی ساخته نشد، طبق بخش «وقتی به مانع خوردی»
خودت رفعش کن؛ رها کردنش یعنی آن نقش اصلاً تست نشده.
### ۱. بازدید از صفحه — اسکرین‌شات + خطاها ### ۱. بازدید از صفحه — اسکرین‌شات + خطاها
```bash ```bash
@@ -152,6 +240,52 @@ AUTHZ GET /api/v1/admin/users
هر ۲۰۰ غیرمنتظره در این جدول = یک باگ Critical. اگر `anonymous` هم ۲۰۰ گرفت، درایور هر ۲۰۰ غیرمنتظره در این جدول = یک باگ Critical. اگر `anonymous` هم ۲۰۰ گرفت، درایور
هشدار می‌دهد. هشدار می‌دهد.
#### ۳.۱ جاروی کامل ماتریس — اجباری، نه نمونه‌ای
`authz` خودش همهٔ پرسوناها را می‌زند، پس **تست دسترسی نباید روی چند اندپوینت منتخب
بماند**. لیست اندپوینت‌ها را از روتر بگیر و همه را جارو کن:
```bash
ddev exec php bin/console debug:router --format=json \
| node -e 'const r=JSON.parse(require("fs").readFileSync(0));
for (const [n,v] of Object.entries(r))
if (v.path.startsWith("/api/v1") && !v.path.includes("{"))
console.log(v.method.split("|")[0].replace("ANY","GET"), v.path);' \
| while read m p; do
node .claude/skills/qa-clinicpro/driver.mjs authz "$m" "$p"
done | tee /tmp/qa-authz-matrix.txt
```
روی این DB حدود **۱۶۸ مسیر بدون پارامتر** برمی‌گردد و `authz` برای هر مسیر به‌ازای هر
پرسونا دوباره لاگین می‌کند (≈۲۲۰۰ درخواست) — چند دقیقه طول می‌کشد، پس در پس‌زمینه
اجرایش کن و بعد فایل را بخوان. دو تله در خواندن خروجی:
- **۴۲۲ روی مسیرهای POST طبیعی است** (بدنه خالی فرستاده شده) و باگ نیست؛ چیزی که مهم
است تمایز ۴۰۱/۴۰۳ از بقیه است. اگر نقشی به‌جای ۴۰۳ یک ۴۲۲ گرفت، یعنی **گارد بعد از
اعتبارسنجی اجرا شده** — همان هم یافته است.
- **مسیرهای عمومی** (لاگین، ثبت‌نام، لیست شهرها) قاعدتاً برای `anonymous` هم ۲۰۰‌اند؛
اول با `config/packages/security.yaml` تطبیق بده، بعد ادعای نشت کن.
اندپوینت‌های پارامتردار (`{uuid}`) از این حلقه می‌افتند — آن‌ها را دستی و با
**شناسهٔ متعلق به پرسونای دیگر** بزن، چون همان‌جاست که IDOR پیدا می‌شود:
```bash
# uuid پزشکِ دیگری را به پرسونای doctor_solo بده — باید ۴۰۳/۴۰۴ بگیرد، نه ۲۰۰
node .claude/skills/qa-clinicpro/driver.mjs authz GET /api/v1/doctor/<uuid-of-another-doctor>
```
سه الگویی که باید در `/tmp/qa-authz-matrix.txt` دنبالشان بگردی:
| یافته | معنی |
|---|---|
| `anonymous` = ۲۰۰ روی مسیر غیرعمومی | نشت داده — Critical |
| نقشی ۲۰۰ می‌گیرد که در ماتریس ۰.۵ نبود | گارد جا افتاده — Critical |
| ۲۰۰ روی uuidِ مستأجر دیگر | IDOR — Critical |
| نقشی ۴۰۳ می‌گیرد که طبق ماتریس باید ۲۰۰ بگیرد | یا گارد سخت‌گیر است یا ماتریس غلط — بررسی کن |
| ۵۰۰ به‌جای ۴۰۳ | گارد کار می‌کند ولی خطا مدیریت نشده — High |
**بدون این جدولِ کامل، فاز دسترسی تمام‌شده نیست.** خروجی‌اش را در گزارش نهایی بیاور.
### ۴. تست قرارداد API ### ۴. تست قرارداد API
```bash ```bash
@@ -207,16 +341,80 @@ SLOWEST API CALLS
وقتی این skill فعال شد، مثل یک **مهندس ارشد تست** رفتار کن، نه فقط اجراکننده دستور: وقتی این skill فعال شد، مثل یک **مهندس ارشد تست** رفتار کن، نه فقط اجراکننده دستور:
0. **Phase 0 را تمام کن** (بالا). بدون پرسوناهای کامل، هر تستی نتیجهٔ بی‌معنی می‌دهد.
1. **اول سناریوی واقعی کاربر را بنویس**، بعد اجرا کن. مثال: ورود منشی → لیست نوبت‌ها → 1. **اول سناریوی واقعی کاربر را بنویس**، بعد اجرا کن. مثال: ورود منشی → لیست نوبت‌ها →
تغییر وضعیت یک نوبت → خروج → ورود مجدد → آیا تغییر ماند؟ تغییر وضعیت یک نوبت → خروج → ورود مجدد → آیا تغییر ماند؟
**پیمایش با هر پرسونا اجباری است.** بعد از Phase 0، برای *هر* پرسونا در جدول، وارد شو و
مسیرهای مجازش را طبق ماتریس ۰.۵ بگرد — نه فقط با `admin`. برای هر پرسونا حداقل:
```bash
for p in admin clinic doctor_solo doctor_member clinic_doctor \
secretary secretary_clinic representation patient; do
node .claude/skills/qa-clinicpro/driver.mjs visit \
"https://clinic-pro.ddev.site/admin/dashboard" --as "$p" --out "/tmp/qa-$p.png"
done
```
بعد **هر اسکرین‌شات را با Read باز کن و ببین** — و بخش `LANDING` را بخوان تا ریدایرکت
بی‌صدای نقش را نگیری. سه چیزی که فقط با مقایسهٔ بین پرسوناها پیدا می‌شوند:
- **نشت داده بین مستأجرها:** آیا `doctor_solo` دادهٔ بیمار پزشک دیگری را می‌بیند؟ آیا
`clinic` نوبت‌های پزشک غیرعضو را می‌بیند؟ این‌ها همیشه Critical‌اند.
- **صفحهٔ سفید به‌جای «دسترسی ندارید»:** نقشی که نباید ببیند، باید پیام روشن بگیرد.
- **منوی سایدبار در برابر دسترسی واقعی:** آیتمی که نمایش داده می‌شود ولی به ۴۰۳
می‌خورد (یا برعکس: مسیر باز است ولی در منو نیست) باگ است.
2. برای هر بخش این حالت‌ها را پوشش بده: 2. برای هر بخش این حالت‌ها را پوشش بده:
Happy Path · ورودی نامعتبر · داده خالی · داده خیلی زیاد (لیست ۱۰٬۹۳۲ کاربری) · Happy Path · ورودی نامعتبر · داده خالی · داده خیلی زیاد (لیست ۱۰٬۹۳۲ کاربری) ·
شرایط مرزی · خطای شبکه · هر پنج نقش · دسکتاپ ۱۴۴۰ و موبایل ۳۹۰. شرایط مرزی · خطای شبکه · **همهٔ پرسوناها** · دسکتاپ ۱۴۴۰ و موبایل ۳۹۰.
3. **هیچ چیز را حدس نزن.** ادعای بدون خروجی دستور، ادعا نیست. 3. **هیچ چیز را حدس نزن.** ادعای بدون خروجی دستور، ادعا نیست.
4. **قبل از گزارش، باگ را دوباره تکرار کن.** همان دستور را دوباره بزن؛ اگر تکرار نشد، 4. **قبل از گزارش، باگ را دوباره تکرار کن.** همان دستور را دوباره بزن؛ اگر تکرار نشد،
flaky بودنش را بنویس نه خودِ باگ را. flaky بودنش را بنویس نه خودِ باگ را.
5. باگ‌های کوچک UI را هم گزارش کن، ولی باگ‌های Business Logic اولویت بالاترند. 5. باگ‌های کوچک UI را هم گزارش کن، ولی باگ‌های Business Logic اولویت بالاترند.
### وقتی به مانع خوردی — رفعش کن، بعد برو تست بعدی
QA اینجا فقط گزارش‌نویس نیست. هر جا اجرای تست گیر کرد، **مثل یک دولوپر ارشد
Symfony/React خودت مشکل را حل کن**، تأیید کن که حل شده، و تست را از همان‌جا ادامه بده.
توقف روی اولین مانع یعنی بقیهٔ نقش‌ها هیچ‌وقت تست نمی‌شوند.
روال ثابت هر مانع:
```
بازتولید → ریشه‌یابی (نه علامت) → اصلاح → اثبات اصلاح → ثبت → ادامهٔ همان تست
```
1. **ریشه را پیدا کن، نه علامت را.** `visit` صفحهٔ سفید داد؟ اول `CONSOLE ERRORS` و
`NETWORK FAILURES`، بعد فایل سورس صفحه (`redesign-page/driver.mjs inspect`)، بعد
کنترلر مربوطه. اصلاح باید در همان لایه‌ای باشد که علت آنجاست.
2. **طبق قواعد پروژه اصلاح کن**، نه با وصلهٔ سریع:
- بک‌اند: `extends BaseController`، خطا با `AppException(ErrorCodes::…)`، کد SOLID،
تغییر entity ⟵ `doctrine:migrations:diff` + `migrate`.
- فرانت: TanStack Query برای دادهٔ سرور، کامپوننت‌های `components/ui/`، توکن‌های
`styles.css` (هیچ hex هاردکد)، رشته‌های فارسی.
- اندپوینت عوض شد ⟵ همان جلسه `docs/api/<domain>.md` را به‌روز کن (قاعدهٔ ثابت پروژه).
3. **اثبات کن.** همان دستوری که شکست خورده بود را دوباره بزن و خروجی سالمش را نشان بده.
بعد `ddev exec php bin/phpunit` و در صورت تغییر فرانت `npx tsc --noEmit` را اجرا کن
تا مطمئن شوی چیزی نشکسته‌ای.
4. **ثبت کن.** هر اصلاح یک ورودی در بخش «Fixes Applied» گزارش نهایی می‌گیرد:
مانع · ریشه · فایل‌های تغییریافته · دستور اثبات.
**مرزهایی که رد نمی‌کنی:**
- **باگ محصول را بی‌صدا رفع نکن.** اگر مانع خودش یک باگ واقعی محصول است، هم Bug Report
را بنویس هم اصلاح را — نه فقط اصلاح. گزارش، خروجی کار است.
- **هرگز برای سبزشدن تست، دسترسی را باز نکن.** اگر نقشی ۴۰۳ می‌گیرد و تو انتظار ۲۰۰
داری، پیش‌فرض این است که **انتظارت غلط است**. `IsGranted` یا `RoleRoute` را فقط وقتی
عوض کن که از روی کد ثابت کرده باشی آن نقش باید دسترسی داشته باشد، و دلیلش را بنویس.
همین قاعده برای حذف اعتبارسنجی ورودی هم هست.
- **دادهٔ تست را با تغییر محصول نساز.** کمبود دادهٔ پرسونا را با seed درست کن، نه با
نرم‌کردن یک قاعدهٔ کسب‌وکار.
- **مهاجرت مخرب نزن.** روی DB لوکالِ پر (۱۰٬۹۳۲ کاربر) `doctrine:schema:drop` یا
مهاجرتی که ستون پرداده را می‌اندازد، ممنوع.
- **اگر اصلاح از تست بزرگ‌تر شد** (بازطراحی معماری، تغییر شکست‌دهندهٔ قرارداد API که
`nobat724_front` و `clinic-pro-tauri` هم مصرفش می‌کنند)، دست نگه دار: باگ را با
اصلاح پیشنهادی گزارش کن، آن یک تست را `SKIPPED` علامت بزن، و **برو تست بعدی**.
### فرمت Bug Report ### فرمت Bug Report
هر یافته را با این قالب بنویس (فارسی): هر یافته را با این قالب بنویس (فارسی):
@@ -253,6 +451,19 @@ node .claude/skills/redesign-page/driver.mjs inspect "https://clinic-pro.ddev.si
۱. خلاصه وضعیت کلی · ۲. تعداد باگ‌ها · ۳. لیست بر اساس Severity · ۱. خلاصه وضعیت کلی · ۲. تعداد باگ‌ها · ۳. لیست بر اساس Severity ·
۴. باگ‌هایی که باید فوری رفع شوند · ۵. پیشنهاد بهبود کیفیت. ۴. باگ‌هایی که باید فوری رفع شوند · ۵. پیشنهاد بهبود کیفیت.
به‌علاوه این سه بخش که از قواعد بالا می‌آیند:
**۶. Fixes Applied** — هر مانعی که خودت رفع کردی:
| مانع | ریشه | فایل‌های تغییریافته | دستور اثبات |
|---|---|---|---|
**۷. ماتریس دسترسی** — جدول کامل `مسیر × پرسونا` از بخش ۳.۱، با اختلاف‌های
ماتریسِ کد و رفتار واقعی مشخص‌شده.
**۸. پوشش** — کدام پرسونا چه چیزی تست شد، و هر `SKIPPED` با دلیلش. اگر نقشی تست نشد
باید اینجا صریح بیاید؛ گزارشِ ساکت بدتر از گزارش ناقص است.
--- ---
## Gotchas ## Gotchas
+16 -3
View File
@@ -25,9 +25,13 @@ const CHROME = process.env.CHROME_BIN
const PORT = Number(process.env.CDP_PORT ?? 9444); const PORT = Number(process.env.CDP_PORT ?? 9444);
/** /**
* Local QA accounts, one per role. Passwords were set deliberately for testing * Local QA personas — one per distinct authorization identity in the product,
* (see SKILL.md § Test accounts); the doctor account predates that and still * not merely one per ROLE_* constant: an independent doctor and a clinic-member
* uses mobile-as-password. TEST_USERS.md is stale — its accounts do not exist. * doctor carry the same role but see different data, so each gets its own row.
*
* The first five predate this list and are known to exist; the rest are
* provisioned by SKILL.md § Phase 0 and report `✗` from `driver.mjs roles`
* until they are. TEST_USERS.md is stale — its accounts do not exist.
*/ */
const ROLES = { const ROLES = {
admin: ['09120671756', 'QaTest@1234'], admin: ['09120671756', 'QaTest@1234'],
@@ -35,6 +39,15 @@ const ROLES = {
secretary: ['09123456778', 'QaTest@1234'], secretary: ['09123456778', 'QaTest@1234'],
doctor: ['09390039833', '09390039833'], doctor: ['09390039833', '09390039833'],
representation: ['09124000001', '09124000001'], representation: ['09124000001', '09124000001'],
// Provisioned by Phase 0. Reserved QA range 0912900000x, password QaTest@1234.
doctor_solo: ['09129000001', 'QaTest@1234'], // own office, no clinic
doctor_member: ['09129000002', 'QaTest@1234'], // member of a clinic
clinic_doctor: ['09129000003', 'QaTest@1234'], // ROLE_CLINIC + ROLE_DOCTOR
secretary_clinic: ['09129000004', 'QaTest@1234'], // secretary of a clinic
unclaimed_doctor: ['09129000005', 'QaTest@1234'], // imported, unclaimed profile
patient: ['09129000006', 'QaTest@1234'], // ROLE_USER only
importer: ['09129000007', 'QaTest@1234'],
}; };
// ddev serves a locally-signed cert Node's fetch refuses. Relax TLS only for it. // ddev serves a locally-signed cert Node's fetch refuses. Relax TLS only for it.