# Production beta — smoke test & env safety

Tài liệu cho bước **sau deploy** (hoặc staging giống production): xác nhận web, env và backend khớp nhau, không bị lệch route beta hay CORS/API base.

Xem thêm: [beta-public-deploy-checklist.md](./beta-public-deploy-checklist.md) (bật gating, route public).  
Script env nhẹ: `npm run check:beta-env` (trong thư mục `web/`).

---

## 1. Env bắt buộc — hosting **web** (Next)

| Biến | Ghi chú |
|------|---------|
| `NODE_ENV=production` | Do platform set khi chạy `next start` / adapter serverless. |
| `NEXT_PUBLIC_API_BASE_URL` | URL backend **HTTPS** production (không `/` cuối). **Không** dùng `localhost` / `127.0.0.1` trên môi trường mà browser người dùng truy cập. |
| `NEXT_PUBLIC_BETA_PUBLIC_SURFACE` | `1` nếu deploy **public beta surface** (gating bật). Embed lúc **build** — đổi biến này cần **build lại** image/artifact. |
| `BETA_PUBLIC_SURFACE` | (Khuyến nghị khi bật beta) Đặt `1` trên server — **proxy** Next đọc **runtime**; tránh trường hợp Edge trong `next dev` không khớp `NEXT_PUBLIC_*`. Nav client vẫn chỉ dựa vào `NEXT_PUBLIC_BETA_PUBLIC_SURFACE`. |
| `BETA_STAFF_BYPASS_SECRET` | Server-only; bật cookie staff. Chỉ **set** / **unset**, không log giá trị. |
| `SOFT_LAUNCH_ADMIN_SECRET` | (Tuỳ chọn) Trùng backend nếu dùng soft launch admin + cookie `ctkp_sl_adm`. |
| `NEXT_PUBLIC_SITE_URL` | (Khuyến nghị) Origin site public, ví dụ `https://www.example.com`. |

Chạy trước / sau deploy:

```bash
npm run check:beta-env
# Nghiêm hơn (ví dụ CI): mô phỏng production
set NODE_ENV=production
npm run check:beta-env
```

---

## 2. Env bắt buộc — **backend** (xác nhận, không sửa code ở bước này)

| Biến | Ghi chú |
|------|---------|
| `DATABASE_URL` | PostgreSQL production. |
| `NODE_ENV` | `production`. |
| `PORT` | Cổng lắng nghe (hoặc theo PaaS). |
| **JWT / auth** | Nếu phase hiện tại đã bật: `JWT_SECRET`, refresh secret, v.v. — theo `backend/.env.example` / tài liệu nội bộ. |
| **CORS** | Origin của **web production** (scheme + host, cổng nếu không phải 443/80) phải được phép gọi `fetch` từ browser. Nếu CORS sai: Network tab → request bị chặn (CORS error), status có thể 0. |
| **Soft launch** (nếu dùng) | `SOFT_LAUNCH_MODE`, secret trùng với web cho route telemetry/feedback. |

**CORS — dấu hiệu thường gặp**

- Console: `blocked by CORS policy`, `No 'Access-Control-Allow-Origin'`.
- API chạy nhưng trình duyệt không đọc được body (preflight `OPTIONS` 404/403).

**Việc cần làm:** trên backend, whitelist đúng origin web (ví dụ `https://cothukyph.example`), không dùng `*` nếu có cookie credentials (hiện tại nhiều API chỉ dùng Bearer/public GET — vẫn nên origin rõ ràng).

---

## 3. Thứ tự test sau deploy (thực tế)

