Hướng dẫn test — GIW POC Identity Platform
Bản: 21/09/2026 · Môi trường: local, localhost only
Ba phần, làm theo thứ tự: A. Khởi động → B. Test bằng tay trên giao diện (15 phút) → C. Test tự động (2 phút)
A. Khởi động
A1. Bật Docker
colima start
Bỏ qua nếu docker ps đã chạy được.
A2. Lên stack
cd "/Users/alexnguyen/data/Project/Galaxy Holding/Galaxy_ID/poc/identity"
docker compose up -d
Lần đầu sau khi sửa code sẽ lâu hơn (--build). Bình thường khoảng 30 giây.
A3. Kiểm tra 6 service
docker compose ps
Phải thấy đủ 6 dòng, postgres / keycloak / mock-hr-api / entitlement / session đều healthy:
giw-postgres Up (healthy)
giw-keycloak Up (healthy)
giw-mock-hr-api Up (healthy)
giw-entitlement Up (healthy)
giw-session Up (healthy)
giw-wifi-portal Up
Nếu giw-keycloak chưa healthy, đợi thêm 30 giây — nó khởi động chậm nhất.
A4. Mở portal
open http://localhost:3000
B. Test bằng tay — 8 kịch bản
Mỗi kịch bản: làm gì → phải thấy gì → điều đáng chú ý.
Mẹo: mở DevTools (
Cmd+Option+I) → tab Network trước khi bắt đầu. Xuyên suốt cả 8 bước, thanh địa chỉ không bao giờ rờilocalhost:3000và mọi request đều vềlocalhost:3000. Đó là điểm chính của kiến trúc API.
B1. Màn đầu
Làm: mở http://localhost:3000
Phải thấy:
- Header
VietjetAir.com │ SkyFi, nút ngôn ngữ góc phải - Dòng chuyến bay
VJ178 · SGN→HAN · Wi-Fi sẵn sàng - Ba nút: Đăng nhập Galaxy ID (đỏ đặc) · Đăng ký Galaxy ID (viền đỏ) · Đăng nhập CBNV (viền xám)
- Khung vàng: "Đây là bước xác thực danh tính..."
Chú ý: chưa có gì mở mạng. Xác thực và cấp quyền là hai việc khác nhau.
B2. Đăng ký tài khoản mới
Làm: bấm Đăng ký Galaxy ID, điền:
| Ô | Giá trị |
|---|---|
| Họ và tên | Nguyễn Văn Test |
test01@example.test (đổi số mỗi lần chạy lại) |
|
| Mật khẩu | Passw0rd!23 |
Bấm Tạo tài khoản và kết nối.
Phải thấy: nhảy thẳng sang màn Kết nối thành công — vòng tròn xanh, dấu tích.
| Trường | Giá trị |
|---|---|
| Loại người dùng | CUSTOMER |
| Gói quyền lợi | WIFI_CUSTOMER_BASIC |
| Hạng / QoS | BASIC · STANDARD |
| Số thiết bị | 1 |
| Nguồn quyền lợi | GALAXY_ID_LOGIN |
| Trạng thái phiên mạng | GRANTED |
Chú ý: một lần bấm chạy 4 việc — tạo tài khoản qua Admin API, đăng nhập, xin entitlement, mở session mạng. Hành khách chỉ có một câu hỏi: "tôi online chưa?"
B3. Đăng xuất và đăng nhập lại
Làm: kéo xuống cuối, bấm Đăng xuất. Rồi Đăng nhập Galaxy ID với chính email vừa tạo.
Phải thấy: về lại màn Kết nối thành công, vẫn BASIC.
Chú ý: đăng xuất thu hồi luôn phiên mạng chứ không chỉ xoá cookie. Kiểm chứng ở B8.
Tài khoản có sẵn nếu không muốn tự tạo:
| Mật khẩu | |
|---|---|
an.nguyen@example.test |
Passw0rd!23 |
binh.tran@example.test |
Passw0rd!23 |
chi.le@example.test |
Passw0rd!23 |
B4. Đăng nhập CBNV — thành công
Làm: Đăng xuất → Đăng nhập CBNV:
| Ô | Giá trị |
|---|---|
| Mã nhân viên | VJ12345 |
| Số CCCD | 001234567890 |
Phải thấy:
| Trường | Giá trị |
|---|---|
| Loại người dùng | EMPLOYEE |
| Gói quyền lợi | WIFI_EMPLOYEE_PACKAGE |
| Hạng / QoS | BOOST · HIGH |
| Số thiết bị | 2 |
| Nguồn quyền lợi | EMPLOYEE_VERIFICATION · EMP-12345 |
Kéo xuống khối Token claims. Đây là chỗ đáng soi nhất:
"user_type": "EMPLOYEE",
"employee_verified": true,
"employee_ref": "EMP-12345",
"company": "VIETJET"
Không có citizenId ở bất kỳ đâu. Bấm Cmd+F tìm 0012345 trên trang — không ra gì.
Chú ý: form do portal render, nhưng Keycloak mới là bên gọi HR. Portal không có credential HR, không tự verify được ai.
Nhân viên khác để thử:
| Mã | CCCD | Công ty |
|---|---|---|
GLX67890 |
001234500001 |
GALAXY |
SVC24680 |
001234500002 |
SOVICO |
B5. Bốn kiểu từ chối
Thử lần lượt, quay lại màn CBNV mỗi lần:
| # | Mã / CCCD | Phải thấy | Vì sao |
|---|---|---|---|
| 1 | VJ00000 / 001234567890 |
"Thông tin đăng nhập không đúng" | Không có nhân viên này |
| 2 | VJ12345 / 000000000000 |
Y hệt câu trên | Sai CCCD. Giống hệt để không ai dò được mã nào có thật |
| 3 | VJ99999 / 001234500003 |
"Hồ sơ nhân viên không ở trạng thái đang làm việc. Vui lòng liên hệ phòng nhân sự." | Đã nghỉ việc. Nói rõ được vì người này đã nhập đúng CCCD |
| 4 | VJ55555 / 001234500004 |
"Hồ sơ này không thuộc diện dùng Wi-Fi trên tàu bay" | Đang làm việc nhưng không thuộc diện |
Chú ý case 1 và 2: thông báo phải giống hệt nhau từng chữ. Khác nhau là lộ ra mã nhân viên nào có thật. Chú ý case 3: chỉ đến được sau khi CCCD đúng, nên nói thẳng lý do không lộ gì, mà lại đỡ cho người ta thử đi thử lại vô ích.
B6. ⭐ Kịch bản đáng xem nhất
Làm: Đăng nhập CBNV với PTX10001 / 001234500005
Phải thấy: "Xác minh thành công, nhưng đơn vị của bạn chưa thuộc diện dùng Wi-Fi trên tàu bay."
Chú ý: HR xác nhận người này là nhân viên thật, đang làm việc, đủ điều kiện. Xác thực thành công. Vẫn không được vào mạng — vì Entitlement Service là một quyết định riêng.
Đây là bằng chứng kiến trúc không phải con dấu cao su: bạn là ai và bạn được gì do hai hệ thống khác nhau trả lời.
B7. Bắt hệ thống hỏng
Làm: mở Terminal, cho HR treo:
curl -X POST localhost:3001/admin/fault \
-H 'Content-Type: application/json' \
-d '{"mode":"timeout","delayMs":4000}'
Rồi thử lại B4 (VJ12345 / 001234567890).
Phải thấy: "Hệ thống nhân sự phản hồi chậm. Vui lòng thử lại." (đợi ~7 giây)
Kiểm tra quan trọng: thử Đăng nhập Galaxy ID với an.nguyen@example.test — vẫn vào bình thường. HR sập chỉ ảnh hưởng luồng CBNV.
Thử tiếp kiểu sập hẳn:
curl -X POST localhost:3001/admin/fault \
-H 'Content-Type: application/json' -d '{"mode":"unavailable"}'
→ "Hệ thống nhân sự tạm thời không truy cập được."
Trả lại bình thường:
curl -X POST localhost:3001/admin/fault \
-H 'Content-Type: application/json' -d '{"mode":"none"}'
Chú ý: không nhánh nào cấp quyền tạm, không có đường vòng. Hỏng thì đóng.
B8. Truy vết một hành trình
Làm: đăng nhập CBNV thành công lần nữa, copy giá trị ở dòng Correlation (dạng corr-xxxx...), rồi:
CORR=corr-<dán-vào-đây>
for c in giw-wifi-portal giw-keycloak giw-mock-hr-api giw-entitlement giw-session; do
echo "── $c"; docker logs $c 2>&1 | grep "$CORR"
done
Phải thấy: cùng một mã chạy suốt 5 service — portal nhận, Keycloak xác thực, HR verify, Entitlement quyết định, Session mở mạng.
Chú ý: mã này in ngay trên màn hình cho hành khách. Gọi CSKH đọc 1 mã là tra được toàn bộ hành trình. Và nó không sinh ra từ CCCD hay dữ liệu cá nhân nào.
B9. Kiểm tra trình duyệt không cầm gì
Làm: DevTools → Application → Cookies → http://localhost:3000
Phải thấy: đúng một cookie tên sid, giá trị là UUID vô nghĩa, cờ HttpOnly ✓ và SameSite=Strict.
DevTools → Network, lọc theo Fetch/XHR, đăng nhập lại: mọi request đều về localhost:3000. Không có request nào tới localhost:8080.
Chú ý: trình duyệt không giữ token, không biết địa chỉ Keycloak, HR, Entitlement hay Session. Đó là ranh giới BFF.
C. Test tự động
C1. Bộ E2E — 59 test
cd "/Users/alexnguyen/data/Project/Galaxy Holding/Galaxy_ID/poc/identity"
node tests/e2e.mjs
Khoảng 30 giây. Kết quả mong đợi:
59 test · 59 pass · 0 fail
Chia theo nhóm:
| Nhóm | Số test | Kiểm gì |
|---|---|---|
| 1 · Service health | 2 | 6 service sống, portal ở chế độ api |
| 1b · Mode B | 5 | Đường browser OIDC (production candidate) chưa mục |
| 2 · Customer | 11 | Đăng nhập, refresh, logout, chống dò tài khoản |
| 3 · Registration | 4 | Đăng ký, trùng email, validate |
| 4 · Employee | 8 | Xác minh CBNV, claims, không có CCCD |
| 5 · Failure cases | 13 | 13 nhánh hỏng, không nhánh nào cấp quyền |
| 6 · API contract | 10 | Idempotency, enumeration, guardrail 422 |
| 7 · CCCD audit | 3 | Quét log, user record, event log |
C2. Bộ Bruno — 35 request
cd api/collection
npx @usebruno/cli run "00 Portal API" --env local
npx @usebruno/cli run "01 HR Verification" --env local \
--env-var hrApiKey=giw-poc-hr-key-change-me
npx @usebruno/cli run "03 Session" --env local \
--env-var sessionApiKey=giw-poc-session-key-change-me
Riêng nhóm Entitlement cần token thật:
cd "/Users/alexnguyen/data/Project/Galaxy Holding/Galaxy_ID/poc/identity"
TOKEN=$(node tests/token.mjs employee | tail -1)
cd api/collection
npx @usebruno/cli run "02 Entitlement" --env local --env-var accessToken="$TOKEN"
Tổng mong đợi: 35 request · 83 assertion · tất cả pass.
Muốn bấm tay thì cài app Bruno, mở thư mục api/collection, chọn environment local.
C3. Kiểm tra OpenAPI
cd api
npx @redocly/cli lint giw-poc
Mong đợi: Woohoo! Your API description is valid. 🎉 — 0 warning.
C4. Audit dữ liệu nhạy cảm
cd "/Users/alexnguyen/data/Project/Galaxy Holding/Galaxy_ID/poc/identity"
for c in giw-keycloak giw-mock-hr-api giw-entitlement giw-session giw-wifi-portal; do
printf "%-20s " "$c"
docker logs $c 2>&1 | grep -cE "001234567890|Passw0rd" | sed 's/^0$/sạch/'
done
Mọi dòng phải là sạch. Bất kỳ số nào khác 0 là lỗi nghiêm trọng.
D. Xử lý khi hỏng
| Triệu chứng | Làm gì |
|---|---|
dependency failed to start: giw-keycloak is unhealthy |
Đợi thêm 30 giây. Còn hỏng thì docker logs giw-keycloak | tail -30 |
Portal trả IDP_UNAVAILABLE |
docker compose ps xem Keycloak còn sống không |
| Đăng nhập báo sai mật khẩu dù gõ đúng | Sai quá nhiều lần → brute-force khoá 60 giây. Đợi rồi thử lại |
| Test fail sau khi sửa code | Dựng lại: docker compose up -d --build |
| Muốn sạch hoàn toàn | docker compose down -v && docker compose up -d --build — xoá DB, import lại realm, tạo lại user test |
| Cổng bị chiếm | lsof -ti tcp:8080 |
Bảng triệu chứng đầy đủ: RUNBOOK.md §6.
E. Dữ liệu test
Toàn bộ là dữ liệu tổng hợp. Không có người thật, hồ sơ nhân viên thật hay CCCD thật. Tên miền .test theo RFC 6761, vĩnh viễn không resolve được.
Khách hàng — mật khẩu Passw0rd!23
an.nguyen@example.test · binh.tran@example.test · chi.le@example.test
Nhân viên
| Mã | CCCD | Công ty | Trạng thái | Kết quả |
|---|---|---|---|---|
VJ12345 |
001234567890 |
VIETJET | ACTIVE | ✅ BOOST |
GLX67890 |
001234500001 |
GALAXY | ACTIVE | ✅ BOOST |
SVC24680 |
001234500002 |
SOVICO | ACTIVE | ✅ BOOST |
VJ99999 |
001234500003 |
VIETJET | INACTIVE | ❌ nghỉ việc |
VJ55555 |
001234500004 |
VIETJET | ACTIVE | ❌ không thuộc diện |
PTX10001 |
001234500005 |
PARTNER-X | ACTIVE | ❌ Entitlement chặn |
VJ00000 |
bất kỳ | — | — | ❌ không tồn tại |
F. Tắt
docker compose down # giữ database
docker compose down -v # xoá sạch, lần sau import lại realm
colima stop # tắt cả Docker VM