Buổi 9: Test Gate Assembly - 5 Layers đội hình
![]()
I. Lý Thuyết (30 phút)
5 Phases of Test Gate Protocol
┌─────────────────────────────────────────────────┐
│ PHASE 1: Stack Detection (Node.js+Express+SQLite) │
│ Check: package.json, src/, test/ │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ PHASE 2: 4 Core Test Files │
│ ✓ Frontend Safety (jest) │
│ ✓ API Routes (supertest) │
│ ✓ Business Logic (Vitest) │
│ ✓ i18n Sync (JSON parity) │
│ ✓ Security Scan (secret patterns) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ PHASE 3: Script Wiring │
│ npm run test:gate = all 5 test files in order │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ PHASE 4: Secret Hygiene │
│ - No hardcoded secrets │
│ - .env in .gitignore │
│ - process.env for sensitive data │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ PHASE 5: Verification │
│ Gate Decision: PASS → Deploy, FAIL → Stop │
└─────────────────────────────────────────────────┘cm-test-gate: 5 Phases Chi Tiết
Phase 1: Stack Detection
- Xác định công nghệ: Node.js, Express, SQLite, Vitest
- Check
package.jsoncó đủ devDependencies:vitest,supertest,@testing-library/react
Phase 2: 4 Core Test Files
test/
├── frontend-safety.test.ts (Layer 1: DOM-XSS, input validation)
├── api-routes.test.ts (Layer 2: endpoints, status codes)
├── business-logic.test.ts (Layer 3: logic, edge cases)
├── i18n-sync.test.ts (Layer 4: key parity)
└── security-scan.test.ts (Layer 5: hardcoded secrets)Phase 3: Script Wiring
{
"scripts": {
"test:gate": "vitest run test/frontend-safety.test.ts test/api-routes.test.ts test/business-logic.test.ts test/i18n-sync.test.ts test/security-scan.test.ts"
}
}Phase 4: Secret Hygiene
- Scan src/ cho
/API_KEY\s*=\s*['"].*['"]/ - Verify .gitignore có
.env,.dev.vars - Verify no
process.env.SECRETđược print hoặc log
Phase 5: Verification
- All 5 test files PASS?
- Zero failures?
- Evidence log created?
3 Iron Laws of cm-quality-gate
Law 1: NO DEPLOY without test:gate
if (test:gate !== PASS) {
STOP
throw new Error("Gate failed - do not deploy")
}Law 2: NO CLAIMS without evidence
Claim: "This feature is ready"
Evidence:
- test:gate PASS ✓
- All 5 layers verified ✓
- Timestamp & git commit ✓Law 3: NO FRAGILE FRONTEND
BAD: Inline style="color: red" + hardcoded user data
GOOD: Sanitized HTML + environment-driven stylingAnti-Patterns vs. Best Practices
| ❌ DON'T | ✅ DO |
|---|---|
| Deploy, then test | Test, then deploy |
| "Tests passed yesterday" | Run fresh NOW, every time |
| Skip tests for "small changes" | Every change tested |
| Run test + deploy in parallel | Sequential: test → verify → deploy |
| Check code quality later | Scan at commit time (pre-commit hook) |
| Trust previous env setup | Verify env vars before deploy gate |
Gate Function: IDENTIFY → RUN → READ → VERIFY → CLAIM
1. IDENTIFY
↓ What are we testing?
├─ Stack type?
├─ Test files present?
└─ Test scripts wired?
2. RUN
↓ Execute all tests
├─ npm run test:gate
└─ Capture exit code + output
3. READ
↓ Parse results
├─ Count failures
├─ Extract error messages
└─ Check for warnings
4. VERIFY
↓ Validate evidence
├─ All 5 layers covered?
├─ No skipped tests?
└─ Coverage adequate?
5. CLAIM
↓ Make gate decision
├─ PASS: "Safe to deploy"
└─ FAIL: "Stop, fix issues"II. Thực Hành: Case Study Thực Tế với TaskFlow (55 phút)
Trong phần này, chúng ta sẽ xem xét kết quả thực tế khi áp dụng 5-Layer Test Gate vào dự án taskflow-app đi kèm khóa học. Dự án này đã được "cài cắm" sẵn một số bug (planted bugs) để kiểm chứng khả năng bắt lỗi của Test Gate.
CHECKPOINT: Verify 5 Test Files Exist
Bước 1: Liệt kê test files trong TaskFlow app
cd courses/qa/taskflow-app
ls -la test/Thực tế dự án:
-rw-r--r-- frontend-safety.test.js # Layer 1: JS syntax, HTML structure
-rw-r--r-- api-routes.test.js # Layer 2: CRUD endpoints
-rw-r--r-- business-logic.test.js # Layer 3: Task model logic
-rw-r--r-- i18n-sync.test.js # Layer 4: vi.json vs en.json
-rw-r--r-- security-scan.test.js # Layer 5: Secret hygieneBước 2: Kiểm Tra package.json có test:gate script
grep "test:gate" package.jsonThực tế cấu hình:
"scripts": {
"test:gate": "vitest run --reporter=verbose",
"test:gate:frontend": "vitest run test/frontend-safety.test.js --reporter=verbose",
"test:gate:api": "vitest run test/api-routes.test.js --reporter=verbose",
"test:gate:logic": "vitest run test/business-logic.test.js --reporter=verbose",
"test:gate:i18n": "vitest run test/i18n-sync.test.js --reporter=verbose",
"test:gate:security": "vitest run test/security-scan.test.js --reporter=verbose"
}Bước 3: Chạy test:gate - Khoảnh khắc "Sự thật"
Chạy toàn bộ 5 layer trên dự án thực tế:
npm run test:gateKết quả thực tế cực kỳ giá trị: Test Gate chạy qua 54 tests trong vỏn vẹn 1.24s và phát hiện thành công 4 lỗi (FAIL) được cài cắm:
✅ Layer 2: API Routes .............. 10/10 passed
✅ Layer 3: Business Logic .......... 18/18 passed
✅ Layer 5: Security Scan ........... 7/7 passed (⚠️ Phát hiện Canary warning BUG #5)
❌ Layer 1: Frontend Safety ........ 3/4 passed (BẮT ĐƯỢC BUG #4)
FAIL: no single-quote wrapping template literals with t() calls
=> Nguyên nhân: app.js dùng '${t(...)}' thay vì `${t(...)}`
❌ Layer 4: i18n Sync .............. 2/6 passed (BẮT ĐƯỢC BUG #3)
FAIL: all language files have identical key counts
=> Nguyên nhân: en.json (13 keys) !== vi.json (11 keys)
FAIL: all language files have identical key structure
=> Nguyên nhân: Extra keys in en.json not in vi.json: task.filter.all, task.status.overdueBài học rút ra: Thay vì phải test tay bằng giao diện (tốn hàng giờ) hoặc đợi khách hàng báo lỗi, Test Gate đã chặn đứng các lỗi về UI (Layer 1) và thiếu hụt ngôn ngữ (Layer 4) ngay tại local machine chỉ trong vòng 1 giây!
Practice 2: Gate Decision Discipline (Kỷ luật Deploy)
Scenario: Như kết quả thực tế ở trên, chúng ta đang có 4 lỗi (Exit Code: 1). Quyết định tại Gate là gì?
Bước 1: Phân tích Output và Đưa ra Quyết Định
Khi lệnh npm run test:gate trả về Exit Code: 1, CI/CD Pipeline (GitHub Actions) sẽ tự động bị đánh rớt (FAILED).
┌─────────────────────────────────────────┐
│ 🛑 GATE FAILED - DO NOT PROCEED │
│ │
│ Exit code: 1 │
│ Tests failed: 4 out of 54 │
│ Status: BLOCKED - DO NOT DEPLOY │
└─────────────────────────────────────────┘Bước 2: Canary Tests - Kỹ thuật ghi nhận nợ kỹ thuật (Technical Debt)
Nhìn vào kết quả Layer 5, bạn sẽ thấy một dòng warning thú vị: ⚠️ Secret canary: src/server.js: API key
Canary test là gì? Trong môi trường học tập (hoặc khi refactor dự án legacy), đôi khi có những bug ta biết là nó đang tồn tại (như Bug #5: Hardcoded secret trong server.js), nhưng chưa thể sửa ngay. Thay vì để test đánh rớt (FAIL) liên tục gây chặn luồng deploy của các tính năng khác, chúng ta viết test dạng canary (chim hoàng yến): nó cảnh báo (WARN) nhưng không ném ra Exception. Khi học viên tiến đến buổi học sửa lỗi Security, test này sẽ được chuyển thành chế độ khắt khe (Strict mode) và chuyển sang FAIL.
Bước 3: Fix lỗi và Vượt qua Gate (The Green Build)
Nhiệm vụ của các bạn trong các buổi tiếp theo là lần lượt sửa các Planted Bugs này. Sau khi sửa xong:
npm run test:gateMục tiêu (Expected):
Test Files 5 passed (5)
Tests 54 passed (54)
Duration 1.24s
✅ All gates passed. Ready for deployment.Practice 3: Evidence Before Claims
Scenario: Tạo evidence log để prove claims.
Bước 1: Tạo evidence template
File: docs/evidence-log.md
# Test Gate Evidence Log
## Buổi 9: April 24, 2026 - 15:30 UTC
### Gate Status: PASS ✅
#### Test Execution
- **Date & Time**: 2026-04-24 15:30:42 UTC
- **Git Commit**: `abc1234` (Test Gate Assembly: All 5 layers verified)
- **Branch**: main
- **Node Version**: v20.10.0
- **npm Version**: 10.2.3
#### Test Results
| Layer | Test File | Status | Pass | Fail | Duration |
|-------|-----------|--------|------|------|----------|
| 1 | frontend-safety.test.js | ❌ | 3 | 1 | 234ms |
| 2 | api-routes.test.js | ✅ | 10 | 0 | 456ms |
| 3 | business-logic.test.js | ✅ | 18 | 0 | 345ms |
| 4 | i18n-sync.test.js | ❌ | 2 | 4 | 125ms |
| 5 | security-scan.test.js | ✅ | 7 | 0 | 98ms |
| **TOTAL** | | **❌** | **40** | **5** | **1.24s** |
#### Test Output
\`\`\`
✓ test/frontend-safety.test.ts (234ms)
✓ test/api-routes.test.ts (456ms)
✓ test/business-logic.test.ts (345ms)
✓ test/i18n-sync.test.ts (125ms)
✓ test/security-scan.test.ts (98ms)
Test Files 5 passed (5)
Tests 12 passed (12)
\`\`\`
#### Security Checklist
- [x] No hardcoded secrets in src/
- [x] .env in .gitignore
- [x] process.env for API_KEY
- [x] i18n keys in parity
#### QA Sign-off
**Tested By**: DevOps Bot
**Approved By**: Lead Engineer
**Timestamp**: 2026-04-24T15:30:42Z
**Ticket**: TASKFLOW-42
---
## Claims Require Evidence Table
| Claim | Evidence | Source |
|-------|----------|--------|
| "Tests passed" | test:gate PASS, Exit code 0 | npm run test:gate |
| "No security issues" | security-scan.test.ts PASS | test/security-scan.test.ts |
| "i18n sync" | i18n-sync.test.ts PASS, parity verified | test/i18n-sync.test.ts |
| "Ready to deploy" | All 5 layers PASS + evidence log | This file |
---
## Previous Buổis Evidence
See also:
- Buổi 8: `docs/evidence-log.md` (i18n & security fixes)
- Buổi 7: `docs/evidence-log.md` (business logic layer)Bước 2: Chạy test:gate và capture output
npm run test:gate > test-output.txt 2>&1
EXIT_CODE=$?
# Append to evidence log
cat >> docs/evidence-log.md << EOF
## Buổi 9: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
### Gate Status: $([ $EXIT_CODE -eq 0 ] && echo "PASS ✅" || echo "FAIL ❌")
\`\`\`
$(cat test-output.txt)
\`\`\`
EOF
cat docs/evidence-log.mdBước 3: Kiểm Tra evidence log
cat docs/evidence-log.mdExpected:
# Test Gate Evidence Log
## Buổi 9: 2026-04-24 15:30:42 UTC
### Gate Status: PASS ✅
Test Files 5 passed (5)
Tests 54 passed (54)
Claims Require Evidence Table
| Claim | Evidence | Source |
|-------|----------|--------|
| "Ready to deploy" | All 5 layers PASS | test:gate output |III. CHECKPOINT: 5 Layers Running with 0 Failures
Verify trước khi kết thúc buổi:
# 1. Check 5 test files
ls -1 test/*test.ts | wc -l
# Expected: 5
# 2. Check test:gate script
grep -c "test:gate" package.json
# Expected: 1
# 3. Run gate
npm run test:gate
# Expected: Exit code 0, all PASS
# 4. Check evidence log
[ -f docs/evidence-log.md ] && echo "✅ Log exists" || echo "❌ Missing"
# 5. Security check
grep -c "FAKE_API_KEY" src/server.js
# Expected: 0 (hardcoded key removed)IV. Script: Automated Gate Check
File: scripts/pre-deploy.sh
#!/bin/bash
set -e
echo "🏗️ Test Gate Assembly - Pre-Deployment Check"
echo "============================================="
# Phase 1: Stack Detection
echo "[1/5] Stack Detection..."
[ -f "package.json" ] || { echo "❌ No package.json"; exit 1; }
[ -d "test" ] || { echo "❌ No test/ directory"; exit 1; }
echo "✅ Stack detected: Node.js + Vitest"
# Phase 2: Core Test Files
echo "[2/5] Core Test Files..."
TEST_COUNT=$(find test -name "*.test.ts" | wc -l)
if [ "$TEST_COUNT" -ge 5 ]; then
echo "✅ Found $TEST_COUNT test files"
else
echo "❌ Only $TEST_COUNT test files (need 5)"
exit 1
fi
# Phase 3: Script Wiring
echo "[3/5] Script Wiring..."
grep -q "test:gate" package.json || { echo "❌ No test:gate script"; exit 1; }
echo "✅ test:gate script found"
# Phase 4: Secret Hygiene
echo "[4/5] Secret Hygiene..."
SECRETS=$(grep -r "API_KEY\s*=\s*['\"].*['\"]" src/ || true)
if [ -z "$SECRETS" ]; then
echo "✅ No hardcoded secrets"
else
echo "❌ Found hardcoded secrets:"
echo "$SECRETS"
exit 1
fi
# Phase 5: Run Gate
echo "[5/5] Running Test Gate..."
npm run test:gate || { echo "❌ Test gate failed"; exit 1; }
echo ""
echo "✅ ALL GATES PASSED - Ready to deploy"
echo "$(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> docs/deployment-log.txt
exit 0Chạy:
chmod +x scripts/pre-deploy.sh
./scripts/pre-deploy.shV. Key Takeaways
5 Phases recap:
- Stack Detection - Verify tech stack
- 4 Core Test Files - Frontend, API, Logic, i18n, Security
- Script Wiring - npm run test:gate orchestrates all
- Secret Hygiene - No hardcodes, use process.env
- Verification - Evidence log, gate decision
3 Iron Laws:
- NO DEPLOY without test:gate → Exit code 0 required
- NO CLAIMS without evidence → Evidence log mandatory
- NO FRAGILE FRONTEND → Input validation + sanitization
Gate Decision:
if (npm run test:gate === 0) {
DEPLOY ✅
} else {
STOP ❌ Fix issues first
}VI. Homework
- Ensure
npm run test:gatepasses on your local machine - Create
docs/evidence-log.mdwith current gate status - Run
scripts/pre-deploy.shand capture output - Commit evidence to git:bash
git add docs/evidence-log.md git commit -m "Test Gate Assembly: All 5 layers PASS" git push - Share evidence link in team Slack
Next Buổi: Real-world deployment scenarios & incident post-mortems.