# QA 11: API testing (2) — Test dizayn, negative testlar, JSON Schema, Auth, Mock va Contract testing

> **Holat:** 2026-yil oktabr. Bu darsda API'ni "chuqur" testlaymiz: javobni 7 qatlamda tekshirish, maydonlar bo'yicha negative test dizayn, JSON Schema, autentifikatsiya va avtorizatsiya matritsasi, mock va contract testing, GraphQL/WebSocket/gRPC xususiyatlari. Darsdagi barcha "real topilmalar" restful-booker, Toolshop API va ochiq GraphQL API'da `curl`/Newman 6.2.3/Ajv 8.20 bilan haqiqatan ishga tushirib olingan natijalar.

> **Bu darsdan keyin siz:** API uchun "faqat 200 qaytdimi?" darajasidan chiqib, ma'lumot buzilishi, xavfsizlik va shartnoma (contract) bug'larini topadigan test to'plamini loyihalay olasiz.

- javobni status'dan tortib yon ta'sirlargacha 7 qatlamda tekshirasiz;
- har bir maydon uchun negative test'larni tizimli chiqarasiz;
- JSON Schema yozasiz va uni Postman hamda Node.js'da ishlatasiz;
- `401`/`403`, token muddati, rollar va IDOR bo'yicha test matritsasi tuzasiz;
- mock, contract testing va GraphQL'ning "200 ichidagi xato"si kabi tuzoqlarni bilasiz.

## Mundarija

