eCourt-Doc-Edit/docs/acceptance-review.md
Suphonchai Phoonsawat 39d610a781 Phase 11: version PDF (FR-15)
- เลข version ใหม่: เว็บ-generate = v0 ฉบับตั้งต้น (ไม่มี PDF), add-in save (Start Versioning + Save & Version) = v1+ มี PDF เสมอ
- PDF ผลิตจาก Word จริงผ่าน task pane เท่านั้น (getFileAsync Pdf + runtime guard, manifest ไม่เพิ่ม requirement); capture/upload เป็น best-effort — docx ผ่านแม้ PDF ล้ม + MessageBar เตือนตาม hasPdf
- Backend: DocumentVersion.PdfObjectKey (nullable, migration AddVersionPdf) + sibling .pdf ใน bucket docx-versions · multipart part pdf (ผิด type → 400) · /download + /download-link รับ ?format=pdf (ไม่มี → 404) · presigned ฝังชื่อไฟล์ไทย + _vN (RFC 5987)
- Web: เปิดไฟล์แทน blob-download — PDF แท็บใหม่, docx ผ่าน ms-word:ofe|u| พร้อม heuristic fallback ลิงก์สำรอง (useVersionOpen, versionDownload) · ปุ่ม "เปิดใน Word" ที่หน้า success ของฟอร์มสร้างเอกสาร
- Spec v7 + CONTEXT.md + ADR 0001 + แผน docs/plans/version-pdf.md
- Automated QA: backend 68 / add-in 63 / web 55 ผ่าน · manual acceptance ผ่านบน Word for Mac (2026-09-08) รวม fidelity + track changes — ค้างเฉพาะ Windows / Word on the web

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-08 14:01:13 +07:00

19 KiB
Raw Blame History

ตรวจ Acceptance Criteria 1–18 (Phase 8 + 9 + 10 + 11)

วันที่ตรวจ: 2026-08-20 (AC 1–16) · 2026-08-22 (AC-17) · 2026-09-08 (AC-18) · อ้างอิง AC จาก specs/spec-officejs.md ส่วน 10

สัญลักษณ์: ✅ ผ่าน (มี automated test คุ้ม) · 🖐️ ผ่านจากการทดสอบด้วยมือ · 🔶 ผ่านบางส่วน/รอยืนยันด้วยมือบน platform ที่เหลือ · ❌ ไม่ผ่าน

สรุปรวม: AC ทั้ง 17 ข้อผ่านในระดับที่ตรวจอัตโนมัติได้ครบ (backend ครอบทุก flow ด้วย integration tests บน PostgreSQL + MinIO + Keycloak จริงผ่าน Testcontainers) — ส่วนที่เหลือคือการยืนยันด้วยมือบน browser/Word จริง (โดยเฉพาะ Word Online + Windows) ตามรายการท้ายเอกสาร

ผลทีละข้อ

AC-1 — Sideload บน Word Desktop และ Word Online ได้ทั้งคู่ ไม่มี error ตอนโหลด task pane — 🔶

  • 🖐️ Word for Mac (Desktop): ผ่าน — sideload ผ่าน npm start (manifest symlink) ตรวจตอน Phase 5 พร้อมแก้ปัญหาตกมุม 4 รอบ (dialog https / slice format / mixed content / blob download)
  • ✅ อัตโนมัติ: npm run build + npm run lint + npm run validate (manifest) ผ่านทุกครั้ง
  • 🔶 Word Online + Word for Windows: ยังไม่ได้ทดสอบ (ต้องมีเครื่อง/บัญชีทดสอบ)

AC-2 — เปิดครั้งแรกยังไม่ login → Sign in → dialog → login สำเร็จ dialog ปิดเอง ได้ token — ✅ (automated) + 🖐️ (Mac)

  • ✅ authService.test.ts (14 tests): PKCE pair format, state validation, token exchange, refresh, dialog message flow
  • 🖐️ ทดสอบจริงบน Word for Mac ผ่าน (Keycloak https:8443 + Office Dialog API)

AC-3 — เรียก backend โดยไม่แนบ token → 401 — ✅

  • AuthTests (xUnit + Keycloak จริง): ครบทุก endpoint ทั้ง GET/POST + กรณี token ขยะ + กรณี token จริงได้ 200