1. **Health backend** (curl hoặc browser): endpoint public tối thiểu (ví dụ health hoặc `GET` bài viết nếu có).  
2. **Web trang chủ** `/` — không 5xx, có nội dung hero.  
3. **`npm run check:beta-env`** trên host build/deploy log (hoặc CI) với `NODE_ENV=production` nếu có thể.  
4. **API từ browser:** mở `/posts` — danh sách hoặc empty state **không** phải lỗi CORS.  
5. **Tử Vi:** `/tu-vi` → submit tối thiểu → `/tu-vi/ket-qua` (hoặc thông báo lỗi API rõ ràng, không im lặng).  
6. **Gieo quẻ:** `/fortune` và `/gieo-que` — một lần gieo thành công tới trang kết quả.  
7. **Beta gating (nếu `NEXT_PUBLIC_BETA_PUBLIC_SURFACE=1`):**  
   - Thử `/about` → redirect `/beta-coming-soon?from=...`.  
   - Staff login → thử lại `/about` → 200.

---

## 4. Route cần test — **public** (beta bật)

| Route | Kỳ vọng ngắn |
|-------|----------------|
| `/` | 200, CTA tới `/posts`, `/tu-vi`, `/gieo-que`, `/fortune` |
| `/posts`, `/posts/{slug}` | 200 hoặc empty hợp lệ |
| `/categories/{slug}` | 200 nếu có mục |
| `/gieo-que` | 200, gieo được |
| `/fortune`, `/fortune/result`, `/fortune/history` | 200 |
| `/tu-vi`, `/tu-vi/ket-qua`, `/tu-vi/da-luu` | 200 |
| `/beta-coming-soon` | 200 |

---

## 5. Route cần test — **hidden** (beta bật, **không** cookie staff)

Mỗi URL phải **redirect** tới `/beta-coming-soon` (hoặc ít nhất không render trang đích đầy đủ).

Ví dụ: `/about`, `/contact`, `/birth-time-reconstruction`, `/internal/soft-launch` (trừ `/internal/soft-launch/login`).

---

## 6. Route cần test — **sau staff bypass**

1. `/internal/beta-staff/login` — POST secret (qua form) → cookie.  
2. Lặp lại một route ẩn (`/about`, `/contact`, …) → **200**.  
3. (Tuỳ chọn) Soft launch: `/internal/soft-launch/login` → sau đó `/internal/soft-launch/summary`.

---

## 7. Lỗi thường gặp & cách nhận biết

| Hiện tượng | Nguyên nhân thường gặp |
|------------|-------------------------|
| Trang chủ không có bài / gieo lỗi | `NEXT_PUBLIC_API_BASE_URL` sai, thiếu, hoặc backend down. |
| Ảnh bài `/uploads/...` vỡ | API base hoặc proxy static uploads chưa public đúng domain. |
| Mọi `fetch` fail, console CORS | Backend chưa cho origin web; hoặc gọi nhầm `http` khi site là `https`. |
| Beta: route ẩn vẫn 200 cho khách | Build cũ không có `NEXT_PUBLIC_BETA_PUBLIC_SURFACE=1`; hoặc CDN cache HTML cũ. |
| Beta: mọi route đều “coming soon” | Biến beta bật nhầm trên staging muốn full nav — tắt hoặc dùng staff cookie. |
| Staff login 503 | Thiếu `BETA_STAFF_BYPASS_SECRET` trên server Next. |
| `NEXT_PUBLIC_API_BASE_URL=http://localhost:3000` trên prod | Browser user không gọi được máy `localhost` của server — **phải** URL public backend. Build sẽ in cảnh báo nếu phát hiện (xem `next.config.ts`). |

---

## 8. E2E / Playwright

### Vitest vs Playwright (beta)

