iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0

摘要
Day 18 已經把驗證流程放進 Docker 與 CI quality gate。Day 19 把既有規則測試、分層驗證測試、情境測試與版本比較測試整理成可閱讀的覆蓋證據,並整理首頁結果。

這和「能不能交換」有什麼關係?

前面幾天已經可以回答:

這份 Bundle 在 JSON、FHIR R4、TW Core 與交換契約規則下能不能通過?

但對資料交換來說,只知道結果還不夠。
還要能回答另一個問題:

這個 Passed / Blocked 判斷,有沒有測試證據支撐?

如果驗證結果只是一個畫面狀態,讀者很難知道:

  • 六條交換契約規則是不是都至少測過失敗案例。
  • PASSFAILNOT_APPLICABLENOT_EVALUATED 有沒有被正確區分。
  • TW Core validation、FHIR R4 validation 和交換契約規則各自負責哪一層。
  • 契約 v1.0 / v1.1 的差異是不是可重現測試,而不是人工觀察。

所以 Day 19 的重點是:

把散在測試裡的行為,整理成可以檢查的覆蓋證據。
把首頁整理成一份可以被使用者閱讀的 Quality Test Report。

這不是新驗證邏輯。
它是把目前 MVP 的證據鏈整理清楚。

今天的實作範圍