AC-4 — ไฟล์ใหม่ไม่เคย track → ปุ่ม "Start Versioning This Document" — ✅

  • metadataService.test.ts: ตรวจ SavedByAddIn เทียบ ADDIN_SIGNATURE_GUID → state untracked (App state machine)
  • 🖐️ ยืนยันแล้วบน Word for Mac ตอน Phase 5

AC-5 — Start Versioning → stamp metadata, สร้าง Document, v1, CreatedByUserId ตรง sub ใน JWT — ✅

  • UploadVersionTests.FirstUpload_CreatesDocumentAndVersionRow_FromJwtClaims: token จริงจาก Keycloak → ตรวจ row ใน Postgres ตรงๆ ว่า CreatedByUserId == sub + SHA256 + object key format ถูกต้อง

AC-6 — ปิดแล้วเปิดไฟล์เดิม → ตรวจพบ tracked + โหลด history ถูก — ✅

  • UploadVersionTests.SecondUpload_... + GetVersions ordering (ใหม่ → เก่า) + snapshot ต่อ version
  • 🖐️ ยืนยันบน Mac แล้ว (round 3 ของ Phase 5)

AC-7 — แก้ไขแล้ว Save & Version → version ใหม่ + LastVersionId/LastVersionTimestamp อัปเดต — ✅

  • versionFlow.test.ts (7 ขั้นตาม spec ครบ) + metadataService stamp tests + SecondUpload_IncrementsVersionNumber
  • หมายเหตุจาก Phase 5: LastVersionId ในไฟล์ = clientVersionId (ต้อง stamp ก่อน document.save())

AC-8 — ดาวน์โหลด version เก่าได้ไฟล์ที่เปิดได้ เนื้อหาตรงตอน upload — ✅

  • UploadVersionTests.Download_ReturnsExactUploadedBytes_WithDocxContentType: byte-for-byte ผ่าน MinIO ไป-กลับ + content-type ถูก
  • 🖐️ บน Mac ใช้ presigned link + openBrowserWindow (แก้จาก blob download ที่ WKWebView ทิ้งเงียบๆ) — ผ่านการตรวจ curl แล้ว ยืนยัน click จริงอีกครั้งพร้อม Word Online

AC-9 — token หมดอายุกลาง session → silent refresh ผู้ใช้ไม่รู้สึก — ✅

  • apiClient.test.ts: 401 → refresh → retry ครั้งเดียว → ยัง 401 → สัญญาณกลับหน้า sign-in · useAuth/useApi ของ web ใช้พฤติกรรมเดียวกัน
  • 🔶 รอยืนยันด้วยมือ (ปล่อย session ทิ้งไว้จริง)

AC-10 — XML Part fail silent บน Mac → ระบบยังทำงานถูก (backend = source of truth) — ✅

  • metadataService.test.ts: XML write fail → catch ไม่ throw · ทุก history อ่านจาก backend เสมอ

AC-11 — upload template → detect fields อัตโนมัติ + บันทึก definitions — ✅

  • TemplateTests: SDT ครบทุกรูปแบบ (run เดี่ยว / multi-run คนละ formatting / block / tag ซ้ำ → dedup) + defaults (label=tag, text, required=false) + PUT บันทึกนิยาม + reject key นอกไฟล์/type ผิด
  • 🖐️ หน้าเว็บอัปโหลด: ตรวจ SSR + จำลอง request sequence แล้ว (Phase 6) รอ click-through บน browser

AC-12 — กรอก form บนเว็บ → docx ค่าครบ formatting ไม่พัง → เปิดใน Word เห็น tracked + v1 ทันที — ✅ (backend) + 🔶 (เต็มรูปแบบรอมือ)

  • GenerateTests.Generate_FillsAllFields_...: ค่าครบทุก SDT + multi-run รวมเป็น run เดียวโดยรักษา bold ของ run แรก + custom properties (SavedByAddIn = GUID ตรงเป๊ะ, DocumentTrackingId = documentId) + Document/v1/field values ใน Postgres ครบ
  • 🖐️ ส่วน "เปิดใน Word แล้ว add-in เห็น v1 ทันที": ตรวจด้วย unzip/OpenXML แล้ว ต้องยืนยัน click จริงบน Word

