E2E Runtime Verification — รันจริง local (real ASB + real DB)
ผลรันจริง E2E onboarding/approval บน local fleet 7 service — แยกชัด 3 ชั้นการทดสอบ (Postman HTTP / dev-driver in-memory / direct-ASB) + sequence ทีละ request ของ Postman collection (18/44 ที่ขับจริง) ว่าพังจังหวะไหน เพราะอะไร
อัปเดต: 2026-06-25
วันที่: 2026-06-25 · runtime view — คู่กับ
05-e2e-sequence-diagrams.md(static code view)เอกสารนี้แก้ใหม่ (v2): รอบก่อน report รวบ 3 ชั้นการทดสอบที่ต่างกันไว้เป็นก้อนเดียว (“ทดสอบ 44 API / เขียว 5/5”) ทำให้ดูเหมือน Postman ขับ flow เขียวทั้งเส้น — ไม่จริง · v2 นี้แยก 3 ชั้นออกจากกัน + ลง sequence ทีละ request ตั้งแต่ต้นจนจบ ว่า request ไหนควรได้อะไร · จริงได้อะไร · พังเพราะอะไร เพื่อให้ตัดสินใจได้ว่าจุดไหนตามต่อจุดไหนข้าม
🟢 UPDATE (รันรอบใหม่ 2026-06-25 เย็น): ปิดช่องว่าง S1 ได้แล้ว — ขับ NEW_REQUESTOR จริงทั้งเส้น ตั้งแต่ OTP step 1 (email มั่ว, mock Entra/OTP) → submit → US→ASB→OC saga → WF pickup/approve จริง → OC saga
Approved/Completed(local OC consume จริงทุก hop, DB-verified) · WF create bug (WfTemplate.Id) = fix แล้วในdevelopment(merge มาใช้ได้) · ขอบเขต: ถึง OC saga terminal เท่านั้น — return path กลับ US/NS ยังไม่ verify · หลักฐานเต็ม: Atlasdocs/orchestration-integration/25062026/REAL_E2E_RUN_RESULT.md(+MOCK_REMOVAL_LIST.mdสำหรับ mock ที่ต้องลบ)
0. สำคัญที่สุด — มี 3 ชั้นการทดสอบ คนละเรื่องกัน (อย่ารวบ)
รอบนี้มีการพิสูจน์ 3 แบบ ซึ่งคนละ surface คนละระดับหลักฐาน — รอบก่อนเอามารวมเป็น “เขียว” ก้อนเดียว = เข้าใจผิด:
| # | ชั้น (surface) | ขับด้วยอะไร | verdict | ระดับหลักฐาน |
|---|---|---|---|---|
| S1 | Postman HTTP จริง (A1–A14) — CRUD ผ่าน HTTP ของ service จริง | newman รัน collection ชน localhost fleet | 🔴 แดงเกือบหมด — ทุก state-mutation พัง | จริงสุด (รัน HTTP จริง มี response code ทุก request) |
| S2 | Postman dev-driver in-memory (request OC demo/onboarding) | 1 HTTP call → OC รัน saga timeline ในหน่วยความจำ | 🟢 เขียว แต่พิสูจน์แค่ saga logic | กลาง — in-memory, endpoint dev-only, ไม่ใช่ integration ผ่าน ASB |
| S3 | Direct node→ASB publish (จำลอง message ที่ US/WF จะส่ง) | script @azure/service-bus ยิง event เข้า superapp-dev จริง | 🟢 เขียว 5/5 hop แต่ message สังเคราะห์ + เป็นหลักฐานรอบก่อน | กลาง — OC consume→saga→persist จริงบน ASB จริง แต่ publisher เป็น harness ไม่ใช่ US/WF จริง · evidence รอบ session ก่อน (RefNo E2E-LOCAL-mqtflyy7) ยังไม่ได้ re-verify รอบนี้ |
อ่านบรรทัดเดียว: Postman (S1) ไม่เคยขับ saga ให้เขียวได้แม้แต่ hop เดียว — มันพังที่ submit ตั้งแต่ต้น · ที่ “เขียว 5/5” คือ S3 (ยิง ASB ตรง ไม่ใช่ Postman) · ที่ saga logic ถูกต้องคือ S2 (in-memory) · 3 อย่างนี้ห้ามนับรวมกัน
1. Boot 7/7 ✅ (พื้นฐานก่อนทุก surface)
| check | ผล | หลักฐาน |
|---|---|---|
7 service boot + /health 200 | ✅ 7/7 | US OC WF Task NS Codex Central ตอบ 200 |
| ต่อ DB จริง (cluster Azure) | ✅ | Codex คืน app rows · OC/WF persist saga ลง DEV_OrchestratorDb |
| ต่อ ASB จริง (superapp-dev) | ✅ | OC register 6/6 consumer (“started — Topic=…”) ไม่มี error |
| WF ชี้ DEV_WorkflowDb โดยไม่ auto-migrate | ✅ | log [KeyVault] VaultUri='' + ไม่มี “Applying migration” |
2. Surface S1 — Postman/newman HTTP run: ทีละ request ตั้งแต่แรกจนจบ
collection มี 44 request · รันจริง 18 (Setup 3 + Scenario A 15) · อีก 26 ไม่ได้ขับ (§3)
รัน: newman ชน localhost fleet · เวลา 2026-06-25T11:01:03Z · report: scratchpad/reports/scenarioA.{html,xml}
ตัวแปรที่จับได้: flow_instance_id = a2d3b444-f704-4492-8b71-beeff7651700 · ref_no = ว่างตลอด (submit ไม่เคยสำเร็จ → ไม่มี refNo ออกมา)
อ่านคอลัมน์: ควรได้ = ตาม design happy-path · จริง = HTTP code ที่ได้ + state · verdict ✅ ผ่าน / ❌ พังเอง (independent) / ⛔ cascade (พังต่อเนื่องเพราะ id ว่าง) / ⚠️ สรุปไม่ได้
| # | request (เจตนา) | ควรได้ | จริง (รันแล้ว) | verdict |
|---|---|---|---|---|
| 1 | 00 NS whoami (no-auth diag) | 200 reachable | 200 | ✅ |
| 2 | 00 US list flow-definitions | 200 list | 200 | ✅ |
| 3 | 00 OC demo/onboarding?approve | 200 (= S2 in-memory saga) | 200 (11.7s, รัน timeline ในหน่วยความจำ) | ✅ (S2 ไม่ใช่ S1) |
| 4 | A1 US Start instance | 200 + refNo + step=OTP | 200 · flowInstanceId ออก · refNo=null · status=Draft · currentStep=OtpVerificationStep | ✅ create (refNo ยังว่างปกติ — ออกตอน submit) |
| 5 | A2 US SaveDraft (CompanyInfoStep) | 200 draft saved | 500 | ❌ F6 |
| 6 | A3 US Submit (SubmitApplicationStep) | 2xx + publish flow-submitted + status=Submitted | 500 (ProblemDetails 400B) · ไม่ publish อะไรเลย | ❌ F6 (ต้นเหตุทั้งหมด) |
| 7 | A3b WF Create instance | 2xx + wfInstanceId | 500 (documentId ว่าง เพราะ refNo ว่าง) | ⚠️ สรุปไม่ได้ — ยิงด้วย documentId ว่าง ยังไม่ฟันธงว่า WF พัง |
| 8 | A4 WF get by-document/{refNo} | 200 instance | 404 — URL เป็น by-document/ (refNo ว่าง) | ⛔ cascade |
| 9 | A5 TS get task/ref/{refNo} | 200 task | 404 — URL เป็น tasks/ref/ (ว่าง) | ⛔ cascade |
| 10 | A6 WF Pickup maker → InReview | 2xx → state InReview | 500 — wf_instance_id ว่างใน body | ⛔ cascade |
| 11 | A7 US get status → คาด InReview | InReview | 200 แต่ status=Draft · step=OtpVerificationStep (ไม่ขยับ) | ✅ อ่านได้ / ❌ ไม่ advance |
| 12 | A8 WF take-action maker → Reviewed | 2xx | 404 — URL Workflow//action (id ว่าง → double-slash) | ⛔ cascade |
| 13 | A9 US get status → คาด Reviewed | Reviewed | 200 = Draft (เหมือนเดิม) | ✅ อ่าน / ❌ |
| 14 | A10 WF Pickup approver → InApprove | 2xx | 500 — id ว่าง | ⛔ cascade |
| 15 | A11 US get status → คาด InApprove | InApprove | 200 = Draft | ✅ อ่าน / ❌ |
| 16 | A12 WF take-action approver → Approved | 2xx | 404 — id ว่าง | ⛔ cascade |
| 17 | A13 US get status → คาด Approved | Approved | 200 = Draft | ✅ อ่าน / ❌ |
| 18 | A14 NS notifications | 200 list | 401 Unauthorized | ❌ F7 |
🔍 หลักฐานชี้ขาด (smoking gun)
A7/A9/A11/A13 (GET status 4 ครั้งระหว่างทาง) ได้ 200 ทุกครั้ง แต่คืนค่าเดิมตลอด:
{"data":{"flowInstanceId":"a2d3b444-…","refNo":null,
"status":"Draft","currentStepType":"OtpVerificationStep", …}}
→ instance ค้างอยู่ที่ step แรก (OTP) ไม่เคยขยับเลย · ทุก GET เขียวเพราะแค่ “อ่าน instance ที่ A1 สร้าง” ไม่ได้แปลว่า flow เดินหน้า
📌 สรุป S1 — เห็นภาพเดียวกัน
- เขียวจริงมีแค่: ping 3 ตัว (1–3) + A1 create + 4× GET-status (อ่านอย่างเดียว) · state-mutation ผ่าน HTTP พังหมด
- ต้นเหตุเดียว = F6 ที่ A3 (submit 500): NEW_REQUESTOR flow-def เริ่มที่
OtpVerificationStepแต่ collection กระโดดไปยิงSubmitApplicationStep(เป็น step ของ RETURNING_REQUESTOR) → guard ตัดสิน forbidden ถูกต้อง แต่ F6 ทำให้ออกเป็น 500 →refNoไม่เคยเกิด → A4–A12 ทั้งหมดยิงด้วย id ว่าง = พังต่อเนื่อง (cascade) - finding ที่ independent จริง ๆ มีแค่ 2 ตัว:
F6(submit→500) กับF7(NS 401) · ที่เหลือคือ cascade จาก id ว่าง — ไม่ใช่ bug แยก (ยังตัดสินไม่ได้จนกว่า submit จะผ่านแล้วได้ refNo จริง) - A3b (WF create 500) = ยังสรุปไม่ได้ — ยิงด้วย documentId ว่าง · ต้องมี refNo จริงก่อนถึงจะรู้ว่า WF create พังจริงไหม
3. Surface S1 — 26 request ที่ “ไม่ได้ขับ” (เหตุผลเดียวกันหมด)
| folder | จำนวน | ทำไมไม่ขับ |
|---|---|---|
| Scenario B (Reject) | 3 | ต้องผ่าน A1–A6 ก่อน → ตันที่ submit/pickup เหมือน A |
| Scenario C (Rework→Resubmit) | 4 | เหมือนกัน |
| Scenario D (Cancel 2 ทาง) | 3 | เหมือนกัน |
| Scenario E (Customer auto-approve) | 4 | เริ่มที่ US submit เหมือนกัน (ไม่ผ่าน OC แต่ก็ติด submit ฝั่ง US) |
| Scenario F (Multi-approver) | 2 | ต้องถึง InApprove ก่อน → ไม่ถึง |
| Scenario G (Resubmit after session loss) | 2 | ต้องมี wf_instance_id จริง → ไม่มี |
| Scenario H (Closed) | 0 | placeholder — design ยังไม่นิยาม (defer D3) |
99 OC Dev-Driver | 8 | endpoint dev-only — ขับเทียบเท่าผ่าน S3 (ยิง ASB ตรง) แทน |
ทุก scenario เริ่มจาก submit ตัวเดียวกัน → ถ้า submit ยัง 500 ก็ block เหมือนกันหมด · ขับได้เมื่อ unblock §6
4. Surface S3 — Choreography spine 5/5 (ยิง ASB ตรง · ไม่ใช่ Postman · หลักฐานรอบก่อน)
ย้ำ provenance: ส่วนนี้ไม่ได้ขับด้วย Postman · ใช้ node @azure/service-bus craft message ที่ US/WF จะ publish แล้วยิงเข้า superapp-dev จริง → ดู OC consume→saga→persist · publisher เป็น harness สังเคราะห์ · evidence = session รอบก่อน (RefNo E2E-LOCAL-mqtflyy7 ใน DEV_OrchestratorDb) — รอบนี้ยังไม่ได้ re-verify
sequenceDiagram
autonumber
participant HR as Harness (node→ASB)
participant OC as OrchestratorService
participant DB as DEV_OrchestratorDb
rect rgb(232,248,232)
HR->>OC: ASB flow-submitted (superapp-dev จริง)
OC->>DB: INSERT OrchestrationInstance = AwaitingPickup ✅
end
rect rgb(232,248,232)
HR->>OC: ASB workflow-pickup {WfInstanceId=99001}
OC->>DB: state = UnderReview ✅
end
rect rgb(232,248,232)
HR->>OC: ASB sent-to-reviewer
OC->>DB: state = UnderConsideration ✅ (05- เคย ❌ MISSING)
end
rect rgb(232,248,232)
HR->>OC: ASB approval-pickup
OC->>DB: state = InApprove ✅ (05- เคย ❌ ใหม่ 100%)
end
rect rgb(232,248,232)
HR->>OC: ASB workflow-decision {Approve}
OC->>DB: state = Approved / Lifecycle = Completed ✅
end
rect rgb(255,232,232)
Note over OC: return path (flow-progress-update / flow-approval-result)
Note over OC: saga raise outbox ✅ แต่ US consume ไม่ verify (synthetic FlowInstanceId)
end
| hop | state | runtime (S3) | หมายเหตุ |
|---|---|---|---|
| flow-submitted | AwaitingPickup | ✅ | OC consume + persist จริง |
| workflow-pickup | UnderReview | ✅ | WfInstanceId=99001 |
| sent-to-reviewer | UnderConsideration | ✅ | 05- เคยมาร์ค ❌ MISSING |
| approval-pickup | InApprove | ✅ | 05- เคยมาร์ค ❌ ใหม่ 100% |
| workflow-decision(Approve) | Approved/Completed | ✅ | จบ saga |
| return → US → NS | — | 🟡/❌ | outbox raise จริง แต่ consume/NS ยังไม่ verify |
คุณค่าของ S3: ยืนยันว่า “code แห้ง” ฝั่ง OC consume→saga→persist ทำงานจริงบน ASB จริง (รวม 3 hop ที่ 05- เคยมาร์คว่า MISSING) · ข้อจำกัด: publisher ไม่ใช่ US/WF จริง + ยังไม่ re-verify รอบนี้ → ก่อนเชื่อ ควรยิงซ้ำ+ query DEV_OrchestratorDb ใหม่
5. ติดอะไร — แยก independent vs cascade
Independent (เป็น bug/gap จริง ต้องจัดการ):
| # | ติดอะไร | ผลตอนรัน | สถานะ · เพื่อ unblock |
|---|---|---|---|
| F6 | FlowAccessGuard คืน Forbidden ด้วย Error.Failure (line 43/46/63) → ApiControllerBase map เป็น 500 ไม่ใช่ 403 (ทุก forbidden path) | US submit/savedraft 500 → block S1 ทั้งหมด | 🔴 confirmed · fix: Error.Failure→Error.Forbidden (product fix ทีม US) · ⚠️ fix แล้วได้ 403 ถูก แต่ยัง submit ไม่ผ่านจนกว่าจะเดิน OTP จริง |
| F7 | NS ไม่มี env auth-bypass | NS HTTP 401 (A14) | 🟡 defer · fix: [AllowAnonymous] หรือใส่ token |
| F4 | NotificationDb ขาด Config.AppConfigurations/CustomErrorCodes (42P01) | NS seeder fail (boot degraded) | 🟡 open — confirm minimal-DB vs migration gap |
| F5 | inter-service URL = cluster-DNS ไม่ว่าง → ไม่ fall-back stub | call-time hard-fail (Centralized→Codex) | 🟡 open (config robustness) |
Cascade (ไม่ใช่ bug แยก — พังเพราะ id ว่างหลัง submit ตาย): A4/A5 404 (refNo ว่าง) · A6/A10 500 (wf_instance_id ว่าง) · A8/A12 404 (//action) · A3b 500 (documentId ว่าง — สรุปไม่ได้) → ทั้งหมดจะหายเองเมื่อ submit ผ่านแล้วมี refNo จริง
Harness-side (เจอตอนทำ S3):
- F9 node publisher ต้องส่ง body เป็น
Buffer.from(json)(AMQP Data) ไม่งั้น .NET deserialize fail → dead-letter “Malformed” · กระทบจริงถ้ามี non-.NET producer บน prod - F9b (ไม่ใช่ bug) approval-pickup ครั้งแรกไม่ transition = harness race (publish เร็วเกิน, saga
ExpectStatereject out-of-order = ถูกต้อง) · เว้นจังหวะแล้วผ่าน
6. ตามต่อ หรือ ข้าม — เลือกได้ (chase-vs-skip)
งานนี้คือ “รู้ก่อน deploy ว่าต้องปิดอะไร” · แต่ละจุดมีต้นทุนต่างกัน — เลือกตามนี้:
| จุด | ถ้า ตามต่อ ต้องทำอะไร | ต้นทุน | ถ้า ข้าม ยอมรับอะไร |
|---|---|---|---|
| ทำ S1 ให้เขียวจริง (HTTP path) | flip OTP ExternalEntra→InternalGenerate (config ฝั่ง NS) แล้วเดิน NEW_REQUESTOR wizard 8 step จริง (OTP→…→submit) → US publish flow-submitted เอง | สูง (harness OTP + 8 step) | พึ่งหลักฐาน S3 (ASB-injection) แทน · ยอมรับว่า US HTTP→publish ยังไม่เคย verify |
| แก้ F6 | Error.Failure→Error.Forbidden 3 จุด | ต่ำ (1 บรรทัด×3) | prod ทุก forbidden ออกเป็น 500 (5xx alert ลวง) ต่อไป |
| ขับ WF จริง (แทน S3 harness) | POST /instances → pickup → take-action จริง ให้ WF publish เอง | กลาง | เชื่อ S3 ว่า OC consume ถูก แต่ไม่รู้ว่า WF publish payload ตรง schema ไหม |
| re-verify S3 รอบนี้ | ยิง ASB ซ้ำ + query DEV_OrchestratorDb ใหม่ | ต่ำ | อ้างหลักฐานรอบก่อน (RefNo mqtflyy7) ตามเดิม |
| A3b / WF create | ต้องมี refNo จริงก่อน (ขึ้นกับ submit) | — | ยังไม่รู้ว่า WF create พังจริงไหม |
| F7 / NS 401, F4, F5 | ตามตาราง §5 | ต่ำ–กลาง | ไม่อยู่ critical path A · defer ได้ |
ขอ user ตัดสิน: จะให้เดิน “ตามต่อ S1 ให้เขียวเต็มเส้น” (ลงทุน OTP+8 step) หรือ “ข้าม S1 — พอใจกับ S3+S2 แล้วโฟกัสแก้ F6 เป็น product bug” · บอกมาได้เลยว่าจุดไหนข้าม
เอกสารหลักฐานละเอียด (command + DB query + log) อยู่ที่ Atlas
docs/orchestration-integration/25062026/AC_REPORT.md⚠️ หมายเหตุ:AC_REPORT.md+HANDOFF_LOCAL_E2E_RUNTIME.mdยังเขียนรวบ “เขียว 5/5” แบบไม่แยก 3 surface เหมือน v1 — ถ้าจะให้ตรงกับ v2 นี้ บอกได้ จะไปแก้ให้แยกชั้นเหมือนกัน