User report: downloaded/opened files named just "file" — v9.2 put a
literal file.docx/file.pdf at the end of open-link URLs (treated as
cosmetic since Content-Disposition carries the real name, but Word
names its URL-copy from the URL path and browsers fall back to the
URL filename)
- OpenLinkService.CreateLink takes fileName and appends it (URL-encoded,
Thai-safe) — version: {base}_v{N}.docx/.pdf per FR-15, draft:
{template}_{timestamp}.docx per FR-10 naming policy
- Draft route split into /d/{templateId}/{stamp}/{fileName} (rebuilds
objectKey + HMAC-validates as before); version route unchanged shape
with real name
- Tests: URL tails assert real names (คำร้อง_v1.pdf/_v1.docx, draft
template name), no /file.docx; new tamper tests — modified stamp/
templateId and swapped docx↔pdf extension (version route) → 404
- backend 89/89; QA reviewer PASS (1 round, minors 2 fixed)
Co-Authored-By: Claude Code <noreply@anthropic.com>
34 KiB
ตรวจ Acceptance Criteria 1–19 (Phase 8 + 9 + 10 + 11 + 12)
วันที่ตรวจ: 2026-08-20 (AC 1–16) · 2026-08-22 (AC-17) · 2026-09-08 (AC-18) · 2026-09-10 (AC-19) · อ้างอิง 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 → stateuntracked(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_...+GetVersionsordering (ใหม่ → เก่า) + 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ไม่ขยับ · snapshotmissing: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 (และการแก้)
- Keycloak ใน Testcontainers พร้อมช้ากว่า port เปิด — docker-proxy รับ connection ก่อนแล้วตัดสาย ("response ended prematurely") → fixture poll discovery endpoint จนได้ 200 จริง + แนบ container logs ตอน timeout
WithResourceMappingของ Testcontainers 4.14 รับ target เป็น directory — ชี้ path ไฟล์ตรงๆ ทำให้ realm ไม่ถูก import (discovery 404 ตลอด 2 นาที) → ชี้/opt/keycloak/data/importแล้วไฟล์เข้าไปชื่อเดิม- Keycloak 26 บังคับ HTTPS บน token endpoint ของ master realm แม้ใน container http → เลิกใช้ master admin API สร้าง test user ตามแผน ใช้
testuserที่มากับ realm import + password grant แทน (backend ยัง validate JWKS/issuer/audience เต็มขั้น) - template .docx ที่ test สร้างเองผิด schema — วาง run-level SDT เป็นลูกตรงของ Body (OOXML ห้าม; Word วางไว้ใน paragraph เสมอ) ตอน reload กลายเป็น SdtBlock + unknown elements ทั้งชุด ทำให้ generate เข้า branch ผิด → แก้
TestDocxให้ถูก schema (service ฝั่ง backend ถูกอยู่แล้ว — ยืนยันด้วย template จริงตั้งแต่ Phase 3) import.meta.clientของ Nuxt ไม่ถูก replace ใน Vitest (vite-node ไม่ apply define กับ import.meta.*) →useThemeเปลี่ยน guard เป็นtypeof window !== 'undefined'(semantics เทียบเท่าใน SSR/client)- ยืนยัน behavior ผ่าน test (ไม่ใช่ bug): field sync update latest value ของทุก field ที่ "ส่งมา" แม้ค่าไม่เปลี่ยน (
SourceVersionIdขยับทุกตัวที่ส่ง) — เฉพาะ field ที่ "ไม่ได้ส่ง" เท่านั้นที่คงค่า+source เดิม ตาม policy FR-11 - (ย้อนอดีต — จาก 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 ตามรายการด้านบน.
AC-19 — ไฟล์ร่าง (v8 · ปรับ v9)
ผล: 🔶 ผ่านด้วย automated tests — ค้างยืนยันด้วยมือบน Word จริง (ตาม pattern เดิม: unit/integration tests ยืนยัน contract และข้อมูลใน DB ได้ แต่ไม่ยืนยันพฤติกรรม add-in/การเรนเดอร์ Word จริงได้)
| เกณฑ์ | หลักฐาน |
|---|---|
| สร้างไฟล์ร่างแล้วไม่เกิด Document/version/field values ใน DB + ค่าครบ + formatting รักษาไว้ + presigned URL ดึงไฟล์จริงได้ทันที (v9) | DraftTests.Draft_FillsAllFields_PreservesFormatting_StampsOnlyTemplateId_CreatesNothing |
stamp เฉพาะ TemplateId — ไม่มี SavedByAddIn/DocumentTrackingId |
test เดียวกัน (อ่าน custom properties จากไฟล์จริง) |
| ค่าที่แทนที่ได้ complex script ของ run ข้อความไทย (w:hint="cs"+w:cs) · SDT เปล่าสืบทอด sdtPr rPr (v9 — แก้การตัดคำไทย) | DraftTests.Draft_ThaiPlaceholder_UsesComplexScriptRun_ForWordBreaking (template จำลองโครงสร้าง T14) |
| ค่าที่มีอักขระควบคุมที่ XML ห้าม (เช่น 0x0B จากการ paste) ถูกตัดออก สร้างไฟล์สำเร็จ ไม่กลายเป็น 500 (v9 — bug จริงจากการใช้งาน) | DraftTests.Draft_ValueWithInvalidXmlControlChars_IsStrippedNot500 (ครอบทั้ง draft และ generate v0) |
open-link ของ backend (v9.2): ไม่มี query string + จบ .docx + [AllowAnonymous] ด้วย HMAC + token ผิด → 404 |
DraftTests.Draft_OpenLink_BadToken_Returns404_AndNoBearerNeeded + VersionPdfTests (inline/attachment + ชื่อไทยผ่าน /api/open) |
| validate เหมือน FR-10: required ขาด/type ผิด → 400, unknown → 404, deactivate → 400, ค่าเกินนิยาม ignore | DraftTests ที่เหลือ (5 tests) |
| Start Versioning ของไฟล์ร่าง: upload + templateId → v1 + Document ผูก template + field sync ครบ + snapshot แบบมีนิยาม | TemplateLinkingTests.Upload_WithTemplateId_OnNewDocument_CreatesLinkedDocV1WithFieldSync |
| upload ถัดไปไม่ส่ง templateId → v2, TemplateId คงเดิม | test เดียวกัน (upload รอบสอง) |
| templateId ไม่ใช่ GUID / ไม่พบ → 400 (แม้ document มีอยู่แล้ว) | Upload_TemplateIdNotGuid_Returns400, Upload_TemplateIdUnknown_Returns400, Upload_ExistingDocument_BadTemplateId_Returns400 |
| template deactivate หลังสร้างไฟล์ร่าง → ยังผูกได้ | Upload_TemplateIdDeactivated_StillLinks |
| document เดิม (standalone) ส่ง templateId มา → เฉย | Upload_ExistingDocument_IgnoresTemplateId |
| endpoint ใหม่ต้อง [Authorize] | AuthTests เพิ่ม POST api/templates/{0}/generate-draft |
| add-in: อ่าน TemplateId / ส่งเฉพาะ startVersioning / append form field | metadataService.test.ts (ไฟล์ร่างอ่าน TemplateId), versionFlow.test.ts (แนบ/ไม่แนบ templateId), versioningService.test.ts (form field) |
| เว็บ: เปิด docx URL ที่ถืออยู่เอง (ไฟล์ร่าง) ผ่าน ms-word + fallback + notice | useVersionOpen.spec.ts openDocxUrl test |
ชุดเต็ม (v9.2.1): backend 89 ผ่าน, add-in 66 ผ่าน, web 60 ผ่าน + production build ผ่าน
ค้างยืนยันด้วยมือ (ต้องมี Word จริง)
- Mac: เว็บสร้างไฟล์ร่าง → Word เปิดไฟล์ทันที พร้อมลิงก์สำรอง → ตรวจการตัดคำ/จัดหน้าภาษาไทยของ field ที่ค่ายาว (เช่น Detail ของ T14) ตรงตาม template → task pane แสดง untracked + hint ไฟล์ร่าง → Start Versioning → v1 + หน้า document detail ของเว็บแสดง template และค่า fields
- Mac: Word cold-start (ปิด Word ทิ้งแล้วกด "เปิดใน Word" ครั้งแรก) — ยืนยันว่า fallback ดาวน์โหลดไฟล์ให้อัตโนมัติ + กดครั้งที่สองขณะ Word เปิดอยู่แล้วเอกสารเปิดสำเร็จ (v9.1 แก้ตาม feedback จริง: ครั้งแรก Word แสดงหน้า Recent แทนเอกสาร — ข้อจำกัด
ms-word:ofeบน macOS ตอน cold-start) - Word for Windows / Word on the web — ตามข้อค้างเดิมตั้งแต่ AC-1
รายการเต็ม: แผน FR-16.
การทดสอบ ms-word:ofe บนเครื่องจริง (Mac, Word 16.101.25092124 — 2026-09-10)
| ทดสอบ | ผล |
|---|---|
| `ms-word:ofe | u |
| `ms-word:ofe | u |
| `ms-word:ofe | u |
| `ms-word:ofe | u |
open-word: / launch-word: scheme |
❌ ไม่ยิง request |
open -a "Microsoft Word" /tmp/.../v1.docx |
✅ เปิดได้ (แต่เรียกจากเว็บไม่ได้ — browser sandbox + backend รัน server กลางใน production) |
→ ข้อสรุป: download-first คือกลไกเดียวที่ deterministic ทุกเครื่อง (commit ตามด้วย v9.2); ms-word:ofe เหลือเป็น best-effort ควบคู่
QA reviewer: PASS, 2 rounds, 8 findings แก้ครบไม่มีที่ถูกปฏิเสธ (2026-09-10) — round 1: blocker .at(-1) ใน test ใหม่ตัดกับ tsconfig lib: es2020 ทำ addin npm run build พัง (jest ไม่ type-check จึงรอดมา) + minors 4 (นับจำนวน test ผิดในตารางนี้, stale notices ระหว่างสองปุ่ม submit, AlertBox สำเร็จไม่ใช่ type="success", isTrackedDocument ไม่มี production caller → เก็บไว้พร้อมหมายเหตุเหตุผล) · round 2: minors 2 + pre-existing 1 (หัวเอกสาร/สถานะเก่าใน acceptance-review, CLAUDE.md, implementation-plan header). ค้าง manual บน Word จริงตามรายการด้านบน.
QA reviewer รอบ v9 (เปิดใน Word + ตัดคำไทย): PASS, 2 rounds, 10 findings แก้ครบไม่มีที่ถูกปฏิเสธ (2026-09-10) — round 1: minors 6 (dead downloadFile/filenameFrom, glossary "stateless" เก่า, lifecycle config เปลี่ยนแล้วเป็น no-op เงียบๆ + days ไม่ validate, ไม่มี test คุม lifecycle, comment เก่าใน TestDocx, typo ใน spec) · round 2: minors 2 + nits 2 (comment เก่าอีก 2 จุด, "(stateless)" ตกค้าง 2 บรรทัด, lifecycle test เพิ่ม assertion ผ่านค่าคืน true/false, ตัว verdict นี้เอง) — และ test ใหม่จับ bug จริงได้: Configuration.Rules เป็น null ได้ทำให้ startup ตั้ง rule ล้มแบบเงียบมาตั้งแต่แรก (โดน catch กลืน) แก้ด้วย ?? [].
QA reviewer รอบ hotfix อักขระควบคุม 0x0B จากการ paste: PASS, 1 round (2026-09-10) — reviewer พิสูจน์ว่า test ใหม่ fail จริงบนโค้ดก่อนแก้ · minors 3: แก้ 1 (เปลี่ยนอักขระ VT/FF ล่องหนใน test เป็น escape sequence \u000B/\u000C กันหายระหว่างแก้ไขไฟล์) · ปฏิเสธ 2 พร้อมเหตุผล: (1) fast-path ตรวจ surrogate คู่ครบเป็น invalid ทำให้ค่ามีอีโมจิเดิน slow path — ผลลัพธ์ยังถูก (loop ประกอบคู่กลับคืน) และค่า field สั้นจึงแพงแค่หยดเดียว ไม่คุ้มจะ duplicate pair-logic (2) broad catch ให้ 502 แทน 500 สำหรับ bug ภายใน — สอดคล้อง convention 502 ของ "ไฟล์ template/ค่ามีปัญหา" ที่มีอยู่ และเหตุการณ์จริงที่ catch นี้มีไว้กันคือ input-triggered แท้ๆ (stack ยัง log ฝั่ง server)
QA reviewer รอบ hotfix "เปิดใน Word ตอน cold-start" (v9.1): PASS, 1 round (2026-09-10) — reviewer stash-verify ว่า test ใหม่ fail จริงบนโค้ดก่อนแก้ · minors 3: แก้ 1 (เพิ่ม test re-click ยกเลิก auto-download ค้าง — save ครั้งเดียว URL ล่าสุด) · ยอมรับ 2 พร้อมเหตุผล: (1) ถ้า Word cold-start มาถึงหน้าต่างภายใน 3 วิ heuristic เห็น "สลับแอป" จึงไม่ auto-download ทั้งที่เอกสารไม่เปิด — เป็นข้อจำกัดเชิงหลักการของ heuristic (แยก "Word เปิด" กับ "เปิดเอกสาร" ไม่ได้จาก web) spec ระบุชัดว่าไม่ใช่หลักฐานความสำเร็จ + มี manual check item ครอบ (2) ถ้า Word เปิดเอกสารสำเร็จแต่ช้ากว่า 3 วิ ผู้ใช้ได้ไฟล์ซ้ำใน Downloads — ต้นทุนต่ำยอมรับได้ · ตรวจแล้ว: PDF path ไม่โดน auto-download, ทุก docx entry point (version rows/v0/ไฟล์ร่าง) ผ่าน openDocxUrl ครบ
QA reviewer รอบ v9.2 (download-first + open-link): PASS, 1 round (2026-09-10) — reviewer ตรวจ security ของ /api/open เชิงลึก (HMAC binding/fixed-time compare/path scoping ของ draft catch-all route — ผ่านทั้งหมด) + รัน suites เองได้ 86/58/66 ตรงตาม docs · minors 4 แก้ครบ: (1) คำ spec header ที่ขัดแย้งตารางทดสอบเอง (2) OpenLinks config ตรวจตั้งแต่สตาร์ทด้วย ValidateOnStart (Secret ว่าง/BaseUrl ไม่ใช่ https = fail ดัง) (3) เอา dead code presigned ออก (GetPresignedGetUrl/PresignedExpirySeconds) + comment/copy เก่าทั้ง web/add-in/CLAUDE.md (4) draft success copy นำด้วยการดาวน์โหลดแทน "กำลังเปิดใน Word"
QA reviewer รอบ follow-up v9.2 (เลิกยิง ms-word บน Mac default + จดสูตร sideload): PASS, 1 round (2026-09-10) — minors 3: แก้ 2 (sync implementation plan กับ spec + ตัวเลข 60, comment เก่าใน create page) · ปฏิเสธ 1 พร้อมเหตุผล: ปุ่มยังชื่อ "เปิดใน Word" บน Mac ทั้งที่ตอนนี้=ดาวน์โหลด — notice หลังกดอธิบายชัดและชื่อปุ่มเดิมมาก่อนการเปลี่ยนนี้ เปลี่ยนชื่อกระทบ UX ที่ทีมคุ้นเคย เก็บเป็นข้อสังเกตไว้ก่อน (จะเปลี่ยนเมื่อมี feedback ผู้ใช้จริง)
QA reviewer รอบ v9.2.1 (ชื่อไฟล์จริงใน URL open-link): PASS, 1 round (2026-09-10) — user แจ้งได้แต่คำว่า "file" เพราะ v9.2 ตั้งท้าย URL เป็น file.docx ตายตัว · minors 2 แก้ครบ: sync spec changelog + เพิ่ม tests ป้องกัน (tampered stamp/templateId และสลับนามสกุล .docx↔.pdf บน version route ต้อง 404 — พบระหว่างเขียนว่า draft route ไม่ผูกชื่อท้ายกับลายเซ็นเพราะ format เป็น docx เสมอ จึงย้ายเคสนามสกุลไป version route ที่ format ผูกลายเซ็นจริง) · reviewer ยืนยัน URL name = disposition name เสมอ (คำนวณจาก source เดียวกัน) และ escaping ชื่อไทยผ่าน end-to-end