AC-13 — แก้ค่าใน content control แล้ว Save & Version → field value ใน DB เปลี่ยน + snapshot ต่อ version — ✅

  • FieldSyncTests.UploadWithChangedFields_...: latest values อัปเดตเฉพาะ field ที่เปลี่ยน (SourceVersionId ชี้ v2) + snapshot v1/v2 เก็บค่าต่างกันตามเวลา (diff ได้)

AC-14 — ลบ content control แล้ว Save & Version → flow สำเร็จ ค่าเดิมคงอยู่ mark missing ไม่ error — ✅

  • FieldSyncTests.UploadWithMissingFields_...: request 201 · field ที่หายคงค่า v1 + SourceVersionId ไม่ขยับ · snapshot missing:true พร้อมค่าเดิม · field เกินนิยามถูก ignore ไม่เก็บ

AC-15 — หน้ารายการเอกสารของ template + detail (fields + history + snapshot ต่อ version + ดาวน์โหลด) + re-generate เป็น v1 โดยต้นทางไม่ถูกแตะ — ✅

  • GenerateTests.ListDocuments_ShowsVersionCounts_LatestFirst (versionCount/Latest + เรียงล่าสุดก่อน) + GetDetail_MergesDefsAndValues_BothOrphanWays (merge นิยาม × ค่า รองรับ orphan สองทาง) + GetDetail_OfStandaloneDocument
  • Re-generate: ผ่าน composition เดิม (detail → prefill → generate) — ตรวจ curl จริง Phase 6.4 ว่า versionCount ของเอกสารต้นทางคงเดิม + TemplateTests.Deactivate_... ยืนยัน policy deactivate
  • 🖐️ หน้าเว็บ: SSR ทุก route 200 แล้ว รอ click-through

AC-16 — (v5) พิมพ์คำอธิบาย (commit message) ตอนสร้าง version ได้ทุก flow → เก็บกับ version นั้น + แสดงใน history ทุกหน้า · เว้นว่างได้ — ✅ (backend + service) + 🔶 (dialog ใน Word รอมือ)

  • ✅ VersionMessageTests (backend): upload พร้อม message → ตรวจ row ใน Postgres + echo ใน history · blank/whitespace → null · trim · oversize 2049 → 400 ไม่มี side effect · ยาวพอดี 2048 ผ่าน · ต่อ version มี message ของตัวเอง · generate v1 พร้อม message · generate oversize → 400 ก่อนแตะ MinIO
  • ✅ versionFlow.test.ts (addin): saveAndVersion/startVersioning แนบ message ใน multipart · whitespace ล้วน → ไม่แนบฟิลด์
  • ✅ Web: VersionListItem.message + build ผ่าน (type-only ตาม convention ไม่มี component test)
  • 🔶 dialog + textarea ใน task pane (Save & Version / Start Versioning) และการแสดงใน VersionHistoryPanel ต้องยืนยันด้วยมือบน Word จริง

AC-17 — (v6) admin เลือก editor ต่อ field ได้ (text → textbox/textarea) → ฟอร์มสร้างเอกสารใช้ control ตาม editor · number/date ใช้ control ตาม type · editor ผิด → 400 · ไม่ส่ง → default — ✅ (backend + web unit) + 🔶 (browser รอมือ)

  • ✅ FieldEditorTests (backend): PUT editor=textarea → persist + echo · ไม่ส่ง → resolve default ตาม type + DB เก็บ null · ส่งค่า = default → normalize เป็น null · editor ไม่รู้จัก → 400 · ไม่เข้ากับ type (3 คู่) → 400 · tag ใหม่จาก replace ได้ default
  • ✅ fieldEditor.spec.ts (web): defaultEditor/resolveEditor/editorOptions/dateInputValue (prefill re-generate ของ field date จาก timestamp เต็ม)
  • 🔶 Click-through บน browser: admin editor select (desktop + mobile) · ฟอร์มสร้างเอกสาร render textarea/number/date ตาม editor

ชุด test ที่คุ้ม AC (สรุป)