- **Vitest** (`npm run test`): chỉ kiểm tra **logic thuần** (ví dụ `beta-middleware-logic`, `betaVisibility`) — **không** khởi chạy Next dev server, **không** xác nhận proxy Edge đọc env runtime.
- **Playwright beta** (`npm run test:e2e:beta`): khởi `next dev` qua `scripts/start-next-dev-beta-e2e.mjs` (cổng **3011**), bật `NEXT_PUBLIC_BETA_PUBLIC_SURFACE=1` + `BETA_PUBLIC_SURFACE=1` + secret staff **placeholder** cho E2E (override bằng `PLAYWRIGHT_BETA_STAFF_SECRET` trên CI). Script **xóa `web/.next/dev` trước khi chạy dev** để Turbopack không giữ chunk proxy cũ sau khi đổi vị trí file gate. Spec gửi header `x-ctkp-playwright-beta-e2e: 1` (loopback) để bật gate trong môi trường dev khi bundle không inline env đúng — **production không dùng header này**; user thật chỉ dựa env + cookie staff. Với `turbopack.root` trỏ `web/`, server dev **merge** biến từ **`web/.env.local`**. Đây là bước gần production nhất để xác nhận **`web/src/proxy.ts`** redirect đúng (không chỉ unit test).

### Troubleshooting local (dev / Playwright)

- **Gate runtime:** Next 16 dùng convention **`web/src/proxy.ts`** (cạnh `src/app`). **Không** dùng `web/proxy.ts` chỉ để re-export — có thể gây **timeout webServer** Playwright hoặc lỗi parse module.
- **Sau khi đổi file gate / từng thử proxy ở root:** xóa cache dev **`web/.next/dev`** rồi chạy lại `next dev` (script beta E2E xóa thư mục này trước khi spawn Next). Nếu vẫn lạ behavior, xóa cả **`web/.next`** thủ công rồi `next dev` / `next build` lại.
- **Windows — port dev bận / “Another next dev is already running”:** tìm PID (log Next hoặc `netstat`), rồi `taskkill /PID <pid> /F`.

### Hai bộ E2E (không trộn)

| Suite | Config | Khi nào |
|-------|--------|---------|
| **Beta tắt** (nav đầy đủ, có `/contact`) | `playwright.config.ts` | `npm run test:smoke` — **loại trừ** `beta-public-smoke.spec.ts` qua `testIgnore`. |
| **Beta bật** (`NEXT_PUBLIC_BETA_PUBLIC_SURFACE=1`) | `playwright.beta.config.ts` | `npm run test:e2e:beta` — port **3011** mặc định, webServer set `NEXT_PUBLIC_BETA_PUBLIC_SURFACE=1` + `BETA_PUBLIC_SURFACE=1` + `BETA_STAFF_BYPASS_SECRET`. |

### Automated beta smoke (`e2e/beta-public-smoke.spec.ts`)

- **Public (200, không dừng ở coming soon):** `/`, `/posts`, `/tu-vi`, `/gieo-que`, `/fortune`.  
- **Ẩn → redirect UI:** `/about`, `/contact`, `/birth-time-reconstruction` — URL cuối `/beta-coming-soon` với `from` khớp pathname (kỳ vọng giống production khi bật beta).  
- **`/internal/beta-staff/login`:** form có `data-testid` `beta-staff-secret-input`, `beta-staff-login-submit`.  
- **Staff bypass:** secret từ `PLAYWRIGHT_BETA_STAFF_SECRET` hoặc placeholder local (trùng script launcher) → mở `/about` không còn coming soon.

Smoke **thủ công** sau deploy vẫn nên quét thêm các route trong mục 4–6 (ví dụ `/fortune/history`, `/categories/...`, soft launch) — spec tự động giữ **tối thiểu** để CI nhanh và ổn định.

**Local:**

```bash
cd web
npm run test:e2e:beta
```

Tuỳ chọn: `PLAYWRIGHT_BETA_STAFF_SECRET` (CI), `PLAYWRIGHT_WEB_PORT_BETA`, `PLAYWRIGHT_BASE_URL_BETA`, `PW_REUSE_SERVER_BETA=1` để tái dùng dev server.

**CI:** job riêng chạy `npm run test:e2e:beta` (không gộp với `test:smoke` mặc định).

---

## 9. Lệnh chất lượng trước khi merge / deploy

```bash
cd web && npm run check:beta-env && npm run lint && npm run test && npm run build
cd web && npm run test:smoke
cd web && npm run test:e2e:beta
cd ../cms && npm run lint && npm run build
```