1. [API javobini 7 qatlamda tekshirish](#layers)
2. [API test dizayni: CRUD matritsasi](#crud-matrix)
3. [Maydonlar bo'yicha negative test dizayn](#field-design)
4. [Real topilmalar: restful-booker](#findings-rb)
5. [Real topilmalar: Toolshop API](#findings-toolshop)
6. [Xato javoblari formati va izchilligi](#error-format)
7. [Idempotentlik, parallel so'rovlar, sahifalash va rate limiting](#idempotency)
8. [JSON Schema bilan sxemani tekshirish](#json-schema)
9. [Autentifikatsiya va avtorizatsiya testlari](#auth-tests)
10. [Mock'lash: back-end tayyor bo'lmaganda](#mocking)
11. [Contract testing](#contract)
12. [GraphQL, WebSocket, SSE va gRPC — QA uchun farqlar](#other-apis)
13. [API test checklisti](#api-checklist)
14. [Amaliy mashqlar](#mashqlar)
15. [Xulosa](#xulosa)
16. [Manbalar](#manbalar)

---

## 1. API javobini 7 qatlamda tekshirish {#layers}

Junior tester ko'pincha bitta narsani tekshiradi: "status `200` mi?". Lekin `200` ichida noto'g'ri narx, `null` bo'lib qolgan maydon yoki boshqa foydalanuvchining ma'lumoti bo'lishi mumkin. API javobini qatlam-qatlam tekshiring:

![API javobini 7 qatlamda tekshirish va har qatlam uchun restful-booker'da topilgan real holatlar](/lessons/images/qa/qa_11_api_layers.svg)

| #   | Qatlam          | Savol                                                                          | Postman'da qanday                                  |
| --- | --------------- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
| 1   | Status kod      | Kod holatga mosmi (`201` yaratishda, `422` validatsiyada)?                     | `pm.response.to.have.status(201)`                  |
| 2   | Header'lar      | `Content-Type`, `Location`, `Allow`, kesh va xavfsizlik header'lari to'g'rimi? | `pm.response.headers.get(...)`                     |
| 3   | Sxema va tiplar | Majburiy maydonlar bormi, tiplar to'g'rimi, ortiqcha maydon yo'qmi?            | `pm.response.to.have.jsonSchema(schema)`           |
| 4   | Qiymatlar       | Yuborilgan qiymat aynan saqlandimi? Hisob-kitob to'g'rimi?                     | `pm.expect(body.total).to.eql(479000)`             |
| 5   | Biznes qoidalar | Domen qoidalari bajarildimi (bepul yetkazish ≥ 300 000 so'm)?                  | Qoida bo'yicha assertion                           |
| 6   | Yon ta'sirlar   | DB'ga yozildimi, ro'yxatda ko'rindimi, email/push ketdimi?                     | Qo'shimcha GET, SQL ([QA 12](/ios-lessons/qa-sql)) |
| 7   | Nofunksional    | Xatolar to'g'ri ushlanadimi, vaqt, xavfsizlik, limitlar                        | `responseTime`, negative va auth testlar           |

> **🧠 Esda tuting:** 1–2 qatlam — "server javob berdimi", 3–5 — "to'g'ri javob berdimi", 6–7 — "tizim to'g'ri ishladimi". Yaxshi QA uchala savolga ham javob qidiradi.

---

## 2. API test dizayni: CRUD matritsasi {#crud-matrix}

Har bir resurs uchun **CRUD matritsasi** tuzing: qatorlarda amallar, ustunlarda tekshiruv turlari. DemoBozor'ning xayoliy `cart/items` resursi uchun:

| Amal                      | Positive                       | Negative (ma'lumot)                        | Chegaraviy                 | Auth/rol                                    | Holat/yon ta'sir                                     |
| ------------------------- | ------------------------------ | ------------------------------------------ | -------------------------- | ------------------------------------------- | ---------------------------------------------------- |
| `POST /cart/items`        | Mavjud mahsulot, `quantity: 1` | `productId` yo'q, string, mavjud bo'lmagan | `quantity` 0, 1, 99, 100   | Token'siz → `401`                           | Savatcha summasi va yetkazish narxi qayta hisoblandi |
| `GET /cart`               | Bo'sh va to'la savatcha        | —                                          | 50 xil mahsulot (limit)    | Boshqa foydalanuvchi savatchasi ko'rinmaydi | Narxlar joriy (keshdan eski emas)                    |
| `PATCH /cart/items/{id}`  | `quantity: 3`                  | `quantity: "3"`, `null`, `-1`              | 1 → 99, 99 → 100           | Begona `id` → `403`/`404`                   | Summa yangilandi                                     |
| `DELETE /cart/items/{id}` | Mavjud element                 | Mavjud bo'lmagan `id`                      | Oxirgi elementni o'chirish | Begona `id`                                 | Ikkinchi DELETE nima qaytaradi (idempotentlik)       |

Bu jadval keyin test case'larga aylanadi ([QA 7](/ios-lessons/qa-test-documentation)) va avtomatlashtiriladi ([QA 20](/ios-lessons/qa-api-automation)).

---

## 3. Maydonlar bo'yicha negative test dizayn {#field-design}

Har bir maydonga [QA 5](/ios-lessons/qa-test-design-1) dagi EP/BVA texnikalarini qo'llang va quyidagi "universal" mutatsiyalarni sinab ko'ring:

| Kategoriya          | Mutatsiyalar                                                                                                                                                                       |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Mavjudlik           | Majburiy maydon yo'q; `null`; bo'sh qator `""`; faqat probellar `"   "`                                                                                                            |
| Tip                 | Son o'rniga string (`"450"`, `"abc"`), boolean o'rniga `"yes"`, obyekt o'rniga massiv                                                                                              |
| Raqamlar            | `0`, manfiy, kasr (`450.75`), juda katta (`9007199254740993`), ilmiy yozuv (`1e3`)                                                                                                 |
| Uzunlik             | min−1, min, max, max+1; 10 000+ belgi                                                                                                                                              |
| Matn tarkibi        | Unicode (`Gʻayrat`, `Ўрол`), emoji, RTL matn, boshida/oxirida probel, yangi qator `\n`                                                                                             |
| Xavfsizlik zondlari | `' OR '1'='1`, `<script>alert(1)</script>` — kutilgani: xavfsiz ishlov (rad etish yoki to'g'ri escape), hech qachon `500` yoki bajarilish emas ([QA 24](/ios-lessons/qa-security)) |
| Sana                | Noto'g'ri format, mavjud bo'lmagan sana (`2026-02-30`), kabisa kuni (`2028-02-29`), o'tmish/kelajak, teskari oraliq                                                                |
| Enum                | Ro'yxatdan tashqari qiymat, kichik harf (`paid` vs `PAID`)                                                                                                                         |
| Massiv              | Bo'sh `[]`, bitta element, limitdan ortiq, dublikatlar                                                                                                                             |
| Ortiqcha maydonlar  | Noma'lum maydon; `"role": "admin"`, `"isAdmin": true` kabi "mass assignment" urinishi                                                                                              |
| Format              | Buzilgan JSON; `Content-Type` yo'q yoki noto'g'ri; bo'sh body                                                                                                                      |

DemoBozor `POST /api/v1/cart/items` uchun namuna:

| #   | Body                                             | Kutilgan                                                                                |
| --- | ------------------------------------------------ | --------------------------------------------------------------------------------------- |
| 1   | `{"productId": 1042, "quantity": 1}`             | `201`                                                                                   |
| 2   | `{"productId": 1042, "quantity": 99}`            | `201` (yuqori chegara)                                                                  |
| 3   | `{"productId": 1042, "quantity": 100}`           | `422`, xato `quantity` maydoniga bog'langan                                             |
| 4   | `{"productId": 1042, "quantity": 0}`             | `422`                                                                                   |
| 5   | `{"productId": 1042, "quantity": "2"}`           | `422` (yoki hujjatda yozilgan bo'lsa — aniq konvertatsiya), lekin **jimgina** emas      |
| 6   | `{"productId": 1042, "quantity": 2.5}`           | `422` — kesib `2` qilish emas                                                           |
| 7   | `{"quantity": 1}`                                | `422`, "productId majburiy"                                                             |
| 8   | `{"productId": 999999999, "quantity": 1}`        | `404` yoki `422` (mahsulot yo'q)                                                        |
| 9   | `{"productId": 1042, "quantity": 1, "price": 1}` | `201`, lekin narx **serverdagi** narx bo'lishi shart (client narxi e'tiborga olinmaydi) |
| 10  | Buzilgan JSON                                    | `400`                                                                                   |
| 11  | To'g'ri JSON, `Content-Type: text/plain`         | `415`                                                                                   |
| 12  | Token'siz                                        | `401`                                                                                   |

> **💡 Maslahat:** 9-qator — eng qimmat bug'lardan biri. Agar server client yuborgan narxga ishonsa, foydalanuvchi choynakni 1 so'mga sotib oladi. Narx, chegirma, rol kabi "server hal qiladigan" qiymatlarni har doim so'rovga qo'shib ko'ring.

---

## 4. Real topilmalar: restful-booker {#findings-rb}

Endi nazariyani haqiqiy API'da sinaymiz. Quyidagi so'rovlarning hammasi `POST https://restful-booker.herokuapp.com/booking` ga `Content-Type: application/json` bilan yuborilgan (2026-yil 9-oktabr):

| #   | Yuborilgan                                         | Javob                       | Saqlangan qiymat                                 | Baho                                       |
| --- | -------------------------------------------------- | --------------------------- | ------------------------------------------------ | ------------------------------------------ |
| 1   | `firstname` yo'q                                   | `500 Internal Server Error` | —                                                | **Bug**: validatsiya o'rniga server xatosi |
| 2   | Bo'sh obyekt `{}`                                  | `500`                       | —                                                | **Bug**                                    |
| 3   | `"totalprice": "abc"`                              | `200`                       | `"totalprice": null`                             | **Bug**: jim ma'lumot yo'qotilishi         |
| 4   | `"totalprice": -100`                               | `200`                       | `-100`                                           | **Bug**: manfiy narx                       |
| 5   | `checkout` < `checkin`                             | `200`                       | teskari sanalar                                  | **Bug**: biznes qoida yo'q                 |
| 6   | `"checkin": "2026-13-45"`                          | `200`                       | `"0NaN-aN-aN"`                                   | **Bug**: yaroqsiz sana "axlat"ga aylandi   |
| 7   | `"firstname": ""`                                  | `200`                       | `""`                                             | **Bug**: bo'sh ism                         |
| 8   | Buzilgan JSON                                      | `400 Bad Request`           | —                                                | ✅ To'g'ri                                 |
| 9   | To'g'ri JSON, `Content-Type: text/plain`           | `500`                       | —                                                | **Bug**: `415` kutiladi                    |
| 10  | `"Gʻayrat 😀"`, `"Oʻrinboyev"` + `"isAdmin": true` | `200`                       | Unicode to'g'ri; `isAdmin` e'tiborsiz qoldirildi | ✅ To'g'ri                                 |
| 11  | `"depositpaid": "yes"`                             | `200`                       | `true`                                           | Jim konvertatsiya (hujjatlanmagan)         |
| 12  | `"totalprice": 450.75`                             | `200`                       | `450`                                            | **Bug**: summa jimgina kesildi             |
| 13  | `firstname` — 100 000 ta `A`                       | `200`                       | 100 000 belgi                                    | **Bug**: uzunlik cheklovi yo'q             |
| 14  | `GET /booking/abc`                                 | `404`                       | —                                                | ✅ Qabul qilinadi                          |
| 15  | `GET /booking?checkin=2026-13-99`                  | `500`                       | —                                                | **Bug**: filtr parametri validatsiyasiz    |

Qiziq tafsilot: `PUT` da `firstname` yo'q bo'lsa server `400` qaytaradi, `POST` da esa xuddi shu holat `500`. Ya'ni bir API ichida validatsiya **izchil emas** — bu ham alohida topilma.

Takrorlash uchun buyruqlar:

```bash
BASE=https://restful-booker.herokuapp.com

# 3-holat: narx string bo'lsa
curl -s -X POST $BASE/booking -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"firstname":"Aziz","lastname":"Karimov","totalprice":"abc","depositpaid":true,
       "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}'
# {"bookingid":529,"booking":{...,"totalprice":null,...}}

# 9-holat: noto'g'ri Content-Type
curl -s -o /dev/null -w "%{http_code}\n" -X POST $BASE/booking -H 'Content-Type: text/plain' \
  -d '{"firstname":"Aziz","lastname":"Karimov","totalprice":450,"depositpaid":true,
       "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}'
# 500
```

### Topilmani bug report'ga aylantirish

```text
Sarlavha: [API] POST /booking: totalprice'ga string yuborilsa 200 qaytadi va narx null bo'lib saqlanadi

Muhit: https://restful-booker.herokuapp.com (public), 2026-10-09
Qadamlar:
  1. POST /booking, Content-Type: application/json
  2. Body: {"firstname":"Aziz","lastname":"Karimov","totalprice":"abc","depositpaid":true,
           "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}
Kutilgan: 400/422, xato totalprice maydoniga bog'langan; booking yaratilmaydi
Haqiqiy:  200, bookingid qaytadi, saqlangan booking'da "totalprice": null
Jiddiylik: Major — ma'lumot jimgina buziladi, keyin hisob-kitoblar noto'g'ri bo'ladi
Qo'shimcha: o'xshash jim konvertatsiyalar — 450.75 → 450, "yes" → true (alohida ticket'lar)
Ilova: curl buyrug'i (yuqorida)
```

Bug report yozish san'ati batafsil: [QA 8](/ios-lessons/qa-bug-reports).

---

## 5. Real topilmalar: Toolshop API {#findings-toolshop}

**Toolshop** (`https://practicesoftwaretesting.com`) — testlashni mashq qilish uchun yaratilgan internet-do'kon; API'si `https://api.practicesoftwaretesting.com`. Demo xaridor: `customer@practicesoftwaretesting.com` / `welcome01`. 2026-yil 9-oktabrdagi kuzatuvlar:

| So'rov                                                     | Javob                                                                          | Baho                                                    |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------- |
| `GET /products?page=1`                                     | `200`, `{"current_page":1,"per_page":9,"last_page":6,"total":50,"data":[...]}` | ✅                                                      |
| `GET /products?page=999`                                   | `200`, `data: []`, `from: null`                                                | ✅ Mantiqiy                                             |
| `GET /products?page=-1` va `page=abc`                      | `200`, jimgina 1-sahifa                                                        | Munozarali: `400` yoki hujjatlangan xatti-harakat kerak |
| `GET /products?sort=price,asc`                             | Eng arzondan boshlab                                                           | ✅                                                      |
| `GET /products?between=price,1,10`                         | `total: 9`                                                                     | ✅                                                      |
| `GET /products?sort=hacker,up`                             | `500 {"message":"Something went wrong"}`                                       | **Bug**: noto'g'ri parametr serverni yiqitadi           |
| `GET /products/NOTEXIST`                                   | `404 {"message":"Requested item not found"}`                                   | ✅                                                      |
| `GET /products/search?q=<script>alert(1)</script>`         | `200`, bo'sh natija                                                            | ✅ Xavfsiz ishlov                                       |
| `POST /users/register` faqat `first_name` bilan            | `422` + maydonlar bo'yicha xatolar                                             | ✅ Namunali validatsiya                                 |
| `POST /users/login` noto'g'ri parol                        | `401 {"error":"Unauthorized"}`                                                 | ✅ kod, lekin pastga qarang                             |
| `GET /users/me` token'siz                                  | `401 {"message":"Unauthorized"}`                                               | ✅ kod                                                  |
| Xaridor token'i bilan `GET /users` (admin)                 | `403 {"message":"Forbidden"}`                                                  | ✅ To'g'ri avtorizatsiya                                |
| `GET /users/logout`, so'ng shu token bilan `GET /users/me` | `200`, keyin `401`                                                             | ✅ Logout token'ni bekor qiladi                         |

Namunali `422` javobi — maydonlar bo'yicha aniq xatolar:

```json
{
  "last_name": ["The last name field is required."],
  "email": ["The email field is required."],
  "password": ["The password field is required."]
}
```

Ikki nozik topilma:

1. **Xato formati izchil emas**: login xatosida kalit `error`, boshqa endpoint'larda `message`. Client dasturchisi har endpoint uchun alohida kod yozishga majbur.
2. `401` javoblarida `WWW-Authenticate` header'i yo'q — HTTP standarti (RFC 9110) `401` uchun bu header'ni talab qiladi. Kichik, lekin spec bo'yicha topilma.

---

## 6. Xato javoblari formati va izchilligi {#error-format}

Xato javobi foydalanuvchi va dasturchi uchun "yo'l ko'rsatkich". Yaxshi xato javobi:

- to'g'ri status kod;
- mashina o'qiy oladigan kod (`"code": "QUANTITY_OUT_OF_RANGE"`) va inson o'qiy oladigan xabar;
- `422` da qaysi maydon va nima xato;
- `X-Request-ID` kabi kuzatish identifikatori;
- **ichki tafsilotlar yo'q**: stack trace, SQL so'rov, fayl yo'llari, kutubxona versiyalari.

Standart format — **RFC 9457 "Problem Details"** (`Content-Type: application/problem+json`). DemoBozor uchun namuna (xayoliy):

```json
{
  "type": "https://api.demobozor.example/errors/quantity-out-of-range",
  "title": "Miqdor ruxsat etilgan oraliqda emas",
  "status": 422,
  "detail": "quantity 1 dan 99 gacha bo'lishi kerak, yuborilgan: 120",
  "instance": "/api/v1/cart/items",
  "errors": [{ "field": "quantity", "code": "QUANTITY_OUT_OF_RANGE" }]
}
```

QA tekshiruvlari:

- barcha endpoint'larda xato tuzilishi bir xilmi (Toolshop'dagi `error` vs `message` holati);
- `Accept-Language: uz` yuborilganda xabar o'zbekchami (agar talab bo'lsa);
- `500` javobida ichki ma'lumot sizib chiqmayaptimi ([QA 10](/ios-lessons/qa-api-postman) dagi Petstore `NumberFormatException` misoli);
- mobil ilova va web xato kodini to'g'ri tarjima qilib, foydalanuvchiga tushunarli xabar ko'rsatyaptimi.

---

## 7. Idempotentlik, parallel so'rovlar, sahifalash va rate limiting {#idempotency}

### Dublikat va idempotentlik

Bir xil `POST /booking` ni ketma-ket ikki marta yuborganimizda restful-booker ikkita booking yaratdi (ikki xil `bookingid`). DemoBozor'da bu "bitta tugma — ikkita buyurtma" degani. Testlar:

- "Buyurtma berish"ni ikki marta tez bosish (UI) va bir xil so'rovni ikki marta yuborish (API);
- tarmoq uzilib, ilova so'rovni qayta yuborganda (Proxyman Repeat bilan simulyatsiya — [QA 15](/ios-lessons/qa-proxyman));
- agar API `Idempotency-Key` kabi mexanizmni qo'llasa — bir xil kalit bilan ikkinchi so'rov yangi buyurtma yaratmasligi.

### Parallel so'rovlar (race condition)

Ikki so'rov bir vaqtda kelsa, natija tasodifiy bo'lib qolishi mumkin. Bash'da `&` va `wait` bilan oddiy tajriba:

```bash
#!/usr/bin/env bash
# Bir booking'ga ikki "foydalanuvchi" bir vaqtda PATCH yuboradi
BASE=https://restful-booker.herokuapp.com
AUTH='Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM='

ID=$(curl -s -X POST $BASE/booking -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"firstname":"Aziz","lastname":"Karimov","totalprice":450,"depositpaid":true,
       "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}' \
  | python3 -c 'import sys, json; print(json.load(sys.stdin)["bookingid"])')

for price in 500 700; do
  curl -s -o /dev/null -w "PATCH totalprice=$price → %{http_code}\n" -X PATCH $BASE/booking/$ID \
    -H 'Content-Type: application/json' -H 'Accept: application/json' -H "$AUTH" \
    -d "{\"totalprice\": $price}" &
done
wait

curl -s $BASE/booking/$ID -H 'Accept: application/json' \
  | python3 -c 'import sys, json; print("Yakuniy narx:", json.load(sys.stdin)["totalprice"])'
```

Uch marta ishga tushirganimizda yakuniy narx `700`, `700`, `500` chiqdi — "oxirgi yozgan yutadi" va kim oxirgi bo'lishi tasodifiy. DemoBozor'da xuddi shu turdagi muhim testlar: bitta martalik promo-kodni ikki parallel buyurtmada ishlatish, omborda 1 dona qolgan mahsulotni ikki kishi bir vaqtda sotib olishi, ikki tabdan bir vaqtda to'lov.

> **⚠️ Diqqat:** Parallel va ko'p so'rovli testlarni faqat **o'z test muhitingizda** yoki ruxsat berilgan amaliyot API'larida, kichik sonlar bilan qiling. Begona serverga yuzlab so'rov yuborish — hujum sifatida qabul qilinishi mumkin. Yuklama testlari: [QA 23](/ios-lessons/qa-performance-k6).

### Sahifalash, saralash va filtr

Toolshop'da `total: 50`, `per_page: 9` → `last_page: 6`. Kutilgan natijalar: 5-sahifa — 37–45, 6-sahifa — 46–50 (5 ta), 7-sahifa — bo'sh. Haqiqatan ham shunday chiqdi. Sahifalash uchun test g'oyalari:

- birinchi, oxirgi va oxirgidan keyingi sahifa;
- `total` va barcha sahifalardagi elementlar yig'indisi teng;
- sahifalar orasida **dublikat yoki tushib qolgan** element yo'q (ayniqsa saralash barqaror bo'lmasa yoki ro'yxat test paytida o'zgarsa);
- `page=0`, manfiy, harf; `per_page` limitidan katta qiymat;
- saralash yo'nalishlari va teng qiymatlar (bir xil narxli mahsulotlar tartibi);
- filtrlar kombinatsiyasi va natija bo'sh bo'lganda javob formati.

### Rate limiting

Server bir mijozdan keladigan so'rovlar sonini cheklashi mumkin: limitdan oshganda `429 Too Many Requests`, ko'pincha `Retry-After` header'i bilan. Ba'zi API'lar limitni header'larda ko'rsatadi — masalan, jsonplaceholder javobida `x-ratelimit-limit`, `x-ratelimit-remaining`, `x-ratelimit-reset` bor. DemoBozor qoidasi bo'yicha test: 3 marta noto'g'ri SMS kod → 4-urinish bloklanadi, blok 15 daqiqa davom etadi, blok tugagach yana ishlaydi, boshqa telefon raqamiga ta'sir qilmaydi.

---

## 8. JSON Schema bilan sxemani tekshirish {#json-schema}

**JSON Schema** — JSON ma'lumotining tuzilishini tasvirlaydigan standart (joriy versiya — draft 2020-12). U "qanday maydonlar bo'lishi, qanday tipda, qanday chegaralarda" degan qoidalarni bir joyda yozish imkonini beradi.

| Kalit so'z                               | Ma'nosi                  | Misol                                         |
| ---------------------------------------- | ------------------------ | --------------------------------------------- |
| `type`                                   | Tip                      | `"string"`, `"integer"`, `["string", "null"]` |
| `properties`                             | Obyekt maydonlari        | `{"firstname": {...}}`                        |
| `required`                               | Majburiy maydonlar       | `["firstname", "lastname"]`                   |
| `additionalProperties`                   | Ortiqcha maydonga ruxsat | `false` — kutilmagan maydon = xato            |
| `enum`                                   | Ruxsat etilgan qiymatlar | `["CREATED", "PAID"]`                         |
| `minimum`, `exclusiveMinimum`, `maximum` | Son chegaralari          | `{"minimum": 0}`                              |
| `minLength`, `maxLength`, `pattern`      | Matn cheklovlari         | `{"pattern": "^\\+998\\d{9}$"}`               |
| `format`                                 | Semantik format          | `"date"`, `"email"`, `"uuid"`                 |
| `items`, `minItems`                      | Massiv elementlari       | `{"items": {"$ref": "#/$defs/product"}}`      |

### Node.js'da Ajv bilan (draft 2020-12)

Ataylab "yomon" booking yaratamiz va uni qat'iy sxema bilan tekshiramiz:

```javascript
// validate-booking.mjs — restful-booker javobini JSON Schema (draft 2020-12) bilan tekshirish
// O'rnatish: npm i ajv ajv-formats
import Ajv2020 from "ajv/dist/2020.js";
import addFormats from "ajv-formats";

const bookingSchema = {
  $schema: "https://json-schema.org/draft/2020-12/schema",
  type: "object",
  required: [
    "firstname",
    "lastname",
    "totalprice",
    "depositpaid",
    "bookingdates",
  ],
  properties: {
    firstname: { type: "string", minLength: 1, maxLength: 50 },
    lastname: { type: "string", minLength: 1, maxLength: 50 },
    totalprice: { type: "integer", minimum: 0 },
    depositpaid: { type: "boolean" },
    bookingdates: {
      type: "object",
      required: ["checkin", "checkout"],
      properties: {
        checkin: { type: "string", format: "date" },
        checkout: { type: "string", format: "date" },
      },
    },
    additionalneeds: { type: "string" },
  },
  additionalProperties: false,
};

const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
const validate = ajv.compile(bookingSchema);

const BASE = "https://restful-booker.herokuapp.com";

// 1) Ataylab "yomon" booking yaratamiz: bo'sh ism, manfiy narx, noto'g'ri sana
const created = await fetch(`${BASE}/booking`, {
  method: "POST",
  headers: { "Content-Type": "application/json", Accept: "application/json" },
  body: JSON.stringify({
    firstname: "",
    lastname: "Karimov",
    totalprice: -100,
    depositpaid: true,
    bookingdates: { checkin: "2026-13-45", checkout: "abc" },
  }),
}).then((r) => r.json());

// 2) Serverda qanday saqlanganini o'qiymiz
const saved = await fetch(`${BASE}/booking/${created.bookingid}`, {
  headers: { Accept: "application/json" },
}).then((r) => r.json());

// 3) Sxema bilan tekshiramiz
if (validate(saved)) {
  console.log("✅ Javob sxemaga mos");
} else {
  console.log("❌ Sxema xatolari:");
  for (const err of validate.errors) {
    console.log(`  ${err.instancePath || "/"} ${err.message}`);
  }
}
```

Haqiqiy natija (`node validate-booking.mjs`):

```text
❌ Sxema xatolari:
  /firstname must NOT have fewer than 1 characters
  /totalprice must be >= 0
  /bookingdates/checkin must match format "date"
  /bookingdates/checkout must match format "date"
```

Bitta sxema to'rtta bug'ni birdaniga ushladi. Shuning uchun sxema tekshiruvi — API regression'ining arzon va kuchli qatlami.

### Postman'da

```javascript
const schema = {
  type: "object",
  required: ["bookingid", "booking"],
  properties: {
    bookingid: { type: "integer" },
    booking: {
      type: "object",
      required: ["firstname", "totalprice", "bookingdates"],
      properties: {
        firstname: { type: "string" },
        totalprice: { type: "integer" },
        bookingdates: {
          type: "object",
          properties: {
            checkin: { type: "string", format: "date" },
            checkout: { type: "string", format: "date" },
          },
        },
      },
    },
  },
};

pm.test("Javob JSON Schema'ga mos", () => {
  pm.response.to.have.jsonSchema(schema);
});
```

Yaroqsiz sana bilan booking yaratilganda Newman 6.2.3 aynan shunday xato beradi:

```text
AssertionError  Javob JSON Schema'ga mos
expected data to satisfy schema but found following errors:
data.booking.bookingdates.checkin should match format "date"
```

> **⚠️ Diqqat:** Newman 6.2.3 (Postman sandbox) bilan tekshirganimizda sxemaga `$schema: "https://json-schema.org/draft/2020-12/schema"` qo'shilsa, test `no schema with key or ref ...` xatosi bilan yiqildi; `$schema` umuman yozilmaganda yoki draft-07 ko'rsatilganda ishladi. Postman skriptlarida `$schema`ni yozmang — ishlatayotgan versiyangizda alohida sinab ko'ring.

**Sxemani qayerdan olamiz?** Agar API OpenAPI hujjatiga ega bo'lsa, sxemalar uning `components.schemas` bo'limida tayyor turadi — ularni qo'lda qayta yozmang, hujjatdan oling. Shunda test hujjat va kod mosligini ham tekshiradi ([11-bo'lim](#contract)).

---

## 9. Autentifikatsiya va avtorizatsiya testlari {#auth-tests}

### Token holatlari matritsasi

| Holat                                               | Kutilgan         | Toolshop'da tekshirildi             |
| --------------------------------------------------- | ---------------- | ----------------------------------- |
| Token yo'q                                          | `401`            | ✅ `401 {"message":"Unauthorized"}` |
| Buzilgan token (oxirgi belgi o'zgartirilgan)        | `401`            | ✅ `401`                            |
| Muddati o'tgan token (Toolshop'da 5 daqiqa)         | `401`            | Mashq sifatida o'zingiz tekshiring  |
| Logout'dan keyingi token                            | `401`            | ✅ `200` → logout → `401`           |
| To'g'ri token, ruxsat yo'q rol                      | `403`            | ✅ xaridor → `GET /users` → `403`   |
| To'g'ri token, o'z resursi                          | `200`            | ✅ `GET /users/me` → `200`          |
| To'g'ri token, **boshqa foydalanuvchining** resursi | `403` yoki `404` | IDOR — pastga qarang                |

### Rollar matritsasi (DemoBozor, xayoliy)

| Endpoint                                            | Mehmon | Xaridor A | Xaridor B   | Sotuvchi | Admin |
| --------------------------------------------------- | ------ | --------- | ----------- | -------- | ----- |
| `GET /products`                                     | ✅     | ✅        | ✅          | ✅       | ✅    |
| `GET /orders/{A ning buyurtmasi}`                   | `401`  | ✅        | `403`/`404` | `403`\*  | ✅    |
| `POST /orders/{id}/cancel` (A ning, `PAID`)         | `401`  | ✅        | `403`/`404` | `403`    | ✅    |
| `PATCH /products/{id}` (sotuvchining o'z mahsuloti) | `401`  | `403`     | `403`       | ✅       | ✅    |
| `GET /admin/users`                                  | `401`  | `403`     | `403`       | `403`    | ✅    |

\* Talabga bog'liq: sotuvchi o'z mahsuloti bor buyurtmani ko'rishi mumkin bo'lsa — faqat o'sha qismini.

### IDOR / BOLA — API'dagi eng xavfli bug turi

**IDOR** (Insecure Direct Object Reference), OWASP API Security Top 10 (2023) da **API1: Broken Object Level Authorization (BOLA)** — server "bu foydalanuvchi shu obyektga egami?" deb tekshirmaydi. Xaridor B o'z token'i bilan `GET /api/v1/orders/58231` (A ning buyurtmasi) so'rasa va javob `200` bo'lsa — begona manzil, telefon va xaridlar ochilib qoladi.

Test usuli oddiy: ikki test foydalanuvchi, A ning resurs ID'sini B ning token'i bilan so'rash — har bir endpoint uchun. Batafsil va boshqa xavfsizlik testlari: [QA 24](/ios-lessons/qa-security).

> **🎯 Intervyuda:** "API'da avtorizatsiyani qanday testlaysiz?" — Rollar × endpoint'lar matritsasi tuzaman; har bir rol uchun ruxsat etilgan va taqiqlangan amallarni tekshiraman; ikki foydalanuvchi bilan bir-birining resurslariga kirishga urinaman (IDOR/BOLA); token yo'q, buzilgan, muddati o'tgan va logout'dan keyingi holatlarni alohida tekshiraman.

---

## 10. Mock'lash: back-end tayyor bo'lmaganda {#mocking}

**Mock** — haqiqiy server o'rniga oldindan tayyorlangan javob qaytaradigan "soxta" server yoki qatlam. Qachon kerak:

- back-end hali tayyor emas, lekin front-end/mobil ilovani testlash kerak;
- qiyin takrorlanadigan holatlar: `500`, timeout, bo'sh ro'yxat, 99 ta element, `null` narx;
- uchinchi tomon xizmati (to'lov, SMS) test muhitida yo'q yoki pullik.

| Vosita                  | Qayerda ishlaydi        | Qachon qulay                                                                       |
| ----------------------- | ----------------------- | ---------------------------------------------------------------------------------- |
| Postman Mock Servers    | Postman bulutida        | Collection'dagi saqlangan misollar asosida tez mock                                |
| Mockoon                 | Desktop ilova + CLI     | Lokal, kod yozmasdan, jamoa bilan fayl orqali bo'lishish                           |
| WireMock                | Java/standalone/Docker  | Murakkab qoidalar, CI'da avtomatik testlar                                         |
| Chrome Local overrides  | Brauzer                 | Web'da bitta javobni tez almashtirish ([QA 9](/ios-lessons/qa-web-http-devtools))  |
| Proxyman Map Local      | Mac/iOS/Android trafigi | Mobil ilova javoblarini almashtirish ([QA 15](/ios-lessons/qa-proxyman))           |
| Playwright `page.route` | Avtomatik UI testlar    | Testning o'zida javobni mock qilish ([QA 19](/ios-lessons/qa-playwright-advanced)) |

> **⚠️ Diqqat:** Mock — haqiqatning nusxasi, haqiqatning o'zi emas. Back-end o'zgarib, mock eskicha qolsa, testlar yashil, production esa qizil bo'ladi. Buning davosi — contract testing.

---

## 11. Contract testing {#contract}

**Contract (shartnoma)** — client va server o'rtasida "qanday so'rovga qanday javob" kelishuvi. Contract testing shu kelishuv buzilmaganini avtomatik tekshiradi.

Ikki asosiy yondashuv:

1. **Consumer-driven contracts (Pact).** Client (consumer) testlari Pact yaratgan mock provider'ga so'rov yuboradi; natijada **pact fayli** — shartnoma yoziladi. Keyin provider (server) tomonida Pact shu so'rovlarni haqiqiy serverga qayta yuboradi va javob shartnomada kutilgan minimal ma'lumotni o'z ichiga olishini tekshiradi. Pact Broker shartnomalarni jamoalar va CI o'rtasida bo'lishish uchun ishlatiladi.
2. **Schema-based (OpenAPI).** Server javoblari OpenAPI hujjatidagi sxemaga mosligini tekshirish (yuqoridagi JSON Schema yondashuvining kengaytmasi).

Shartnoma buzilishiga real misollar (2026-yil oktabr):

- **Toolshop**: OpenAPI hujjatida `GET /users` uchun faqat `200`, `400`, `401` javoblari yozilgan, amalda esa xaridor token'i bilan `403` keladi. `POST /users/login` uchun faqat `200` hujjatlangan, noto'g'ri parolda esa `401` qaytadi. Hujjat to'liq emas.
- **Swagger Petstore**: hujjat noto'g'ri ID uchun `400` deydi, server `404` qaytaradi ([QA 10](/ios-lessons/qa-api-postman)).

Bunday "drift" mobil ilovalar uchun ayniqsa xavfli: eski versiyadagi ilovalar foydalanuvchilarda oylab ishlaydi va server javobi o'zgarsa, ular qulashi mumkin.

Test piramidasida contract testlar unit va integration testlar orasida turadi: tez, lekin ikki tomon o'rtasidagi kelishuvni tekshiradi. Ularni odatda dasturchilar yoki SDET'lar yozadi; QA'ning vazifasi — qaysi endpoint'lar va maydonlar "shartnoma"ga kirishi kerakligini aniqlashda qatnashish va drift'larni bug sifatida ko'tarish.

---

## 12. GraphQL, WebSocket, SSE va gRPC — QA uchun farqlar {#other-apis}

### GraphQL

GraphQL'da odatda **bitta endpoint** (`POST /graphql`) bo'ladi; client aynan qaysi maydonlar kerakligini so'rovda yozadi (`query` — o'qish, `mutation` — o'zgartirish).

QA uchun eng muhim tuzoq — **xato ko'pincha HTTP 200 bilan qaytadi**. Ochiq GraphQL API'da tekshirdik:

```bash
# To'g'ri so'rov
curl -s -X POST https://countries.trevorblades.com/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ country(code: \"UZ\") { name capital currency } }"}'
# 200 {"data":{"country":{"capital":"Tashkent","currency":"UZS","name":"Uzbekistan"}}}

# Mavjud bo'lmagan maydon
curl -s -w "\n%{http_code}\n" -X POST https://countries.trevorblades.com/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ country(code: \"UZ\") { name population } }"}'
# {"errors":[{"message":"Cannot query field \"population\" on type \"Country\".", ...}]}
# 200

# Mavjud bo'lmagan davlat kodi
curl -s -w "\n%{http_code}\n" -X POST https://countries.trevorblades.com/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ country(code: \"XX\") { name } }"}'
# {"data":{"country":null}}
# 200
```

GraphQL-over-HTTP spetsifikatsiyasining yangi qoralamasi validatsiya xatolari uchun `422` ni tavsiya qiladi, lekin amalda serverlar har xil ishlaydi. Xulosa: **GraphQL'da faqat statusga ishonmang** — har doim `errors` massivini tekshiring:

```javascript
const json = pm.response.json();

pm.test("HTTP 200", () => {
  pm.response.to.have.status(200);
});

pm.test("GraphQL xatosi yo'q", () => {
  pm.expect(json.errors, JSON.stringify(json.errors)).to.be.undefined;
});

pm.test("Davlat nomi to'g'ri", () => {
  pm.expect(json.data.country.name).to.eql("Uzbekistan");
});
```

Noto'g'ri maydonli so'rovda bu skriptning birinchi testi **o'tdi** (status 200), ikkinchisi esa xatoni ushladi — aynan shu sababli `errors` tekshiruvi shart. GraphQL uchun qo'shimcha test g'oyalari: juda chuqur ichma-ich so'rovlar (server cheklaydimi), katta `first: 100000` kabi limitlar, introspection'ning production'da yopiqligi, mutation'larda avtorizatsiya.

### WebSocket va SSE

- **WebSocket** — client va server o'rtasida doimiy, ikki tomonlama ulanish (chat, jonli buyurtma holati). Ulanish `101 Switching Protocols` bilan ochiladi. Test: ulanishda autentifikatsiya, xabarlar tartibi, ulanish uzilib qayta tiklanishi (reconnect), bir vaqtda ko'p xabar. Chrome DevTools → Network → `WS` filtri → **Messages** tab'i; Postman ham WebSocket so'rovlarini qo'llab-quvvatlaydi.
- **SSE (Server-Sent Events)** — faqat serverdan client'ga oqim (`Content-Type: text/event-stream`), masalan, "buyurtmangiz yo'lda" bildirishnomalari. Test: ulanish uzilganda qayta ulanish va o'tkazib yuborilgan hodisalar.

### gRPC

gRPC — Protocol Buffers (binary format) va HTTP/2 ustida ishlaydi; mikroservislar orasida ko'p uchraydi. O'z status kodlari bor (`OK`, `INVALID_ARGUMENT`, `NOT_FOUND`, `PERMISSION_DENIED`, `UNAUTHENTICATED` va boshqalar). Testlash uchun Postman'ning gRPC client'i yoki `grpcurl` kabi CLI vositalar ishlatiladi; `.proto` fayl — gRPC'ning "shartnomasi".

---

## 13. API test checklisti {#api-checklist}

| Soha            | Tekshiruv                                                                                                                                |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Hujjat          | Har endpoint hujjatlangan; misollar ishlaydi; javob kodlari to'liq                                                                       |
| Status          | Har holat uchun to'g'ri kod; `500` faqat haqiqiy kutilmagan holatda                                                                      |
| Header'lar      | `Content-Type`; `201` da `Location`; `405` da `Allow`; `401` da `WWW-Authenticate`; keshlash; `x-powered-by` kabi ortiqcha ma'lumot yo'q |
| Sxema           | Majburiy maydonlar, tiplar, `null` siyosati, ortiqcha maydonlar                                                                          |
| Qiymatlar       | Yuborilgan = saqlangan; hisob-kitoblar (summa, chegirma, yetkazish)                                                                      |
| Validatsiya     | Har maydon uchun EP/BVA, tiplar, uzunlik, format; jim konvertatsiya yo'q                                                                 |
| Biznes qoidalar | DemoBozor qoidalari (OTP, promo, savatcha limitlari, holat o'tishlari)                                                                   |
| Auth            | Token yo'q/buzilgan/muddati o'tgan/logout'dan keyin; rollar; IDOR                                                                        |
| Idempotentlik   | Takroriy so'rovlar, `Idempotency-Key`, ikki marta bosish                                                                                 |
| Parallel        | Race condition: oxirgi mahsulot, bir martalik promo                                                                                      |
| Sahifalash      | Chegaralar, `total` mosligi, dublikat/tushib qolish, saralash barqarorligi                                                               |
| Rate limit      | `429`, `Retry-After`, blokdan chiqish                                                                                                    |
| Xato formati    | Izchil tuzilma, maydon darajasidagi xatolar, ichki ma'lumot yo'q                                                                         |
| Yon ta'sirlar   | DB, keyingi GET, email/SMS/push, audit log                                                                                               |
| Performance     | Asosiy endpoint'lar javob vaqti kelishilgan chegarada ([QA 23](/ios-lessons/qa-performance-k6))                                          |
| Contract        | Javob OpenAPI/pact bilan mos; eski mobil versiyalar buzilmaydi                                                                           |

---

## 14. Amaliy mashqlar {#mashqlar}

### Mashq 1: DemoBozor promo-kod endpoint'i uchun negative testlar

Xayoliy `POST /api/v1/orders` body'sida ixtiyoriy `promoCode` maydoni bor. Qoidalar: 6–12 belgi, A–Z va 0–9, bitta buyurtmaga bitta, minimal summa, muddat, har foydalanuvchi bir marta. Kamida 10 ta negative/chegaraviy test yozing (body'dagi qiymat va kutilgan natija).

<details>
<summary>💡 Javobni ko'rish</summary>

| #   | `promoCode` / holat                               | Kutilgan                                                                                   |
| --- | ------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| 1   | `"ABC12"` (5 belgi)                               | `422`, format xatosi                                                                       |
| 2   | `"ABC123"` (6)                                    | ✅ qo'llanadi (agar kod mavjud va amal qilsa)                                              |
| 3   | `"ABCDEFGH1234"` (12)                             | ✅                                                                                         |
| 4   | `"ABCDEFGH12345"` (13)                            | `422`                                                                                      |
| 5   | `"abc123"`                                        | `422` (yoki hujjatda "katta harfga o'tkaziladi" deyilgan bo'lsa — aynan shu xatti-harakat) |
| 6   | `"ABC 123"`, `"ABC-123"`                          | `422`                                                                                      |
| 7   | `null` va maydon yo'q                             | ✅ promo'siz buyurtma                                                                      |
| 8   | `""`                                              | `422` yoki promo'siz — hujjatga ko'ra, lekin `500` emas                                    |
| 9   | Muddati o'tgan kod                                | `422`, aniq xabar ("muddati tugagan")                                                      |
| 10  | Minimal summadan kam savatcha                     | `422`, "minimal summa ..."                                                                 |
| 11  | Bir foydalanuvchi ikkinchi marta                  | `409` yoki `422`                                                                           |
| 12  | Ikki parallel buyurtmada bir xil bir martalik kod | Faqat bittasi muvaffaqiyatli                                                               |
| 13  | `"promoCode": ["ABC123", "XYZ789"]` (massiv)      | `422` — bitta buyurtmaga bitta kod                                                         |
| 14  | Mavjud kod + ataylab `"discount": 100` maydoni    | Chegirma faqat serverda hisoblanadi                                                        |

</details>

### Mashq 2: Topilmalarni tasniflang

Restful-booker kuzatuvlarini **bug / hujjatga mos, lekin odatga zid / to'g'ri** deb tasniflang va bug'larga jiddiylik bering: (a) `DELETE` → `201`; (b) `totalprice: 450.75` → `450`; (c) buzilgan JSON → `400`; (d) mavjud bo'lmagan ID'ga `DELETE` → `405`; (e) `"isAdmin": true` e'tiborsiz qoldirildi; (f) `GET /booking?checkin=2026-13-99` → `500`.

<details>
<summary>💡 Javobni ko'rish</summary>

|     | Tasnif                   | Izoh                                                           |
| --- | ------------------------ | -------------------------------------------------------------- |
| a   | Hujjatga mos, odatga zid | API dizayni masalasi, past ustuvorlik                          |
| b   | **Bug, Major**           | Pul summasi jimgina o'zgaradi — ma'lumot buzilishi             |
| c   | To'g'ri                  | Kutilgan xatti-harakat                                         |
| d   | **Bug, Minor**           | `404` kutiladi; `405` uchun `Allow` header'i ham yo'q          |
| e   | To'g'ri                  | Mass assignment'ga qarshi to'g'ri xatti-harakat (yaxshi belgi) |
| f   | **Bug, Major**           | Foydalanuvchi kiritgan filtr serverni yiqitadi; `400` kutiladi |

</details>

### Mashq 3: Toolshop mahsuloti uchun JSON Schema

`GET https://api.practicesoftwaretesting.com/products?page=1` javobidagi bitta mahsulot uchun sxema yozing: `id` va `name` — bo'sh bo'lmagan string, `price` — musbat son, `in_stock` — boolean, `category` va `brand` — `id`, `name` maydonli obyektlar. So'ng barcha sahifalardagi mahsulotlarni tekshiring.

<details>
<summary>💡 Javobni ko'rish</summary>

```javascript
// validate-products.mjs — Toolshop mahsulotlar ro'yxatini sxema bilan tekshirish
import Ajv2020 from "ajv/dist/2020.js";

const productSchema = {
  type: "object",
  required: ["id", "name", "price", "in_stock", "category", "brand"],
  properties: {
    id: { type: "string", minLength: 1 },
    name: { type: "string", minLength: 1, maxLength: 120 },
    price: { type: "number", exclusiveMinimum: 0 },
    in_stock: { type: "boolean" },
    is_rental: { type: "boolean" },
    category: {
      type: "object",
      required: ["id", "name"],
      properties: { id: { type: "string" }, name: { type: "string" } },
    },
    brand: {
      type: "object",
      required: ["id", "name"],
      properties: { id: { type: "string" }, name: { type: "string" } },
    },
  },
};

const pageSchema = {
  type: "object",
  required: ["current_page", "data", "last_page", "per_page", "total"],
  properties: {
    current_page: { type: "integer", minimum: 1 },
    per_page: { type: "integer", minimum: 1 },
    total: { type: "integer", minimum: 0 },
    data: { type: "array", items: { $ref: "#/$defs/product" } },
  },
  $defs: { product: productSchema },
};

const ajv = new Ajv2020({ allErrors: true });
const validate = ajv.compile(pageSchema);

let checked = 0;
for (let page = 1; page <= 6; page++) {
  const body = await fetch(
    `https://api.practicesoftwaretesting.com/products?page=${page}`,
  ).then((r) => r.json());
  checked += body.data.length;
  if (!validate(body)) {
    console.log(
      `❌ ${page}-sahifa:`,
      validate.errors.map((e) => `${e.instancePath} ${e.message}`),
    );
  }
}
console.log(`Tekshirilgan mahsulotlar: ${checked}`);
```

2026-yil oktabrda natija: `Tekshirilgan mahsulotlar: 50`, sxema xatolari yo'q. Keyingi qadam: sxemani OpenAPI hujjatidagi `components.schemas` bilan solishtiring — farq bo'lsa, bu ham topilma.

</details>

### Mashq 4: Avtorizatsiya matritsasi

DemoBozor'ning `GET /api/v1/orders/{id}` va `POST /api/v1/orders/{id}/cancel` endpoint'lari uchun test matritsasi tuzing: mehmon, buyurtma egasi, boshqa xaridor, sotuvchi, admin, muddati o'tgan token. `cancel` uchun holat qoidalarini ham hisobga oling.

<details>
<summary>💡 Javobni ko'rish</summary>

| Kim                  | `GET /orders/{id}`                                                    | `POST /orders/{id}/cancel`                                                                                                     |
| -------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Mehmon (token yo'q)  | `401`                                                                 | `401`                                                                                                                          |
| Muddati o'tgan token | `401`                                                                 | `401`                                                                                                                          |
| Buyurtma egasi       | `200`                                                                 | `CREATED`/`PENDING_PAYMENT`/`PAID`/`PACKING` da `200` (PAID'da pul qaytarish boshlanadi); `SHIPPED`/`DELIVERED` da `409`/`422` |
| Boshqa xaridor       | `403` yoki `404` (ID mavjudligini oshkor qilmaslik uchun `404` afzal) | `403`/`404`, buyurtma holati **o'zgarmaydi**                                                                                   |
| Sotuvchi             | Talabga ko'ra (faqat o'z mahsulotlari qismi)                          | `403`                                                                                                                          |
| Admin                | `200`                                                                 | Talabga ko'ra, audit log yoziladi                                                                                              |

Qo'shimcha: har bir rad etilgan urinishdan keyin `GET` bilan buyurtma holati o'zgarmaganini tekshiring (yon ta'sir!).

</details>

### Mashq 5: GraphQL'da "yashil" yolg'on

Nega GraphQL API uchun faqat `pm.response.to.have.status(200)` testi yetarli emas? Countries API misolida qaysi so'rovlar `200` qaytarib, aslida muammoli? Ularni ushlaydigan testni yozing.

<details>
<summary>💡 Javobni ko'rish</summary>

Countries API'da: mavjud bo'lmagan maydon → `200` + `errors`; mavjud bo'lmagan davlat kodi → `200` + `"country": null`. Ikkalasi ham faqat status bo'yicha "yashil".

```javascript
const json = pm.response.json();

pm.test("GraphQL xatosi yo'q", () => {
  pm.expect(json.errors, JSON.stringify(json.errors)).to.be.undefined;
});

pm.test("Davlat topildi", () => {
  // .to.exist — null ham, undefined ham bo'lmasligi kerak
  pm.expect(json.data && json.data.country, "data.country").to.exist;
});
```

</details>

### Mashq 6: Sahifalash test rejasi

Toolshop'da `GET /products` uchun `total: 50`, `per_page: 9`. Har sahifada nechta element bo'lishi kerak? Kamida 6 ta test case yozing (kutilgan natija bilan).

<details>
<summary>💡 Javobni ko'rish</summary>

`ceil(50 / 9) = 6` sahifa: 1–5-sahifalarda 9 tadan (45 ta), 6-sahifada 5 ta (46–50), 7-sahifa bo'sh.

| #   | So'rov                               | Kutilgan                                                                                                                                     |
| --- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | `page=1`                             | 9 element, `from=1`, `to=9`                                                                                                                  |
| 2   | `page=6`                             | 5 element, `from=46`, `to=50`                                                                                                                |
| 3   | `page=7`                             | `data: []`, `200` (2026-yil oktabrda shunday)                                                                                                |
| 4   | Barcha sahifalar ID'lari             | 50 ta noyob ID, dublikat yo'q                                                                                                                |
| 5   | `page=0`, `page=-1`, `page=abc`      | Hujjatlangan xatti-harakat; amalda jimgina 1-sahifa — talab bilan solishtiring                                                               |
| 6   | `sort=price,asc` bilan 1→6 sahifalar | Barcha 50 ta narx o'sish tartibida, sahifa chegaralarida ham (N-sahifaning oxirgisi ≤ N+1-sahifaning birinchisi) — 2026-yil oktabrda shunday |
| 7   | `sort=noto'g'ri,qiymat`              | `400` kutiladi; amalda `500` — bug                                                                                                           |

</details>

---

## Xulosa {#xulosa}

- "Status 200" — testning boshlanishi, oxiri emas: sxema, qiymatlar, biznes qoidalar va yon ta'sirlarni ham tekshiring.
- Har maydon uchun mavjudlik, tip, chegara, format, Unicode va "server hal qiladigan" qiymatlar (narx, rol) — tizimli negative test manbai.
- Eng xavfli API bug'lari jim bo'ladi: `"abc"` → `null`, `450.75` → `450`, yaroqsiz sana → `"0NaN-aN-aN"`. Ularni sxema va qiymat tekshiruvlari ushlaydi.
- JSON Schema bitta testda bir nechta bug'ni ushlaydi; Postman'da `$schema`ni yozmang, Node'da Ajv 2020 bilan draft 2020-12 ishlating.
- `401` = autentifikatsiya, `403` = avtorizatsiya; rollar matritsasi va IDOR/BOLA testlari majburiy.
- Mock'lar tezlik beradi, contract testing esa mock va haqiqat ajralib ketmasligini kafolatlaydi.
- GraphQL'da xato ko'pincha `200` ichida keladi — `errors` massivini tekshiring.

**Keyingi dars:** [QA 12: SQL va ma'lumotlar bazasi — QA uchun SELECT, JOIN, GROUP BY va data tekshiruvi](/ios-lessons/qa-sql)

## Manbalar {#manbalar}

1. [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
2. [RFC 9457 — Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457.html)
3. [MDN — 422 Unprocessable Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/422)
4. [MDN — 429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429)
5. [MDN — Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After)
6. [MDN — 405 Method Not Allowed](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/405)
7. [MDN — WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/WWW-Authenticate)
8. [JSON Schema — Understanding JSON Schema](https://json-schema.org/understanding-json-schema/)
9. [JSON Schema — Draft 2020-12](https://json-schema.org/draft/2020-12)
10. [JSON Schema — type keyword](https://json-schema.org/understanding-json-schema/reference/type)
11. [Ajv JSON schema validator](https://ajv.js.org/)
12. [Ajv — JSON Schema drafts support](https://ajv.js.org/json-schema.html)
13. [Postman Docs — Test script examples](https://learning.postman.com/docs/tests-and-scripts/write-scripts/test-examples/)
14. [Postman Docs — Set up mock servers](https://learning.postman.com/docs/design-apis/mock-apis/set-up-mock-servers/)
15. [Postman Docs — WebSocket requests](https://learning.postman.com/docs/sending-requests/websocket/websocket-overview/)
16. [Postman Docs — gRPC client](https://learning.postman.com/docs/sending-requests/grpc/grpc-client-overview/)
17. [OWASP API Security Top 10 (2023)](https://owasp.org/API-Security/editions/2023/en/0x11-t10/)
18. [OWASP — API1:2023 Broken Object Level Authorization](https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/)
19. [OWASP — API3:2023 Broken Object Property Level Authorization](https://owasp.org/API-Security/editions/2023/en/0xa3-broken-object-property-level-authorization/)
20. [OWASP — API4:2023 Unrestricted Resource Consumption](https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/)
21. [Pact — How Pact works](https://docs.pact.io/getting_started/how_pact_works)
22. [Pact — What is Pact good for?](https://docs.pact.io/getting_started/what_is_pact_good_for)
23. [OpenAPI Specification (latest)](https://spec.openapis.org/oas/latest.html)
24. [Mockoon](https://mockoon.com/) va [Mockoon CLI](https://mockoon.com/cli/)
25. [WireMock — Documentation](https://wiremock.org/docs/)
26. [Proxyman — Map Local](https://docs.proxyman.com/advanced-features/map-local)
27. [Playwright — Mock APIs](https://playwright.dev/docs/mock)
28. [GraphQL — Learn](https://graphql.org/learn/)
29. [GraphQL Specification (October 2021) — Errors](https://spec.graphql.org/October2021/)
30. [GraphQL over HTTP (draft)](https://graphql.github.io/graphql-over-http/draft/)
31. [Countries GraphQL API — GitHub](https://github.com/trevorblades/countries)
32. [MDN — WebSockets API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
33. [MDN — Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
34. [gRPC](https://grpc.io/) va [gRPC status codes](https://grpc.io/docs/guides/status-codes/)
35. [Restful-booker API hujjati](https://restful-booker.herokuapp.com/apidoc/index.html)
36. [Toolshop (Practice Software Testing) — GitHub](https://github.com/testsmith-io/practice-software-testing)
37. [Toolshop API hujjati](https://api.practicesoftwaretesting.com/api/documentation)