ชุด จำนวน ครอบ
backend integration (Testcontainers: Postgres + MinIO + Keycloak จริง) 60 AC-3, 5, 6, 7 (backend), 8, 11–17
addin unit (Jest) 50 AC-2, 4, 7 (client flow), 9, 10, 16 (ส่วน form)
web unit (Vitest) 46 AC-11/12 (validation ฝั่งฟอร์ม), theme, snapshot rendering, AC-17 (ส่วน editor util)

ปัญหาที่เจอระหว่างไล่ AC (และการแก้)

  1. Keycloak ใน Testcontainers พร้อมช้ากว่า port เปิด — docker-proxy รับ connection ก่อนแล้วตัดสาย ("response ended prematurely") → fixture poll discovery endpoint จนได้ 200 จริง + แนบ container logs ตอน timeout
  2. WithResourceMapping ของ Testcontainers 4.14 รับ target เป็น directory — ชี้ path ไฟล์ตรงๆ ทำให้ realm ไม่ถูก import (discovery 404 ตลอด 2 นาที) → ชี้ /opt/keycloak/data/import แล้วไฟล์เข้าไปชื่อเดิม
  3. Keycloak 26 บังคับ HTTPS บน token endpoint ของ master realm แม้ใน container http → เลิกใช้ master admin API สร้าง test user ตามแผน ใช้ testuser ที่มากับ realm import + password grant แทน (backend ยัง validate JWKS/issuer/audience เต็มขั้น)
  4. template .docx ที่ test สร้างเองผิด schema — วาง run-level SDT เป็นลูกตรงของ Body (OOXML ห้าม; Word วางไว้ใน paragraph เสมอ) ตอน reload กลายเป็น SdtBlock + unknown elements ทั้งชุด ทำให้ generate เข้า branch ผิด → แก้ TestDocx ให้ถูก schema (service ฝั่ง backend ถูกอยู่แล้ว — ยืนยันด้วย template จริงตั้งแต่ Phase 3)
  5. import.meta.client ของ Nuxt ไม่ถูก replace ใน Vitest (vite-node ไม่ apply define กับ import.meta.*) → useTheme เปลี่ยน guard เป็น typeof window !== 'undefined' (semantics เทียบเท่าใน SSR/client)
  6. ยืนยัน behavior ผ่าน test (ไม่ใช่ bug): field sync update latest value ของทุก field ที่ "ส่งมา" แม้ค่าไม่เปลี่ยน (SourceVersionId ขยับทุกตัวที่ส่ง) — เฉพาะ field ที่ "ไม่ได้ส่ง" เท่านั้นที่คงค่า+source เดิม ตาม policy FR-11
  7. (ย้อนอดีต — จาก Phase 5) Office Dialog API ปฏิเสธ http, slice.data หลายรูปแบบ, WKWebView บล็อก mixed content + blob download — แก้ครบแล้ว มี regression coverage ใน unit tests

ที่เหลือ (ต้องทำด้วยมือ — ทำแทนไม่ได้)

  • Word Online: sideload + login + full flow (AC-1, 2, 4–8 บน Online)
  • Word for Windows (ถ้ามีเครื่อง)
  • ปล่อย session จริงจน token หมดอายุ → ยืนยัน silent refresh (AC-9)
  • Click-through บน browser: สร้าง/แก้ template → สร้างเอกสาร → ดาวน์โหลด → เปิดใน Word ยืนยัน add-in เห็น v1 ทันที (AC-11, 12) + ดาวน์โหลด version ผ่าน presigned link บน Mac อีกครั้ง (AC-8)
  • Theme toggle จำค่าหลัง reload บน browser จริง (Phase 6 DoD ที่เหลือ)
  • (v5) ใน Word จริง: textarea ใน dialog ของทั้ง Save & Version และ Start Versioning + ข้อความโชว์ใน VersionHistoryPanel (AC-16) · และบน browser: ฟอร์มสร้างเอกสารพร้อม message + แสดงใน history ทั้ง desktop/mobille
  • (v6) บน browser: หน้าแก้ template เลือก editor textarea (desktop + mobile) · ฟอร์มสร้างเอกสาร render textarea/ช่องตัวเลข/date picker ตาม editor · re-generate เอกสารที่ field date เก็บ timestamp เต็มแล้วช่อง date แสดงวันที่ถูกต้อง (AC-17)

