Test Checklist · BQ-Calibrated
~35 test case chia 7 nhóm (A → G). Mỗi TC có
expected_valuelấy trực tiếp từdata/calibration/2026Q1.json,reference_queryđể verify lại nếu cần, vàautomation(auto qua vitest / manual qua UI).Pre-condition:
npm run seedchạy thành công. Snapshot có:
- 6 warehouses, 12 customers, 589 calendar days
- ~16,779 historical orders (rng_seed=20260523 → deterministic)
- 162 config values
Nhóm A · Cấu hình tham số (5 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| A01 | Monthly factor 12 tháng đúng calibration | T1=0.95, T2=0.83, T11=1.55, T12=1.45 | calibration.monthly_factors_workload_adjusted | auto: forecast.bq-calibrated.test.ts |
| A02 | Event multiplier 6 loại đúng | NORMAL=1.0, MEGA=3.8, DOUBLE=2.0 | calibration.event_multipliers | auto: same |
| A03 | Sửa monthly:11 từ 1.55 → 1.60 | Tạo ConfigValue version=2, version=1 is_current=false, audit row | Supabase MCP configService.test.ts | manual UI |
| A04 | Effective_from < hôm nay | Reject với error "effective_from must be future" | configService validation | manual UI |
| A05 | Concurrent edit cùng key | Optimistic lock fail → toast "config đã thay đổi" | Supabase RLS | manual (2 tab) |
Nhóm B · Cấu hình định mức (4 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| B01 | Đủ tổ hợp UPH (DEFAULT + KEY override) | ≥ 130 row UPH_NORM | expected-config-snapshot.json#counts.uph_norms_min | auto: same |
| B02 | Lookup ưu tiên customer-specific → DEFAULT | C-Store COSMETIC UPH ≠ DEFAULT | wlu.bq-calibrated.test.ts | auto |
| B03 | Thay UPH 1 ô + re-forecast | wlu_per_order_minutes đổi inverse | computeWlu() | manual UI |
| B04 | Missing combo (PG=ELECTRONICS, no override) | Fallback DEFAULT_UPH=60, used_fallback=true | calculator.ts | auto |
Nhóm C · Forecast engine (6 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| C01 | Forecast NORMAL wh_vn_hcm_01 × c_cstore_vn ngày thường | qty ∈ [2500, 5500], confidence high | forecast-targets.json#T01 | auto |
| C02 | Forecast MEGA 11.11 wh_vn_hcm_03 × c_unicorn | qty ∈ [8k, 150k], low_confidence=true (sample=4) | targets#T03 | auto |
| C03 | Forecast Tết wh_vn_hn_01 × c_cstore_vn 2026-02-17 | qty ∈ [0, 800] | targets#T05 | auto |
| C04 | Forecast MID_MONTH wh_vn_hcm_01 × c_skinetiq | qty ∈ [800, 2500] | targets#T08 | auto |
| C05 | Customer thiếu history (TH × c_judydoll VN-only) | low_confidence=true, fallback | targets#T06 | auto |
| C06 | Fallback chain MEGA → DOUBLE_DAY → NORMAL | resolved_type updates correctly | aggregator.test.ts | auto (đã có) |
Nhóm D · Planner (6 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| D01 | Planner load wh_vn_hcm_01 ngày NORMAL | 3 shift (45/42/13%), total = Σ shifts | planner-scenarios.json#P01 | manual UI |
| D02 | Planner 11.11 MEGA | Spillover >30% sang ca sau + ngày sau | P07 | manual UI |
| D03 | Override headcount | ratio recompute | screens-hiring-override.jsx | manual UI |
| D04 | Switch warehouse | state reset, data load lại | router | manual UI |
| D05 | Date range picker 14 ngày | grid 14 cột × 3 ca | UI | manual UI |
| D06 | Hiring gap (workload/8h − current_headcount) | Hiển thị đỏ nếu gap > 0 | hiring-override | manual UI |
Nhóm E · KAM submit + Review pipeline (5 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| E01 | KAM submit override 11.11 c_cstore_vn | status NEW, audit INSERT | adjustmentService.test.ts | auto + manual |
| E02 | Ops mở review → SEEN | status SEEN, seen_at set | reviewService.test.ts | auto |
| E03 | Accept override | status ACCEPTED, forecast cập nhật theo submitted_qty | reviewService | auto |
| E04 | Reject override | status REJECTED, forecast giữ engine output | reviewService | auto |
| E05 | Audit row INSERT-only | UPDATE / DELETE bị Supabase reject | RLS policy | manual via Supabase MCP |
Nhóm F · Báo cáo & Hiring override (6 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| F01 | Báo cáo kho Q1 2026 wh_vn_hcm_01 | ~1.18M đơn ±5% | BQ Q1b row 3 | manual UI |
| F02 | Top customer leaderboard | C-Store #1, Skinetiq #2, Judydoll #3 | customer_mapping[].net_sale_q1 | manual UI |
| F03 | MoM/YoY tính từ history | YoY % hiển thị (KEY customers) | data-forecast.js#CUSTOMERS_EXT | manual UI |
| F04 | Hiring override theo kho | thêm/bớt số nhân sự theo kho | UI | manual UI |
| F05 | Export CSV báo cáo | header đầy đủ, ngày dạng ISO | xlsx lib | manual UI |
| F06 | Filter báo cáo theo channel | SHOPEE / LAZADA / TIKTOK / WEBSITE / B2B | Customer.channels | manual UI |
Nhóm G · History import & data intake (3 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| G01 | Import XLSX 1 ngày | upsert vào historical_orders, source='IMPORT_XLSX' | historicalImportService.test.ts | auto |
| G02 | Import API push (mock) | source='IMPORT_API', audit có request_id | historicalImportService | auto |
| G03 | Manual entry single row | source='MANUAL_ENTRY', audit before/after | service + audit | manual UI |
Nhóm tổng — Smoke tests (4 TC)
| ID | Test | Expected | Reference | Automation |
|---|---|---|---|---|
| Z01 | npm run seed không lỗi | exit 0, snapshot có 6 wh + 12 cust + 162 config | seed/index.ts | auto: shell |
| Z02 | npm run typecheck | 0 errors | tsconfig.app.json | auto: shell |
| Z03 | npm run test (toàn bộ) | 138+ tests pass | vitest | auto: shell |
| Z04 | npm run build | dist/ tạo OK, no errors | vite | auto: shell |
Cách diễn giải expected_value
Tất cả expected_value đều derive từ data/calibration/2026Q1.json. Quy ước:
qty ±15%= qty trong khoảng [0.85·expected, 1.15·expected]low_confidence=sample_size < 5 || fallback_used- Khi BQ data có thêm tháng / năm mới, chỉ cần re-run query + cập nhật
2026Q1.json→npm run seed. Test sẽ tự match khoảng mới (range trong fixture).
Khi test fail
- So sánh
forecast_qtythực tế với expected range trongforecast-targets.json— confirm có thực sự fail không (ranges generous ±15%). - Check
rng_seed=20260523không đổi (deterministic). - Chạy
git diff public/seed-snapshot.json— config_values count > 145? - Nếu sửa calibration, update
expected-config-snapshot.jsonđể match.