@educa-corp/sdd-framework 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/build.js +11 -0
- package/bin/qc-base-map.json +119 -49
- package/bin/self-check.js +30 -0
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/qc-analyze.md +85 -75
- package/core/commands/qc-design-test.md +144 -25
- package/core/commands/qc-plan.md +40 -7
- package/core/commands/qc-review.md +74 -7
- package/core/commands/qc-run-test.md +21 -1
- package/core/commands/setup-ai-first.md +5 -5
- package/core/commands/update-framework.md +1 -1
- package/core/commands/validate-traces.md +1 -1
- package/core/modules/qc-playwright/stack-profile.yaml +3 -3
- package/core/rules/workflow.md +1 -1
- package/core/skills/qc/qa-analyst/DOC_GAP.template.md +47 -17
- package/core/skills/qc/qa-analyst/acceptance-criteria.md +1 -1
- package/core/skills/qc/qa-analyst/business-rules.md +2 -2
- package/core/skills/qc/qa-analyst/data-flow.md +2 -2
- package/core/skills/qc/qa-analyst/spec-breakdown.md +4 -4
- package/core/skills/qc/qa-analyst/spec-issue-reporter.md +14 -2
- package/core/skills/qc/qa-designer/api/auth-chain.md +155 -0
- package/core/skills/qc/qa-designer/api/auth-sequence.md +75 -0
- package/core/skills/qc/qa-designer/api/common-headers.md +61 -0
- package/core/skills/qc/qa-designer/api/crud-sequence.md +122 -0
- package/core/skills/qc/qa-designer/api/endpoint.md +231 -0
- package/core/skills/qc/qa-designer/api/http-status-codes.md +102 -0
- package/core/skills/qc/qa-designer/e2e/journey.md +13 -8
- package/core/skills/qc/qa-designer/exploratory/charter.md +2 -0
- package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +7 -4
- package/core/skills/qc/qa-designer/functional/api.md +87 -18
- package/core/skills/qc/qa-designer/functional/gui-feature.md +12 -9
- package/core/skills/qc/qa-designer/functional/gui-screen.md +12 -10
- package/core/skills/qc/qa-designer/integration/api.md +12 -5
- package/core/skills/qc/qa-designer/integration/db.md +12 -6
- package/core/skills/qc/qa-designer/integration/gui.md +12 -5
- package/core/skills/qc/qa-designer/integration/kafka.md +12 -5
- package/core/skills/qc/qa-designer/non-functional.md +12 -5
- package/core/skills/qc/qa-designer/shared/action-keywords-glossary.md +91 -0
- package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +105 -0
- package/core/skills/qc/qa-designer/shared/implicit-scenarios.md +22 -0
- package/core/skills/qc/qa-designer/shared/precision-rules.md +198 -0
- package/core/skills/qc/qa-designer/shared/read-doc-gap-inputs.md +25 -0
- package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +93 -0
- package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +243 -0
- package/core/skills/qc/qa-planner/risk-model.md +1 -1
- package/core/skills/qc/qa-planner/test-plan.md +24 -13
- package/core/skills/qc/qa-reviewer/script/e2e.md +9 -1
- package/core/skills/qc/qa-reviewer/script/exploratory.md +9 -1
- package/core/skills/qc/qa-reviewer/script/functional.md +9 -1
- package/core/skills/qc/qa-reviewer/script/integration.md +9 -1
- package/core/skills/qc/qa-reviewer/script/non-functional.md +9 -1
- package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +26 -0
- package/core/skills/qc/qa-reviewer/shared/review-check-groups.md +207 -0
- package/core/skills/qc/qa-reviewer/shared/review-file-template.md +228 -0
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +71 -13
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +53 -4
- package/core/skills/qc/qa-reviewer/test-case/functional.md +63 -15
- package/core/skills/qc/qa-reviewer/test-case/integration.md +64 -12
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +72 -13
- package/core/skills/qc/qa-runner/e2e.md +1 -1
- package/core/skills/qc/qa-runner/exploratory/session.md +1 -1
- package/core/steps/context-loader.md +1 -1
- package/core/steps/qc-scope.md +119 -0
- package/core/templates/project-context.yaml +3 -1
- package/docs/02-concepts/pipeline-steps/08-qc-automation.md +1 -1
- package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
- package/docs/04-reference/configuration.md +146 -146
- package/docs/04-reference/trace-schema.md +1 -1
- package/docs/explain/00-setup-ai-first.md +1 -1
- package/docs/explain/15-qc-analyze.md +1 -1
- package/docs/explain/16-qc-plan.md +1 -1
- package/docs/explain/17-qc-design-test.md +1 -1
- package/docs/plans/qc-implementation-log.md +288 -5
- package/docs/plans/qc-sync-command.md +2 -1
- package/package.json +1 -1
- package/scripts/migrate-qc-docs.js +261 -0
|
@@ -13,13 +13,18 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
|
|
|
13
13
|
> **Một chỗ CỐ Ý khác upstream:** mức nặng nhất dùng từ **`Blocker`**, không phải `Critical`.
|
|
14
14
|
> Lý do: `/qc-run-test` đọc `🔴 Blocker` để đặt *"scenario đang chờ PO"* vào sổ kết quả trace.
|
|
15
15
|
> Đổi từ là đứt liên kết đó. Ba mức còn lại giữ nguyên upstream.
|
|
16
|
+
>
|
|
17
|
+
> **Phạm vi: MỘT file cho cả (PRD × nền)** *(B11)*, không phải một file mỗi UC. Đây là quay về
|
|
18
|
+
> đúng thiết kế upstream — chính ID `GAP-<UC>-…` của bản gốc chỉ có nghĩa khi một file chứa
|
|
19
|
+
> nhiều UC, và file thật của đội QC (`DOC_GAP_FEAT-02-3.md`) cũng không có hậu tố UC. Đường
|
|
20
|
+
> dẫn: `{paths.qc_dir}/{TICKET-ID}/{platform}/DOC_GAP.md`.
|
|
16
21
|
|
|
17
|
-
# DOC GAP -- <
|
|
22
|
+
# DOC GAP -- <TICKET-ID> / <nền>: <Tên feature>
|
|
18
23
|
|
|
19
24
|
| Trường | Giá trị |
|
|
20
25
|
|---|---|
|
|
21
|
-
| Feature | `<
|
|
22
|
-
|
|
|
26
|
+
| Feature | `<TICKET-ID>` — `<Tên feature>` |
|
|
27
|
+
| Nền (platform) | `<web \| app \| system>` |
|
|
23
28
|
| Tài liệu nguồn | `<đường dẫn PRD>` · `<đường dẫn BDD nếu có>` · `<các file inputs/ liên quan>` |
|
|
24
29
|
| Ngày phân tích | `<YYYY-MM-DD>` |
|
|
25
30
|
| Tổng số gap | `<N>` (Blocker: x · High: y · Medium: z · Low: w) |
|
|
@@ -27,6 +32,21 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
|
|
|
27
32
|
|
|
28
33
|
---
|
|
29
34
|
|
|
35
|
+
## Phạm vi phân tích
|
|
36
|
+
|
|
37
|
+
> **Một file gap phủ CẢ PRD** — mỗi UC là các hàng trong bảng gap, phân biệt bằng cột `UC`.
|
|
38
|
+
> Bảng dưới là **căn cứ độ phủ**: nó phân biệt *"đã xét, không thấy gap"* với *"chưa xét"*.
|
|
39
|
+
> UC nào có BDD chưa `approved` thì ghi `⏸ Chưa xét` — **KHÔNG bỏ khỏi bảng**.
|
|
40
|
+
|
|
41
|
+
| UC | Tên UC | `@trace.status` | Đã phân tích? | Số gap |
|
|
42
|
+
|---|---|---|---|---|
|
|
43
|
+
| `<UC-ID>` | … | `approved` | ✅ | `<n>` |
|
|
44
|
+
| `<UC-ID>` | … | `draft` | `⏸ Chưa xét` | — |
|
|
45
|
+
|
|
46
|
+
**Trong phạm vi: `<n>`/`<N>` UC.** Chưa xét: `<danh sách UC-ID>` — chạy lại sau khi BDD được duyệt, hoặc `--include-draft` để xét luôn bản nháp.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
30
50
|
## Tài liệu đầu vào đã đọc để phân tích
|
|
31
51
|
|
|
32
52
|
> Liệt kê **đầy đủ** mọi file đã đọc để dựng phân tích gap (spec chính + mọi ref-link + transitive 1-hop). Đây là căn cứ độ phủ — mọi file đã mở đều phải có mặt, KHÔNG bỏ sót. Đường dẫn tính từ `{paths.specs_dir}`.
|
|
@@ -41,9 +61,14 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
|
|
|
41
61
|
|
|
42
62
|
---
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
> **ID gap mang UC** — `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`). Gap thuộc **cả PRD**, không riêng
|
|
65
|
+
> UC nào → `GAP-GEN-{nnn}`. Đánh số **độc lập trong từng UC**: chạy lại phân tích cho UC1
|
|
66
|
+
> KHÔNG được làm đổi số gap của UC2 — test case đã có đang trỏ `🚫 Block: [GAP-UC2-003]`, đổi
|
|
67
|
+
> số là đứt liên kết đó trong im lặng.
|
|
68
|
+
|
|
69
|
+
| ID | UC | Loại | Vấn đề cần confirm | Câu hỏi / Lý do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời |
|
|
70
|
+
|---|---|---|---|---|---|---|---|---|---|---|
|
|
71
|
+
| GAP-UC1-001 | `<UC-ID>` | MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION | Tiêu đề ngắn mô tả vấn đề | **Bối cảnh:** Ngữ cảnh dẫn đến gap này.<br/>**Vấn đề:** Điều gì chưa được spec hoặc mâu thuẫn.<br/>**Tại sao quan trọng:** Hậu quả nếu không làm rõ.<br/>**Gợi ý:** Ai cần làm gì để giải quyết. | `<đường dẫn file spec>` `<TÊN-BR hoặc AC gốc trong PRD, ví dụ FEAT-01-2-UC4-BR11>`: trích nguyên văn | Dev / PO / BA / Design | 🔴 Blocker | | Open | |
|
|
47
72
|
|
|
48
73
|
---
|
|
49
74
|
|
|
@@ -51,13 +76,15 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
|
|
|
51
76
|
|
|
52
77
|
| Mức độ | Gap ID |
|
|
53
78
|
|---|---|
|
|
54
|
-
| 🔴 Blocker | GAP
|
|
55
|
-
| 🟠 High | GAP
|
|
56
|
-
| 🟡 Medium | GAP
|
|
57
|
-
| ⚪ Low | GAP
|
|
79
|
+
| 🔴 Blocker | GAP-UC1-00x · GAP-UC3-00x · ... |
|
|
80
|
+
| 🟠 High | GAP-UC2-00x · ... |
|
|
81
|
+
| 🟡 Medium | GAP-UC1-00x · ... |
|
|
82
|
+
| ⚪ Low | GAP-GEN-00x · ... |
|
|
83
|
+
|
|
84
|
+
*Xếp theo mức độ trước, KHÔNG theo UC — người đọc cần biết "chặn gì" trước "chặn ở đâu".*
|
|
58
85
|
|
|
59
86
|
**Cần chốt trước khi viết test case:**
|
|
60
|
-
- GAP
|
|
87
|
+
- GAP-UC1-00x (`<UC-ID>`): <lý do block>
|
|
61
88
|
|
|
62
89
|
---
|
|
63
90
|
|
|
@@ -99,16 +126,19 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
|
|
|
99
126
|
Trước khi lưu file gap, kiểm tra **từng hàng** trong bảng gap:
|
|
100
127
|
|
|
101
128
|
- [ ] **Có section `Tài liệu đầu vào đã đọc để phân tích`** – bảng liệt kê **đầy đủ** mọi file đã đọc (spec chính + ref-link + transitive 1-hop), có đường dẫn `{paths.specs_dir}`, vai trò, phiên bản; ghi tổng số + trạng thái lane API. KHÔNG bỏ sót file nào đã mở.
|
|
102
|
-
- [ ] **
|
|
103
|
-
- [ ] **
|
|
104
|
-
- [ ] **Cột
|
|
129
|
+
- [ ] **Có section `Phạm vi phân tích`** – bảng mỗi UC một hàng kèm `@trace.status` + Đã phân tích? + Số gap. UC chưa duyệt vẫn có hàng, ghi `⏸ Chưa xét`. Thiếu bảng này thì không ai phân biệt được *"đã xét, không thấy gap"* với *"chưa xét"*.
|
|
130
|
+
- [ ] **11 cột đủ** – đúng thứ tự: `ID | UC | Loại | Vấn đề cần confirm | Câu hỏi / Lý do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời`
|
|
131
|
+
- [ ] **Cột 2 = `UC`** – mã UC đầy đủ (`<TICKET-ID>-UC{N}`), hoặc `— (toàn PRD)` cho gap `GAP-GEN-`. Mọi UC có gap phải khớp một hàng `✅` ở bảng *Phạm vi phân tích*.
|
|
132
|
+
- [ ] **ID dạng `GAP-UC{N}-{nnn}`** (hoặc `GAP-GEN-{nnn}`) – KHÔNG dùng `GAP-01` phẳng: số phẳng sẽ bị đánh lại khi phân tích lại một UC, làm đứt `🚫 Block: [GAP-UC1-001]` trong test case đã có.
|
|
133
|
+
- [ ] **Cột 4 = `Vấn đề cần confirm`** – KHÔNG viết tắt thành `Vấn đề`
|
|
134
|
+
- [ ] **Cột 5 = `Câu hỏi / Lý do cần confirm & Gợi ý`** – bắt buộc có đủ 4 phần, tách bằng `<br/>`:
|
|
105
135
|
```
|
|
106
136
|
**Bối cảnh:** ...<br/>**Vấn đề:** ...<br/>**Tại sao quan trọng:** ...<br/>**Gợi ý:** ...
|
|
107
137
|
```
|
|
108
138
|
Không viết 4 mục liên tiếp trên cùng một dòng.
|
|
109
|
-
- [ ] **Cột
|
|
110
|
-
- [ ] **Cột Mức độ** (cột
|
|
111
|
-
- [ ] **Cột Giao cho đội** (cột
|
|
139
|
+
- [ ] **Cột 6 = `Trích đoạn tài liệu (Evidence)`** – KHÔNG viết tắt thành `Evidence`; dẫn nguyên văn + đường dẫn file `{paths.specs_dir}`; **dùng PRD source ID** (ví dụ `FEAT-01-2-UC4-BR11`), KHÔNG dùng internal analysis ID (ví dụ `BR-UC4-12`)
|
|
140
|
+
- [ ] **Cột Mức độ** (cột 8) – bắt buộc dùng emoji: `🔴 Blocker` / `🟠 High` / `🟡 Medium` / `⚪ Low`. Không được ghi text thuần.
|
|
141
|
+
- [ ] **Cột Giao cho đội** (cột 7) – dùng: `Dev` / `PO` / `BA` / `Design` / `Architect` / `Analytics` hoặc kết hợp. Nếu thấy "Open" ở đây → đang bị lệch cột.
|
|
112
142
|
- [ ] **Cột Người trả lời = vai trò** (PO / BA / Dev / Design / ...). Nếu thấy "Open" ở đây → đang bị lệch cột.
|
|
113
143
|
- [ ] **Cột Trạng thái** = `Open` / `Resolved` / `Out of Scope` / `Re-scoped → Covered`
|
|
114
144
|
- [ ] **Trạng thái chung** trong metadata có emoji: `🔴 Blocked` / `🟠 Cần làm rõ` / `🟢 Đủ rõ để thiết kế TC`
|
|
@@ -53,7 +53,7 @@ Mỗi AC gắn mã trace: chức năng + BR-xx để TC sau này map 1-1.
|
|
|
53
53
|
|
|
54
54
|
## Output
|
|
55
55
|
|
|
56
|
-
Ghi vào **mục Acceptance Criteria** của `{
|
|
56
|
+
Ghi vào **mục Acceptance Criteria** của `{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`
|
|
57
57
|
(KHÔNG tạo file riêng — qc-analyze chỉ trả 2 file: `REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`):
|
|
58
58
|
|
|
59
59
|
- Danh sách AC dạng Given/When/Then, có mã trace về chức năng, business rule (BR-xx)
|
|
@@ -83,11 +83,11 @@ Với rule có nhiều điều kiện kết hợp → gợi ý dựng **Decision
|
|
|
83
83
|
|
|
84
84
|
## Output
|
|
85
85
|
|
|
86
|
-
Ghi vào **mục Business Rules** của `{
|
|
86
|
+
Ghi vào **mục Business Rules** của `{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`
|
|
87
87
|
(KHÔNG tạo file riêng — qc-analyze chỉ trả 2 file: `REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`):
|
|
88
88
|
|
|
89
89
|
- Bảng business rule có ID (BR-xx) để TC trace ngược về.
|
|
90
90
|
- Gợi ý các rule cần Decision Table / BVA khi sang qa-designer.
|
|
91
91
|
|
|
92
|
-
Rule MÂU THUẪN / KHÔNG RÕ → ghi vào `{
|
|
92
|
+
Rule MÂU THUẪN / KHÔNG RÕ → ghi vào `{qc_artifact_dir}DOC_GAP.md`
|
|
93
93
|
(loại CONTRADICTORY / AMBIGUOUS, cột "Ảnh hưởng" trỏ BR-xx).
|
|
@@ -54,7 +54,7 @@ Thể hiện luồng dạng bước tuần tự hoặc sơ đồ text:
|
|
|
54
54
|
|
|
55
55
|
## Output
|
|
56
56
|
|
|
57
|
-
Ghi vào **mục Data Flow** của `{
|
|
57
|
+
Ghi vào **mục Data Flow** của `{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`
|
|
58
58
|
(KHÔNG tạo file riêng — qc-analyze chỉ trả 2 file: `REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`):
|
|
59
59
|
|
|
60
60
|
- Sơ đồ/list luồng dữ liệu cho mỗi kịch bản chính.
|
|
@@ -63,4 +63,4 @@ Ghi vào **mục Data Flow** của `{paths.qc_dir}/{UC-ID}/REQUIREMENT_ANALYSIS.
|
|
|
63
63
|
- Dữ liệu/trạng thái cần chuẩn bị & cleanup → đầu vào fixture cho qa-runner.
|
|
64
64
|
|
|
65
65
|
Chặng nào luồng/hành vi chưa rõ (vd lỗi xử lý ra sao, retry, partial commit) →
|
|
66
|
-
ghi vào `{
|
|
66
|
+
ghi vào `{qc_artifact_dir}DOC_GAP.md` (loại MISSING / OPEN QUESTION).
|
|
@@ -29,7 +29,7 @@ Bóc tách spec/PRD/user story thô thành mô tả yêu cầu có cấu trúc c
|
|
|
29
29
|
vào phân tích/BR/AC. Với file Confluence/HTML/MHTML: phát hiện qua thẻ `<s>`,
|
|
30
30
|
`<strike>`, `<del>` hoặc style `text-decoration: line-through`.
|
|
31
31
|
3. Xác định: feature name, actor/role, mục tiêu nghiệp vụ, phạm vi (in/out scope).
|
|
32
|
-
4. Đánh dấu phần MƠ HỒ / THIẾU → ghi vào `DOC_GAP.md` (gap GAP-
|
|
32
|
+
4. Đánh dấu phần MƠ HỒ / THIẾU → ghi vào `DOC_GAP.md` (gap `GAP-UC{N}-{nnn}`; gap thuộc cả PRD → `GAP-GEN-{nnn}`).
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
@@ -49,7 +49,7 @@ F. GIẢ ĐỊNH & CÂU HỎI MỞ: điều suy ra được vs điều cần dev
|
|
|
49
49
|
## Output
|
|
50
50
|
|
|
51
51
|
⚠️ `/qc-analyze` chỉ ghi **ĐÚNG 2 FILE** cho mỗi UC, đặt trong thư mục QC **lộ ra ngoài**
|
|
52
|
-
`{
|
|
52
|
+
`{qc_artifact_dir}` (mặc định `docs/{TICKET-ID}/{platform}/` — gom theo **PRD**, KHÔNG để trong `.agent/` ẩn):
|
|
53
53
|
`REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`. KHÔNG tách mỗi bước phân tích thành file riêng.
|
|
54
54
|
|
|
55
55
|
Phần spec-breakdown là **mục đầu tiên** của `REQUIREMENT_ANALYSIS.md`:
|
|
@@ -57,7 +57,7 @@ Phần spec-breakdown là **mục đầu tiên** của `REQUIREMENT_ANALYSIS.md`
|
|
|
57
57
|
- Sơ đồ/list luồng chính & phụ
|
|
58
58
|
- Danh sách giả định và câu hỏi mở (đánh dấu rõ điều CHƯA chắc)
|
|
59
59
|
|
|
60
|
-
Đồng thời ghi mọi khoảng trống phát hiện vào `{
|
|
61
|
-
(theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`), mỗi gap có ID `GAP-
|
|
60
|
+
Đồng thời ghi mọi khoảng trống phát hiện vào `{qc_artifact_dir}DOC_GAP.md`
|
|
61
|
+
(theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`), mỗi gap có ID `GAP-UC{N}-{nnn}` — mã mang UC để phân tích lại một UC không đánh số lại UC khác.
|
|
62
62
|
|
|
63
63
|
Kết thúc bằng gợi ý: feature đã đủ rõ để chuyển sang `qa-planner` (phân tích rủi ro) chưa.
|
|
@@ -14,6 +14,9 @@ có cấu trúc, đủ thông tin để người nhận trả lời được nga
|
|
|
14
14
|
> **`/qc-analyze` nạp file này** *(B9 — hợp nhất 2026-08-25)*. Nó là luật viết cho
|
|
15
15
|
> `DOC_GAP.template.md` — template **duy nhất** của file gap. Bản 9 cột cũ đã bỏ.
|
|
16
16
|
>
|
|
17
|
+
> **Một file gap phủ CẢ PRD** *(B11)* — `{paths.qc_dir}/{TICKET-ID}/{platform}/DOC_GAP.md`,
|
|
18
|
+
> mỗi UC là các hàng phân biệt bằng cột `UC`. Không còn một-file-mỗi-UC.
|
|
19
|
+
>
|
|
17
20
|
> **Một chỗ cố ý khác upstream:** mức nặng nhất dùng `🔴 Blocker`, không phải `Critical` —
|
|
18
21
|
> `/qc-run-test` đọc đúng từ đó để đặt *"scenario đang chờ PO"* vào sổ trace.
|
|
19
22
|
|
|
@@ -38,8 +41,11 @@ Một file gap gồm:
|
|
|
38
41
|
transitive 1-hop): `# | Đường dẫn | Vai trò | Phiên bản`, kèm tổng số.
|
|
39
42
|
KHÔNG bỏ sót file nào đã mở — đây là **căn cứ độ phủ**: không có nó thì không ai phân biệt
|
|
40
43
|
được *"đã đọc và không thấy"* với *"chưa đọc"*.
|
|
41
|
-
2. **Bảng gap
|
|
42
|
-
`ID | Loại | Vấn đề cần confirm | Câu hỏi / Lý do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời`
|
|
44
|
+
2. **Bảng gap 11 cột**, đúng thứ tự:
|
|
45
|
+
`ID | UC | Loại | Vấn đề cần confirm | Câu hỏi / Lý do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời`
|
|
46
|
+
3. **Section `Phạm vi phân tích`** — BẮT BUỘC, giữa metadata và section trên. Mỗi UC của
|
|
47
|
+
(PRD × nền) một hàng, kèm `@trace.status` + đã phân tích chưa + số gap. UC có BDD chưa
|
|
48
|
+
`approved` **vẫn có hàng**, ghi `⏸ Chưa xét`.
|
|
43
49
|
|
|
44
50
|
## Quy tắc
|
|
45
51
|
|
|
@@ -56,6 +62,12 @@ Một file gap gồm:
|
|
|
56
62
|
do bước phân tích tự đánh số lại (vd `BR-UC4-12`). Hai hệ ID khác nhau; trộn vào là người
|
|
57
63
|
đọc không tra ngược được về PRD.
|
|
58
64
|
- **Mức độ bắt buộc có emoji**: `🔴 Blocker` · `🟠 High` · `🟡 Medium` · `⚪ Low`.
|
|
65
|
+
- **ID gap mang UC**: `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`); gap thuộc cả PRD → `GAP-GEN-{nnn}`.
|
|
66
|
+
Đánh số **độc lập trong từng UC** — phân tích lại UC1 KHÔNG được làm đổi số gap của UC2,
|
|
67
|
+
vì test case đã có đang trỏ `🚫 Block: [GAP-UC2-003]` và đổi số là đứt liên kết trong im lặng.
|
|
68
|
+
**Không dùng `GAP-01` phẳng.**
|
|
69
|
+
- **Cột `UC`** ghi mã UC đầy đủ (`{TICKET-ID}-UC{N}`), hoặc `— (toàn PRD)` cho `GAP-GEN-`.
|
|
70
|
+
Mọi UC xuất hiện ở đây phải khớp một hàng `✅` trong bảng *Phạm vi phân tích*.
|
|
59
71
|
- **Pipe trong cell**: escape thành `\|` (vd `{a\|b\|c}`), nếu không sẽ vỡ bảng.
|
|
60
72
|
- **Thêm hàng vào bảng đã có**: nối **ngay liền sau hàng cuối**, TUYỆT ĐỐI không chèn dòng
|
|
61
73
|
trắng giữa các hàng — dòng trắng làm Markdown tách thành hai bảng riêng và các gap mới
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-04
|
|
4
|
+
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-designer/api-testcase-designer/api/templates/auth-chain.md
|
|
6
|
+
upstream_sha: ef8f9761f1b53d0ccd752f8fe0c13a427fa15f45
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
> **VIẾT LẠI, không copy** *(B12)* — cùng lý do với `endpoint.md`: bản upstream có trước luật
|
|
10
|
+
> ATOMIC (2026-07-09), gộp nhiều kết cục vào một `**Expected:**` và dùng hệ mã
|
|
11
|
+
> `API-<FEATURE>-AUTH-001`. Ba nhóm ca giữ nguyên; cấu trúc theo `shared/tc-metadata-format.md`.
|
|
12
|
+
|
|
13
|
+
# Khuôn TC — chuỗi xác thực (login → token → gọi endpoint được bảo vệ)
|
|
14
|
+
|
|
15
|
+
Dùng khi TC phải đăng nhập lấy token trước khi gọi endpoint chính. Thứ tự gọi và điểm verify
|
|
16
|
+
của từng chuỗi: xem `auth-sequence.md`.
|
|
17
|
+
|
|
18
|
+
Phủ **3 nhóm ca**: chuỗi thành công · token hết hạn · login sai mật khẩu.
|
|
19
|
+
|
|
20
|
+
> **Một chuỗi auth KHÔNG phải một TC.** Chuỗi có nhiều bước, mỗi bước có kết cục riêng —
|
|
21
|
+
> nên nó là **nhiều TC độc lập**, mỗi TC giữ FULL steps để tự tới được trạng thái cần assert.
|
|
22
|
+
> Đây đúng là chỗ luật ATOMIC dễ bị bỏ nhất, vì cảm giác "một chuỗi = một ca".
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Nhóm ca 1 — Chuỗi thành công
|
|
27
|
+
|
|
28
|
+
### TC_<FEATURE>_101 — login trả về access_token
|
|
29
|
+
|
|
30
|
+
- **Title:** Login role `<role>` với thông tin đúng trả về access_token
|
|
31
|
+
- **Feature:** <TICKET-ID> — <Tên feature>
|
|
32
|
+
- **Priority:** P0
|
|
33
|
+
- **Status:** Draft
|
|
34
|
+
- **Author:** AI
|
|
35
|
+
- **Tags:** api, happy-path, auth, <feature-tag>
|
|
36
|
+
- **Trace:** BR-xx
|
|
37
|
+
- **@trace.verifies:** {UC-ID}-SC{N}
|
|
38
|
+
|
|
39
|
+
#### Preconditions
|
|
40
|
+
- Tài khoản role `<role>` đã tồn tại (username `<username>`, password `<password>`).
|
|
41
|
+
|
|
42
|
+
#### Test Data
|
|
43
|
+
- Method: POST
|
|
44
|
+
- Path: `/auth/login`
|
|
45
|
+
- Headers: Content-Type: application/json
|
|
46
|
+
- Body:
|
|
47
|
+
```json
|
|
48
|
+
{ "username": "<username>", "password": "<password>" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Test Steps
|
|
52
|
+
1. **[Action]** Gửi `POST /auth/login` với body trên.
|
|
53
|
+
2. **[Verify]** Đọc `body.access_token`.
|
|
54
|
+
|
|
55
|
+
#### Expected Result
|
|
56
|
+
- HTTP 200 AND `body.access_token` exists
|
|
57
|
+
|
|
58
|
+
> ✅ Compound hợp lệ — cả hai vế nói về **cùng một** kết cục: "login thành công và có token".
|
|
59
|
+
|
|
60
|
+
### TC_<FEATURE>_102 — token lấy được gọi được endpoint bảo vệ
|
|
61
|
+
|
|
62
|
+
- **Title:** Token của role `<role>` gọi <METHOD> <protected_path> trả về <success_code>
|
|
63
|
+
- **Priority:** P0
|
|
64
|
+
- **Tags:** api, happy-path, auth, <feature-tag>
|
|
65
|
+
- **Trace:** BR-xx
|
|
66
|
+
|
|
67
|
+
#### Preconditions
|
|
68
|
+
- Tài khoản role `<role>` đã tồn tại.
|
|
69
|
+
|
|
70
|
+
#### Test Steps
|
|
71
|
+
1. **[Action]** Gửi `POST /auth/login` lấy `access_token`.
|
|
72
|
+
2. **[Action]** Gửi `<METHOD> <protected_path>` với `Authorization: Bearer {access_token}`.
|
|
73
|
+
3. **[Verify]** Đọc status code của request thứ hai.
|
|
74
|
+
|
|
75
|
+
#### Expected Result
|
|
76
|
+
- HTTP `<success_code>`
|
|
77
|
+
|
|
78
|
+
> ⚠️ Bước 1 **lặp lại đủ**, không viết "dùng token từ TC_101". Mỗi TC tách phải tự chạy độc
|
|
79
|
+
> lập — nếu nó phụ thuộc TC khác đã chạy thì thứ tự chạy trở thành điều kiện ẩn, và chạy riêng
|
|
80
|
+
> một TC sẽ fail vì lý do không liên quan tới thứ nó kiểm.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Nhóm ca 2 — Token hết hạn
|
|
85
|
+
|
|
86
|
+
### TC_<FEATURE>_103 — token hết hạn trả về 401
|
|
87
|
+
|
|
88
|
+
- **Priority:** P0
|
|
89
|
+
- **Tags:** api, negative, auth, security
|
|
90
|
+
- **Trace:** BR-xx
|
|
91
|
+
|
|
92
|
+
#### Preconditions
|
|
93
|
+
- Có sẵn một token đã quá thời hạn (`<expired_token>` — seed hoặc lấy từ fixture).
|
|
94
|
+
|
|
95
|
+
#### Test Data
|
|
96
|
+
- Headers: Authorization: Bearer `<expired_token>`
|
|
97
|
+
|
|
98
|
+
#### Test Steps
|
|
99
|
+
1. **[Action]** Gửi `<METHOD> <protected_path>` với token đã hết hạn.
|
|
100
|
+
2. **[Verify]** Đọc status code.
|
|
101
|
+
|
|
102
|
+
#### Expected Result
|
|
103
|
+
- HTTP 401
|
|
104
|
+
|
|
105
|
+
*Chuỗi refresh token (nếu API có) là nhóm ca riêng — xem `auth-sequence.md` §Chuỗi 2. Mỗi bước
|
|
106
|
+
của chuỗi refresh cũng là một TC.*
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Nhóm ca 3 — Login sai mật khẩu
|
|
111
|
+
|
|
112
|
+
### TC_<FEATURE>_104 — sai password trả về 401
|
|
113
|
+
|
|
114
|
+
- **Priority:** P0
|
|
115
|
+
- **Tags:** api, negative, auth, security
|
|
116
|
+
- **Trace:** BR-xx
|
|
117
|
+
|
|
118
|
+
#### Test Data
|
|
119
|
+
- Body: `{ "username": "<username>", "password": "<wrong_password>" }`
|
|
120
|
+
|
|
121
|
+
#### Test Steps
|
|
122
|
+
1. **[Action]** Gửi `POST /auth/login` với password sai.
|
|
123
|
+
2. **[Verify]** Đọc status code.
|
|
124
|
+
|
|
125
|
+
#### Expected Result
|
|
126
|
+
- HTTP 401
|
|
127
|
+
|
|
128
|
+
### TC_<FEATURE>_105 — sai password KHÔNG trả về token
|
|
129
|
+
|
|
130
|
+
- **Priority:** P0
|
|
131
|
+
- **Tags:** api, negative, auth, security
|
|
132
|
+
- **Trace:** BR-xx
|
|
133
|
+
|
|
134
|
+
#### Test Steps
|
|
135
|
+
1. **[Action]** Gửi `POST /auth/login` với password sai.
|
|
136
|
+
2. **[Verify]** Kiểm trường `access_token` trong body.
|
|
137
|
+
|
|
138
|
+
#### Expected Result
|
|
139
|
+
- `body.access_token` not exists
|
|
140
|
+
|
|
141
|
+
> **Vì sao tách khỏi TC_104 dù cùng một request.** *"Trả 401"* và *"không phát token"* là hai
|
|
142
|
+
> kết cục **fail độc lập được**: một API hỏng có thể trả 401 mà vẫn kèm token trong body. Gộp
|
|
143
|
+
> lại thì TC pass khi chỉ một nửa đúng — và nửa còn lại là lỗ bảo mật.
|
|
144
|
+
>
|
|
145
|
+
> Đây chính là luật *"assertion loại-trừ (KHÔNG có / KHÔNG xảy ra) là oracle THẬT"* ở
|
|
146
|
+
> `shared/tc-metadata-format.md`. Khi tách bằng script, kiểm lại không đánh rơi các bullet
|
|
147
|
+
> phủ định — chúng dễ bị bỏ nhất.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Không cần Teardown
|
|
152
|
+
|
|
153
|
+
Cả 5 TC trên **không tạo bản ghi thật** — chúng chỉ login và đọc. Không thêm dòng
|
|
154
|
+
`#### Teardown` cho có. Thêm khi và chỉ khi TC tạo tài khoản mới hoặc đổi trạng thái thật; lúc
|
|
155
|
+
đó ghi `[Teardown: deactivate user via admin API]` *(`shared/precision-rules.md` §8)*.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-04
|
|
4
|
+
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-designer/api-testcase-designer/api/flows/auth-sequence.md
|
|
6
|
+
upstream_sha: 69d60c12fe6d641638132581cce1dc6c955354a6
|
|
7
|
+
---
|
|
8
|
+
# Flow — Auth Sequence (login → token → authenticated calls)
|
|
9
|
+
|
|
10
|
+
Mô tả chuỗi gọi API liên tiếp liên quan đến xác thực.
|
|
11
|
+
Load file này khi thiết kế TC cần thực hiện đăng nhập trước khi gọi endpoint chính.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Chuỗi 1 — Login lấy token + gọi 1 endpoint
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
POST /auth/login → nhận access_token → <METHOD> <protected_endpoint>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Khi dùng:** TC cần tài khoản đăng nhập trước khi thao tác (tạo/sửa/xóa resource).
|
|
22
|
+
|
|
23
|
+
**Điểm verify:**
|
|
24
|
+
1. Sau login: HTTP 200, `body.access_token` không rỗng.
|
|
25
|
+
2. Sau gọi endpoint: status code kỳ vọng, body đúng schema.
|
|
26
|
+
|
|
27
|
+
**TC pattern:** xem `templates/auth-chain.md` (TC AUTH-001).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Chuỗi 2 — Token refresh
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
POST /auth/login → nhận access_token + refresh_token
|
|
35
|
+
→ (sau khi access_token hết hạn)
|
|
36
|
+
→ POST /auth/refresh → nhận access_token mới
|
|
37
|
+
→ <METHOD> <protected_endpoint> → thành công
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Khi dùng:** API có cơ chế refresh token (nếu plan đề cập).
|
|
41
|
+
|
|
42
|
+
**Điểm verify:**
|
|
43
|
+
1. Login: nhận cả `access_token` và `refresh_token`.
|
|
44
|
+
2. Gọi refresh: HTTP 200, `body.access_token` khác token cũ.
|
|
45
|
+
3. Gọi endpoint bằng token mới: thành công.
|
|
46
|
+
|
|
47
|
+
**TC negative liên quan:**
|
|
48
|
+
- Dùng refresh_token hết hạn → 401.
|
|
49
|
+
- Dùng refresh_token đã dùng lần trước (replay) → 401.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Chuỗi 3 — Multi-role: cùng endpoint, khác role, khác kết quả
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
POST /auth/login (admin) → token_admin → <METHOD> <endpoint> → 200/201
|
|
57
|
+
POST /auth/login (teacher) → token_teacher → <METHOD> <endpoint> → 200 hoặc 403
|
|
58
|
+
POST /auth/login (student) → token_student → <METHOD> <endpoint> → 403
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Khi dùng:** TC RBAC — verify phân quyền theo role.
|
|
62
|
+
|
|
63
|
+
**Điểm verify:** mỗi role cần TC riêng; Expected ghi rõ mã cho từng role.
|
|
64
|
+
|
|
65
|
+
**TC pattern:** Decision Table (role × endpoint → expected code).
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Ghi chú thiết kế TC
|
|
70
|
+
|
|
71
|
+
- **Test isolation:** mỗi TC tự gọi login để lấy token riêng. Không dùng token từ TC khác.
|
|
72
|
+
- **Token fixture (Python):** nếu nhiều TC cùng role, dùng `conftest.py` fixture
|
|
73
|
+
`logged_in_token_<role>` (session scope) để tránh login lặp lại khi chạy test.
|
|
74
|
+
- **Expired token test:** tạo token expired sẵn trong `test_data/tokens.json` thay vì
|
|
75
|
+
đợi token tự hết hạn trong test.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-04
|
|
4
|
+
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-designer/api-testcase-designer/api/references/common-headers.md
|
|
6
|
+
upstream_sha: 74f10a36bec7b40f4d9de2d0928798cb6fad0c2c
|
|
7
|
+
---
|
|
8
|
+
# Bảng tra cứu — Headers chuẩn
|
|
9
|
+
|
|
10
|
+
Dùng khi điền mục **Test Data > Headers** trong TC API.
|
|
11
|
+
Chỉ ghi header thực sự cần thiết cho TC đó; không copy toàn bộ bảng.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Request headers thường dùng
|
|
16
|
+
|
|
17
|
+
| Header | Giá trị điển hình | Khi nào cần |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `Authorization` | `Bearer <access_token>` | Mọi endpoint yêu cầu auth |
|
|
20
|
+
| `Content-Type` | `application/json` | POST / PUT có body JSON |
|
|
21
|
+
| `Content-Type` | `multipart/form-data` | Upload file |
|
|
22
|
+
| `Accept` | `application/json` | Luôn ghi khi API có thể trả nhiều format |
|
|
23
|
+
| `Accept-Language` | `vi` hoặc `en` | Khi test response message theo ngôn ngữ |
|
|
24
|
+
| `X-Request-ID` | `<uuid>` | Nếu API yêu cầu idempotency key |
|
|
25
|
+
| `X-Tenant-ID` | `<tenant_id>` | Nếu LMS hỗ trợ multi-tenant |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Response headers cần verify (ghi vào `[Verify]` step nếu TC liên quan)
|
|
30
|
+
|
|
31
|
+
| Header | Verify khi |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `Content-Type: application/json` | Mọi response trả JSON |
|
|
34
|
+
| `Location: <url>` | POST 201 Created — verify URL tạo mới |
|
|
35
|
+
| `Retry-After: <seconds>` | TC rate limit (429) |
|
|
36
|
+
| `X-RateLimit-Remaining` | TC rate limit — verify còn bao nhiêu request |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## TC Negative liên quan header
|
|
41
|
+
|
|
42
|
+
| Kịch bản | Header điều chỉnh | Expected |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| Không có Authorization | Bỏ header `Authorization` | 401 |
|
|
45
|
+
| Token sai format | `Authorization: InvalidToken abc` | 401 |
|
|
46
|
+
| Content-Type sai | `Content-Type: text/plain` (khi API cần JSON) | 400 hoặc 415 |
|
|
47
|
+
| Token đúng nhưng sai role | Token của role không được phép | 403 |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Quy ước ghi trong Test Data
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Headers:
|
|
55
|
+
- Authorization: Bearer <valid_token_admin>
|
|
56
|
+
- Content-Type: application/json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- Dùng placeholder có tên rõ ràng: `<valid_token_admin>`, `<valid_token_teacher>`,
|
|
60
|
+
`<expired_token>`, `<token_wrong_role>` — không dùng `<token>` chung chung.
|
|
61
|
+
- Không ghi header không liên quan đến TC (vd `User-Agent`, `Host`).
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-04
|
|
4
|
+
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-designer/api-testcase-designer/api/flows/crud-sequence.md
|
|
6
|
+
upstream_sha: 65bf214d9f9beabe3c40b8697ba7c058adfd8bb5
|
|
7
|
+
---
|
|
8
|
+
# Flow — CRUD Sequence (create → read → update → delete)
|
|
9
|
+
|
|
10
|
+
Mô tả các chuỗi gọi API CRUD phụ thuộc nhau.
|
|
11
|
+
Load file này khi TC cần tạo resource trước khi thao tác sửa/xóa/đọc chi tiết.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Chuỗi 1 — Create → Read
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
POST <resource> → HTTP 201, nhận body.id
|
|
19
|
+
→ GET <resource>/{id} → HTTP 200, body đúng data đã tạo
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Khi dùng:** Verify GET trả đúng data sau khi tạo; kiểm tra data persistence.
|
|
23
|
+
|
|
24
|
+
**Điểm verify:**
|
|
25
|
+
1. POST: HTTP 201, `body.id` tồn tại và không rỗng.
|
|
26
|
+
2. GET bằng `id` từ bước 1: HTTP 200; các field chính (name, status…) = giá trị đã gửi lúc POST.
|
|
27
|
+
|
|
28
|
+
**TC pattern:**
|
|
29
|
+
- 1 TC happy cho chuỗi (P0).
|
|
30
|
+
- Tách TC riêng nếu cần test GET với ID không tồn tại (P1).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Chuỗi 2 — Create → Update → Read
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
POST <resource> → HTTP 201, nhận id
|
|
38
|
+
→ PUT <resource>/{id} → HTTP 200/204
|
|
39
|
+
→ GET <resource>/{id} → HTTP 200, body = data đã update
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Khi dùng:** Verify update áp dụng đúng, không mất dữ liệu không liên quan.
|
|
43
|
+
|
|
44
|
+
**Điểm verify:**
|
|
45
|
+
1. POST: `body.id` tồn tại.
|
|
46
|
+
2. PUT: HTTP 200 hoặc 204 (theo contract).
|
|
47
|
+
3. GET sau PUT: field đã update = giá trị mới; field không update = giá trị cũ (unchanged).
|
|
48
|
+
|
|
49
|
+
**TC negative liên quan:**
|
|
50
|
+
- PUT với ID không tồn tại → 404.
|
|
51
|
+
- PUT với body vi phạm validation → 400.
|
|
52
|
+
- PUT sau khi resource đã ở trạng thái không cho sửa (vd đã publish) → 409/422.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Chuỗi 3 — Create → Delete → Read
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
POST <resource> → HTTP 201, nhận id
|
|
60
|
+
→ DELETE <resource>/{id} → HTTP 200/204
|
|
61
|
+
→ GET <resource>/{id} → HTTP 404
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Khi dùng:** Verify xóa thực sự xóa resource (không còn truy cập được).
|
|
65
|
+
|
|
66
|
+
**Điểm verify:**
|
|
67
|
+
1. POST: HTTP 201, `body.id` tồn tại.
|
|
68
|
+
2. DELETE: HTTP 200 hoặc 204.
|
|
69
|
+
3. GET sau DELETE: HTTP 404.
|
|
70
|
+
|
|
71
|
+
**TC negative liên quan:**
|
|
72
|
+
- DELETE với ID không tồn tại → 404.
|
|
73
|
+
- DELETE resource đang được tham chiếu (foreign key) → 409/422 (nếu API có bảo vệ).
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Chuỗi 4 — List + Pagination
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
POST <resource> x N lần → tạo N bản ghi
|
|
81
|
+
→ GET <resource>?page=1&limit=<L> → trả đúng L bản ghi + pagination
|
|
82
|
+
→ GET <resource>?page=2&limit=<L> → trang tiếp theo đúng
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Khi dùng:** TC pagination — verify tổng bản ghi, số trang, dữ liệu mỗi trang.
|
|
86
|
+
|
|
87
|
+
**Điểm verify:**
|
|
88
|
+
1. `body.total` = N (tổng bản ghi đã tạo).
|
|
89
|
+
2. `body.data.length` = L (số bản ghi trên trang).
|
|
90
|
+
3. Trang 2: `body.data` không trùng trang 1.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Chuỗi 5 — Dependent resource (parent → child)
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
POST <parent> → HTTP 201, nhận parent_id
|
|
98
|
+
→ POST <child>?parentId=<parent_id> → HTTP 201, nhận child_id
|
|
99
|
+
→ GET <parent>/{parent_id} → body.children chứa child_id
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Khi dùng:** Resource con phụ thuộc resource cha (vd: buổi học phụ thuộc lớp học).
|
|
103
|
+
|
|
104
|
+
**Điểm verify:**
|
|
105
|
+
1. POST parent: `body.id` tồn tại.
|
|
106
|
+
2. POST child với `parentId`: HTTP 201.
|
|
107
|
+
3. GET parent: `body.children` (hoặc trường tương đương) chứa child đã tạo.
|
|
108
|
+
|
|
109
|
+
**TC negative liên quan:**
|
|
110
|
+
- POST child với `parentId` không tồn tại → 404.
|
|
111
|
+
- POST child với parent đã bị xóa/đóng → 422/409.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Ghi chú thiết kế TC
|
|
116
|
+
|
|
117
|
+
- **Cleanup:** Mỗi TC trong chuỗi CRUD cần xóa data đã tạo ở cuối (teardown).
|
|
118
|
+
Ghi rõ bước cleanup trong Test Steps: `**[Action]** Xóa resource id=<id> (cleanup)`.
|
|
119
|
+
- **ID dependency:** Khi TC sau cần ID từ TC trước, ghi rõ trong Preconditions:
|
|
120
|
+
`Đã có <resource> với id=<test_id> trong DB`.
|
|
121
|
+
- **Không nối TC thành chuỗi:** Mỗi TC vẫn độc lập; nếu cần data sẵn → tạo trong
|
|
122
|
+
Preconditions/fixture, không phụ thuộc TC khác đã chạy trước.
|