AC-18 — Version PDF (v7)

ผล: ✅ ผ่าน — automated verification + 🖐️ manual acceptance บน Word for Mac (2026-09-08). AC เดิมที่กล่าว generate=v1 ให้อ่านเป็น v0 ตาม spec v7; Start Versioning ยังคงเริ่ม v1.

เกณฑ์ หลักฐาน
generate v0 ไม่มี PDF → upload v1 มี PDF VersionPdfTests.Generate_V0WithoutPdf_ThenUpload_V1WithPdf; migration AddVersionPdf ผ่าน PostgreSQL จริง
PDF sibling key, hasPdf, bytes/type/ชื่อไทย, presigned signature Upload_WithPdf_PersistsSiblingKey_AndDownloadsExactBytes อ่าน proxy และ presigned URL จาก MinIO จริงทั้ง PDF/docx
ไม่มี PDF → false + 404 ทั้งสอง download endpoint Upload_WithoutPdf_HasFalse_AndBothPdfDownloadsReturn404
นามสกุล/type ผิดหรือว่าง → 400 ก่อนสร้าง document InvalidPdf_Returns400_BeforeCreatingDocument (3 cases)
PDF storage ล้ม → 201/false; docx storage ล้ม → 502/rollback StorageFailure_PdfIsBestEffort_DocxStillRollsBack (2 cases; fault injection + PostgreSQL จริง)
ทั้งสอง add-in flows capture docx → PDF, fail/empty ไม่บล็อก upload versionFlow.test.ts FR-15 matrix: start/save × success/failure/empty/storage-failure
PDF runtime guard, normalize slices, ปิด File object fileService.test.ts PDF cases; manifest ไม่เพิ่ม requirement
multipart PDF + format query versioningService.test.ts
URL ms-word คง signed URL, v0 filename, PDF path versionDownload.spec.ts
PDF popup/fallback/reset/error และ Word timeout cleanup useVersionOpen.spec.ts 6 lifecycle tests

ชุดเต็ม: backend 68 ผ่าน, add-in 63 ผ่าน + build/lint ผ่าน, web 55 ผ่าน + production build ผ่าน. Add-in build มี webpack bundle-size warnings; ไม่มี compile/lint errors.

ยืนยันด้วยมือ — ผ่านบน Word for Mac (2026-09-08)

  • Mac: เว็บ v0 → เปิดใน Word → Save & Version → v1 + PDF ไม่มี warning, ปุ่ม PDF เฉพาะ v1 · Start Versioning → v1 + PDF ทันที
  • เทียบ PDF กับ Word Save As PDF: ฟอนต์ไทย TH Sarabun New render + embed, หัวท้ายกระดาษ — ตรง
  • Track Changes เปิดทิ้งไว้ → Save & Version → PDF ไม่มี markup ปน — ไม่มีข้อจำกัดต้องบันทึกเพิ่มใน spec
  • Warning path เมื่อ PDF capture ล้ม; ปุ่ม PDF ขึ้นเฉพาะ version ที่มี
  • เว็บ: docx เปิด Word ผ่าน ms-word พร้อม fallback ลิงก์สำรอง, PDF เปิดแท็บใหม่, ชื่อไฟล์สวยทั้งสองกรณี
  • ค้างตามเดิม (ต้องมีเครื่อง/บัญชีทดสอบ): Word for Windows + Word on the web — sideload + full flow; เป็นข้อค้างเดิมตั้งแต่ AC-1 ไม่ใช่ของใหม่ใน AC-18

รายการเต็ม: แผน FR-15. ไม่มีการอ้างว่า unit tests ยืนยัน layout/Word protocol handler ได้.

QA reviewer: PASS, 2 rounds, 3 findings fixed (build compatibility, pending PDF tab cleanup, PDF success copy); 2026-09-08. Manual acceptance ผ่านบน Word for Mac 2026-09-08 — Phase 11 ปิดแล้ว; ค้างเฉพาะ Word for Windows / Word on the web ตามรายการด้านบน.