今天新增或修改的範圍有:

  • docs/journal/20260820.md
  • src/main/resources/templates/index.html
  • src/main/java/com/twlab/qualitygate/validation/*
  • src/main/java/com/twlab/qualitygate/web/ParseController.java
  • src/test/java/com/twlab/qualitygate/web/ParseControllerTests.java
  • src/test/java/com/twlab/qualitygate/validation/*Tests.java

Day 19 做三件事:

整理六條交換契約規則覆蓋矩陣。
整理分層驗證與契約版本情境覆蓋表。
把首頁統一成 English-first Quality Test Report。

為什麼不是只看測試數?

Day19 更新後確認:

./mvnw test
Tests run: 60, Failures: 0, Errors: 0, Skipped: 0

測試數通過很重要,但還不夠。
因為 60 個測試可能集中在某幾條規則,也可能只測 happy path。
對資料品質閘門來說,更重要的是每條規則的狀態語意是否被固定下來。

本專案目前的交換契約規則結果有四種:

Outcome 意義
PASS 規則已執行,資料符合交換契約條件
FAIL 規則已執行,資料違反交換契約條件
NOT_APPLICABLE 規則不適用目前 Bundle 內容
NOT_EVALUATED 規則需要的判斷超出 MVP 可評估邊界

這四種狀態不能混用。

例如外部 HTTP reference 目前沒有外部 FHIR Server 查詢能力。
它不能被寫成 PASS,因為系統其實沒有查到。
它也不一定應該直接寫成 FAIL,因為資料可能存在於外部 server。
所以 reference 類規則需要 NOT_EVALUATED

但 LOINC / UCUM 契約允許集合規則不同。
它們只檢查 Bundle 內 Observation 的 coding 或 quantity 欄位,不需要外部查詢。
所以目前沒有 NOT_EVALUATED 案例,是設計邊界,不是測試缺漏。

六條交換契約規則覆蓋矩陣

這張矩陣只放六條交換契約規則。
TW Core validation 和契約版本比較不放在這裡,避免把不同層級混在同一張表。

規則 規則目的 PASS 證據 FAIL 證據 NOT_APPLICABLE 證據 NOT_EVALUATED 證據 測試類別
LAB-REF-001 Observation.subject 必須指向 Bundle 內 Patient valid-internal-reference.json / passesWhenObservationSubjectPointsToBundlePatientById missing-internal-reference.json / failsWhenObservationSubjectPatientIsMissingFromBundle unsupported-resource-in-bundle.json / isNotApplicableWhenBundleHasNoObservation external-http-reference.json / doesNotEvaluateExternalHttpReference LabRef001ObservationSubjectRuleTests
LAB-REF-002 DiagnosticReport.result 必須指向 Bundle 內 Observation valid-internal-reference.jsonvalid-report-result-full-url-reference.json missing-report-result-reference.jsonmissing-report-result-field.json unsupported-resource-in-bundle.json / isNotApplicableWhenBundleHasNoDiagnosticReport external-report-result-reference.json / doesNotEvaluateExternalHttpReference LabRef002DiagnosticReportResultRuleTests
LAB-REF-003 DiagnosticReport.subject 與 referenced Observation.subject 必須是同一 Patient valid-internal-reference.jsonreport-subject-full-url-observation-subject-id.json mismatched-report-observation-patient.jsonmissing-report-result-reference.jsonmissing-report-result-field.json unsupported-resource-in-bundle.json / isNotApplicableWhenBundleHasNoDiagnosticReport external-report-result-reference.jsonexternal-observation-subject-reference.json LabRef003ReportObservationPatientRuleTests
LAB-CODE-001 Observation.code 必須包含契約允許的 LOINC coding valid-loinc-code.json / passesWhenObservationHasAllowedLoincCode loinc-code-not-allowed.jsonobservation-code-without-coding.jsonobservation-coding-without-code.json unsupported-resource-in-bundle.json / isNotApplicableWhenBundleHasNoObservation 不適用:此規則只檢查 Bundle 內 coding,不查外部 terminology server LabCode001ObservationLoincRuleTests
LAB-UNIT-001 Quantity 檢驗值必須有可讀 valueQuantity.unit valid-minimal-lab-bundle.json / passesWhenQuantityHasReadableUnit observation-quantity-without-unit.json / failsWhenQuantityHasNoUnit observation-value-string.jsonunsupported-resource-in-bundle.json 不適用:此規則只檢查 Bundle 內 Quantity.unit LabUnit001ObservationQuantityUnitRuleTests
LAB-UNIT-002 Quantity 檢驗值必須有允許的 UCUM system/code valid-ucum-code.json / passesWhenQuantityHasAllowedUcumSystemAndCode observation-quantity-wrong-ucum-system.jsonobservation-quantity-ucum-code-not-allowed.jsonobservation-quantity-without-ucum-code.json observation-value-string.jsonunsupported-resource-in-bundle.json 不適用:此規則只做契約允許集合檢查,不做完整 UCUM terminology validation LabUnit002ObservationUcumCodeRuleTests

這張表暴露出三件事。

第一,六條規則都有獨立 FAIL 證據。
這符合目前 MVP 的最低要求:

每條規則至少有獨立 Fail case。

第二,NOT_EVALUATED 目前集中在 Reference 類規則。
因為 LAB-REF-001LAB-REF-002LAB-REF-003 都可能遇到外部 HTTP reference。
目前 MVP 沒有外部 FHIR Server 查詢能力,所以只能標示尚未評估。

第三,LAB-REF-001 原先沒有獨立 NOT_APPLICABLE 測試。
因此補上沒有 Observation 的 unsupported-resource-in-bundle.json,確認此規則不會把「不適用」誤寫成 PASSFAIL

分層驗證覆蓋表

首頁的 Quality Gate 不只包含六條交換契約規則。
它還包含 JSON parse、FHIR R4 parse、Resource Type Gate、FHIR R4 validation 與 TW Core validation。

所以這些層級另外整理成分層驗證覆蓋表:

驗證層 主要目的 已覆蓋狀態 代表測試 / fixture 備註
JSON parse 確認輸入是否為合法 JSON PASSED, FAILED BundleParseServiceTests.reportsInvalidJsonWithoutThrowing JSON 失敗時後續 FHIR / 契約層不執行
FHIR R4 parse 確認 JSON 可被 HAPI FHIR 解析為 R4 Resource PASSED, FAILED BundleParseServiceTests 的非法輸入與合法 Bundle 測試 這層是 FHIR parser,不等同 Profile validation
Resource Type Gate 確認入口只處理 Bundle.type = collection PASSED, FAILED rendersResourceInventoryForSupportedAndUnsupportedEntries, non-Bundle / missing type 測試 不支援 Resource 不冒充已驗證
FHIR R4 validation 執行 HAPI FHIR R4 validation 並保留 OperationOutcome PASSED, FAILED, NOT_EVALUATED rendersFhirValidationIssue, invalid JSON 測試 warning 顯示但不一定阻擋
TW Core validation 執行 TW Core 或安全降級 PASSED, FAILED, NOT_EVALUATED BundleParseServiceTests 的 stub passed / failed / fallback 測試 無法穩定驗證時顯示 NOT_EVALUATED,不冒充 Profile 通過
Exchange contract rules 執行六條交換契約規則 PASS, FAIL, NOT_APPLICABLE, NOT_EVALUATED 六條規則測試類別 詳細證據在六條交換契約規則覆蓋矩陣
Quality Gate 彙總前面各層與契約規則結果 PASSED, PASS_WITH_WARNINGS, BLOCKED BundleParseServiceTests TW Core 明確失敗或契約規則失敗會阻擋

這張表的目的,是把「首頁顯示的每一層」和「測試裡保護的行為」對起來。

契約版本情境覆蓋表

單條規則測試確認規則本身。
情境測試則確認多條規則一起跑時,Quality Gate 和契約版本比較仍然符合預期。

目前 ContractScenarioCaseTests 覆蓋 4 組代表案例:

情境 Fixture v1.0 Gate v1.1 Gate v1.0 failed rules v1.1 failed rules
合法最小檢驗 Bundle valid-minimal-lab-bundle.json PASSED PASSED None None
v1.1 新增 UCUM 要求 observation-quantity-wrong-ucum-system.json PASSED BLOCKED None LAB-UNIT-002
缺少內部 Patient reference missing-internal-reference.json BLOCKED BLOCKED LAB-REF-001, LAB-REF-003 LAB-REF-001, LAB-REF-003
非 Quantity Observation non-quantity-observation-bundle.json PASSED PASSED None None

另外,coversNotApplicableWhenObservationValueIsNotQuantity 會確認非 Quantity Observation 在 v1.1 下:

LAB-UNIT-001 -> NOT_APPLICABLE
LAB-UNIT-002 -> NOT_APPLICABLE

這個情境很重要。
如果只看 Gate,它是 PASSED
但從規則結果看,它不是 unit 規則通過,而是 unit 規則不適用。

這兩者不能混在一起。

版本比較測試覆蓋

ContractComparisonServiceTests 固定一個最小版本差異:

同一份 observation-quantity-wrong-ucum-system.json
v1.0 -> PASSED
v1.1 -> BLOCKED

測試同時確認:

v1.0 不包含 LAB-UNIT-002
v1.1 包含 LAB-UNIT-002 且結果是 FAIL

這讓版本比較不只是畫面展示。
它有 service-level regression test 保護。

首頁統一成 Quality Test Report

Day 18 之前,首頁已經能顯示很多資訊:

  • Quality Gate。
  • JSON / FHIR R4 / TW Core 狀態。
  • Resource summary。
  • OperationOutcome issues。
  • Exchange contract rule results。
  • Contract comparison。
  • 升級後新增阻擋的 Expected / Actual 證據。

問題是這些區塊比較像陸續加上去的結果。
使用者可以看到很多表,但不一定能立刻理解它們共同構成一份品質測試報告。

Day 19 把結果區統一成:

Quality Test Report

最上方先放:

Overall Quality Gate
Input summary
Layer summary

Layer summary 以驗證層為列,整理:

  • Layer。
  • Status。
  • Blocking?
  • Evidence。
  • Notes。

https://ithelp.ithome.com.tw/upload/images/20260820/201779136OWISm0iZG.png

Contract version comparison:說明版本影響

契約版本比較同一份 Bundle 會用 v1.0 和 v1.1 各跑一次。
如果兩個版本結果相同,畫面顯示:

No impact

如果 v1.1 讓 Gate outcome 改變,或新增 v1.1 才失敗的交換規則,畫面顯示:

Impact

例如 UCUM system/code 在 v1.0 還不檢查,但 v1.1 新增 LAB-UNIT-002 後會出現新的交換規則失敗:

v1.0 -> no LAB-UNIT-002 failure
v1.1 -> LAB-UNIT-002 failed
Failed rule: LAB-UNIT-002
Blocking reason: Exchange contract rule failed.

下面的 Upgrade blocker evidence 會列出可修正的 Expected / Actual 證據。

https://ithelp.ithome.com.tw/upload/images/20260820/20177913HPRSP2YqgJ.png

https://ithelp.ithome.com.tw/upload/images/20260820/20177913I1XGqLdzJn.png

自動化驗證

本機 Maven 測試:

./mvnw test

結果:

Tests run: 60, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

https://ithelp.ithome.com.tw/upload/images/20260820/20177913zUbUWv5bQl.png

這次測試確認:

  • 六條交換契約規則的單元測試維持通過。
  • BundleParseService 的分層驗證與 Quality Gate 測試維持通過。
  • ContractComparisonService 的 v1.0 / v1.1 差異測試維持通過。
  • ContractScenarioCaseTests 的 4 組代表案例維持通過。
  • Controller 顯示測試確認 Quality Test Report、分層 summary、契約規則證據與版本比較仍會顯示。

常見錯誤 & 排查

  1. 把 TW Core validation 放進六條規則矩陣

TW Core validation 不是交換契約規則。
它是 Profile validation layer。
所以應該放在分層驗證覆蓋表,不放進六條規則矩陣。

  1. 把版本比較塞進規則矩陣

契約版本比較不是第七條規則。
它是同一份 Bundle 在不同 contract version 下的情境比較。
所以應該放在契約版本情境覆蓋表。

  1. NOT_APPLICABLE 寫成 PASS

非 Quantity Observation 對 unit 規則不是通過。
它是規則不適用。
如果寫成 PASS,會讓讀者誤以為 unit 條件被檢查過。

今天完成了什麼

  • 整理六條交換契約規則覆蓋矩陣。
  • 整理分層驗證覆蓋表。
  • 整理契約版本情境覆蓋表。
  • 確認六條交換契約規則都有獨立 FAIL 證據。
  • 確認 Reference 類規則的 NOT_EVALUATED 來自外部 reference 邊界。
  • 確認 LOINC / UCUM 規則目前沒有 NOT_EVALUATED 是設計邊界。
  • 首頁結果區統一成 Quality Test Report
  • 首頁文案和 validation message 改成 English-first。
  • Controller 測試補上 Quality Test Report、layer labels、scroller 與版本比較斷言。
  • 補上 LAB-REF-001 的獨立 NOT_APPLICABLE 小測試。
  • ./mvnw test 通過,測試數 60。

Day 19 尚未處理:

  • 完整 UI redesign 或 dashboard。
  • 完整 terminology validation。
  • 外部 FHIR Server reference 查詢。
  • Change Manifest。
  • COMPATIBLE / EXPECTED_BREAKING_CHANGE / UNEXPECTED_REGRESSION 三分類。
  • History 或完整契約治理。

目前的 MVP 進度:

validation-flow
├─ JSON parse                                  完成
├─ FHIR R4 parse                               完成
├─ FHIR R4 validation                          完成
├─ TW Core validation / safe NOT_EVALUATED      完成
├─ Exchange contract rules
│  ├─ LAB-REF-001                              完成並覆蓋四態
│  ├─ LAB-REF-002                              完成並覆蓋四態
│  ├─ LAB-REF-003                              完成並覆蓋四態
│  ├─ LAB-CODE-001                             完成,NOT_EVALUATED 不適用於目前設計
│  ├─ LAB-UNIT-001                             完成,NOT_EVALUATED 不適用於目前設計
│  └─ LAB-UNIT-002                             完成,NOT_EVALUATED 不適用於目前設計
├─ Quality Gate                                完成最小版
├─ Contract comparison
│  ├─ ContractVersion                          完成最小版
│  ├─ v1.0 / v1.1 rule selection                完成最小版
│  ├─ comparison service test                   完成最小版
│  ├─ homepage comparison display               完成最小版
│  └─ upgrade blocker evidence display          完成最小版
├─ Scenario test pack
│  ├─ v1.0 / v1.1 representative cases          完成 4 例
│  └─ NOT_APPLICABLE scenario fixture           完成 1 例
├─ Coverage evidence
│  ├─ six-rule coverage matrix                  完成最小版
│  ├─ layer coverage table                      完成最小版
│  └─ contract version scenario coverage         完成最小版
├─ Homepage Quality Test Report                 完成最小版
└─ Reproducible delivery
   ├─ Dockerfile                                完成最小版
   ├─ Docker Compose                            完成最小版並驗證啟動
   └─ GitHub Actions CI                         完成最小版

下一步預計處理:

README 支援範圍與限制整理

Repository:twcore-data-quality-gate


上一篇
Day18 - 把驗證流程放進 Docker 與 CI quality gate
下一篇
Day20 - 將合作方交換要求抽成可載入 Contract
系列文
醫療資料通過標準驗證,就真的能交換嗎?——30 天打造 TW Core 資料品質閘門20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言