mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-17 13:41:02 +07:00
parent 460cd9c600
commit 65b72cf817
16 changed files with 414 additions and 131 deletions

58
tests/utils/user.test.ts Normal file
View file

@ -0,0 +1,58 @@
import { describe, expect, it } from "vitest";
import {
buildOacCaseCookie,
OAC_CASE_COOKIE,
parseOacCase,
} from "@/utils/user";
/** ตัวอย่าง cookie จริงจากระบบ e-Court (เก็บใน test เพื่อล็อก format ของจริง) */
const REAL_OAC_CASE =
"u_userid=235821&u_userlogin=tu_2&u_courtid=4&u_password=&u_AdminStatus=&u_perCardNo=5000000000006&u_perCardNoEncript=1800650010005002003&u_userType=4&u_CidAuthen=NNNENNNNNNNNNNNNNNNN&u_hSecId=NNNNNNNNNNNNNNNNNNNN&u_username=&u_courtName=&u_AuditID=1995227&u_PsGroupNum=1&u_PsGroup=11&u_PsGroupName=";
describe("parseOacCase", () => {
it("cookie e-Court จริง → identity จาก u_userid/u_userlogin", () => {
expect(parseOacCase(REAL_OAC_CASE)).toEqual({
userId: "235821",
userName: "tu_2",
});
});
it("ค่าจาก buildOacCaseCookie อ่านกลับได้ (round-trip ค่า mock login จริง user1)", () => {
expect(parseOacCase(buildOacCaseCookie("user1", "user1"))).toEqual({
userId: "user1",
userName: "user1",
});
});
it("ค่าเสีย/ว่าง/ขาด field → null (ถือว่ายังไม่เข้าสู่ระบบ)", () => {
expect(parseOacCase(null)).toBeNull();
expect(parseOacCase(undefined)).toBeNull();
expect(parseOacCase("")).toBeNull();
expect(parseOacCase("garbage")).toBeNull();
expect(parseOacCase("u_userlogin=user_x")).toBeNull(); // ขาด u_userid
expect(parseOacCase("u_userid=123&u_userlogin=")).toBeNull(); // u_userlogin ว่าง
});
it("ค่ารอบด้วย space ถูก trim", () => {
expect(parseOacCase("u_userid= 123 &u_userlogin= tu_2 ")).toEqual({
userId: "123",
userName: "tu_2",
});
});
it("ชื่อ cookie ตรงกับระบบ e-Court", () => {
expect(OAC_CASE_COOKIE).toBe("oacCase");
});
});
describe("buildOacCaseCookie", () => {
it("แทนเฉพาะ u_userid/u_userlogin — ฟิลด์อื่นคงค่าตัวอย่างจริงครบทุก field", () => {
const fields = Object.fromEntries(new URLSearchParams(buildOacCaseCookie("xxxxxx", "user_x")));
expect(fields["u_userid"]).toBe("xxxxxx");
expect(fields["u_userlogin"]).toBe("user_x");
expect(fields["u_courtid"]).toBe("4");
expect(fields["u_perCardNo"]).toBe("5000000000006");
expect(fields["u_AuditID"]).toBe("1995227");
expect(Object.keys(fields)).toHaveLength(16);
});
});