Files
gitea/tools/check-contents-format.sh
jiantw83 c7c14445e6 test(check-contents-format): 新增目錄頁區塊格式的離線驗證
新增一支只讀寫暫存檔的驗證腳本,全程走 format 子命令,涵蓋五種舊頁狀態:
純表格、已是條列且鍵命中、已是條列且鍵未命中、表格與區塊混合、用範本建
新頁。另外驗身分欄取標題的四種情形與引言的五種情形。

轉檔與 upsert 的錯法都是靜默的:標題取錯只會多長一個區塊,引言沒換掉只是
說明過期,兩種都不會報錯,要等到線上頁面壞掉才看得出來。手動打 API 驗又
會在正式頁上留下試出來的垃圾紀錄。

驗證只比對輸入與輸出檔,不碰網路,任何機器上都跑得完。除了正常流程還特別
釘住三件事:表格取不出鍵那一列要擋下來不猜標題、轉檔後拿結果重跑同一筆
必須一字不變、區塊檔沒帶標題也照樣補上。任一項不符就印出期望值與實際值
之後立刻停住,不續跑其餘項目。

功能範圍:目錄頁版面改版的迴歸防護。
2026-09-02 17:22:18 +08:00

519 lines
15 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env sh
# check-contents-format.sh — 驗證目錄頁的區塊轉檔與 upsert 規則。
# 全部走 wiki-contents.sh 的 format 子命令,只讀寫暫存檔,不打任何 API。
# 涵蓋五種舊頁狀態:純表格、已是條列且鍵命中、已是條列且鍵未命中、表格與區塊混合、
# 用範本建新頁。另外驗三件事:表格取不出鍵那一列要擋下來不猜標題、轉檔後重跑同一筆
# 結果一字不變、區塊檔沒帶標題也照樣補上。
# 轉檔取 H2 標題另外驗四種身分欄:連結文字不是頁名、連結文字剛好等於頁名、純文字沒有
# 連結、網址帶百分號編碼。四種都要取到真正的頁名,呼叫端那一筆才會是 updated。
# 引言另外驗五種情形:轉檔且有範本要換成範本那一份、轉檔但沒範本保留舊的、已是條列
# 加了範本也不准動引言、--fresh 建新頁照舊、拿轉檔結果重跑引言不再變動。
# 全部通過印 OK 並 exit 0;任一項不符印出 want 與 got 並 exit 1。
# 結束碼: 0=全部通過,stdout 印 OK
# 1=有一項不符,stderr 印出 {項目}: want=… got=… 之後立刻停住,不續跑其餘項目。
# 只有 0 與 1 兩種;這支不吃參數,也沒有用法錯誤那條路。
set -eu
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
contents="$dir/wiki-contents.sh"
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
fail() {
printf '%s\n' "$1" >&2
exit 1
}
expect_eq() { # got want label
[ "$1" = "$2" ] || fail "$(printf '%s: want=\n%s\ngot=\n%s' "$3" "$2" "$1")"
}
# run <label> <keycol> <key> <fresh|keep> [範本檔] — 讀 $work/old 與 $work/entry,
# 結果留在 $work/new,動作留在 $work/action。
run() {
rc=0
if [ "$4" = fresh ]; then
sh "$contents" format "$2" "$3" "$work/entry" "$work/old" "$work/new" --fresh > "$work/action" || rc=$?
elif [ -n "${5-}" ]; then
sh "$contents" format "$2" "$3" "$work/entry" "$work/old" "$work/new" "$5" > "$work/action" || rc=$?
else
sh "$contents" format "$2" "$3" "$work/entry" "$work/old" "$work/new" > "$work/action" || rc=$?
fi
[ "$rc" -eq 0 ] || fail "$1: want exit=0 got exit=$rc"
}
# ---- 1. 純表格舊頁:整頁轉成區塊,再換掉鍵相同那一筆 ----
cat > "$work/old" <<'EOF'
# 日誌目錄
> 由 `jsc-log:worklog` 維護。一個區塊代表一個日誌頁。
| 日誌頁 | 存取庫 | 條目數 |
| --- | --- | --- |
| [LOG_AAA](https://example.test/wiki/LOG_AAA) | plugins/meta | 3 |
| [LOG_BBB](https://example.test/wiki/LOG_BBB) | plugins/ask | 5 |
EOF
cat > "$work/entry" <<'EOF'
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
- 條目數:9
EOF
run 'table page' 1 LOG_BBB keep
expect_eq "$(cat "$work/action")" updated 'table page action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 由 `jsc-log:worklog` 維護。一個區塊代表一個日誌頁。
## LOG_AAA
- 日誌頁:[LOG_AAA](https://example.test/wiki/LOG_AAA)
- 存取庫:plugins/meta
- 條目數:3
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
- 條目數:9' 'table page'
# ---- 2. 已是條列,鍵命中:只換那一塊,別人那一筆一字不動 ----
cat > "$work/old" <<'EOF'
# 日誌目錄
> 引言。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
## LOG_BBB
- 存取庫:plugins/ask
- 條目數:5
EOF
cat > "$work/entry" <<'EOF'
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:7
EOF
run 'list hit' 1 LOG_AAA keep
expect_eq "$(cat "$work/action")" updated 'list hit action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 引言。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:7
## LOG_BBB
- 存取庫:plugins/ask
- 條目數:5' 'list hit'
# ---- 3. 已是條列,鍵未命中:附加到頁尾,前面的區塊照舊 ----
cat > "$work/entry" <<'EOF'
## LOG_CCC
- 存取庫:plugins/git
- 條目數:1
EOF
run 'list miss' 1 LOG_CCC keep
expect_eq "$(cat "$work/action")" added 'list miss action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 引言。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
## LOG_BBB
- 存取庫:plugins/ask
- 條目數:5
## LOG_CCC
- 存取庫:plugins/git
- 條目數:1' 'list miss'
# ---- 4. 表格與區塊混合:表格轉出來的區塊接在既有區塊後面 ----
cat > "$work/old" <<'EOF'
# 日誌目錄
> 引言。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
| 日誌頁 | 存取庫 |
| --- | --- |
| [LOG_BBB](https://example.test/wiki/LOG_BBB) | plugins/ask |
EOF
cat > "$work/entry" <<'EOF'
## LOG_CCC
- 日誌頁:[LOG_CCC](https://example.test/wiki/LOG_CCC)
- 存取庫:plugins/git
EOF
run 'mixed page' 1 LOG_CCC keep
expect_eq "$(cat "$work/action")" added 'mixed page action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 引言。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
## LOG_CCC
- 日誌頁:[LOG_CCC](https://example.test/wiki/LOG_CCC)
- 存取庫:plugins/git' 'mixed page'
# ---- 5. 用範本建新頁:示範區塊與示範表格都要剝掉,只留 H1 與引言 ----
cat > "$work/old" <<'EOF'
# 日誌目錄
> 由 `jsc-log:worklog` 維護。一個區塊代表一個日誌頁。
## {日誌頁頁名}
- 存取庫:{owner}/{repo}
- 條目數:{數字}
EOF
cat > "$work/entry" <<'EOF'
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
EOF
run 'fresh page' 1 LOG_AAA fresh
expect_eq "$(cat "$work/action")" added 'fresh page action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 由 `jsc-log:worklog` 維護。一個區塊代表一個日誌頁。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3' 'fresh page'
# ---- 6. 表格那一列取不出鍵:擋下來回 1,不猜標題 ----
cat > "$work/old" <<'EOF'
# 日誌目錄
> 引言。
| 日誌頁 | 存取庫 |
| --- | --- |
| | plugins/ask |
EOF
cat > "$work/entry" <<'EOF'
## LOG_AAA
- 存取庫:plugins/meta
EOF
if sh "$contents" format 1 LOG_AAA "$work/entry" "$work/old" "$work/new" >/dev/null 2>&1; then
code=0
else
code=$?
fi
[ "$code" -eq 1 ] || fail "keyless table row: want exit=1 got exit=$code"
# ---- 7. 轉檔後重跑同一筆:結果一字不變,程式碼圍欄不被當成表格拆掉 ----
cat > "$work/old" <<'EOF'
# 監控目錄
> 引言。
```mermaid
flowchart LR
A --> B
```
| 監控頁 | 主機 |
| --- | --- |
| `MONITOR_AAA` | myhost |
EOF
cat > "$work/entry" <<'EOF'
## MONITOR_AAA
- 監控頁:[MONITOR_AAA](https://example.test/wiki/MONITOR_AAA)
- 主機:myhost
EOF
run 'converted once' 1 MONITOR_AAA keep
expect_eq "$(cat "$work/action")" updated 'converted once action'
expect_eq "$(cat "$work/new")" '# 監控目錄
> 引言。
```mermaid
flowchart LR
A --> B
```
## MONITOR_AAA
- 監控頁:[MONITOR_AAA](https://example.test/wiki/MONITOR_AAA)
- 主機:myhost' 'converted once'
cp "$work/new" "$work/old"
run 'converted twice' 1 MONITOR_AAA keep
expect_eq "$(cat "$work/action")" updated 'converted twice action'
expect_eq "$(cat "$work/new")" "$(cat "$work/old")" 'converted twice'
# ---- 8. 區塊檔沒帶「## 」標題:照樣補上,鍵就是標題 ----
cat > "$work/old" <<'EOF'
# 監控目錄
> 引言。
EOF
cat > "$work/entry" <<'EOF'
- 主機:myhost
EOF
run 'headless entry' 1 MONITOR_BBB keep
expect_eq "$(cat "$work/action")" added 'headless entry action'
expect_eq "$(cat "$work/new")" '# 監控目錄
> 引言。
## MONITOR_BBB
- 主機:myhost' 'headless entry'
# ---- 9. 連結文字不是頁名:標題取網址最後一段,這一筆算 updated 不是 added ----
# 頁名一律走變數帶進來。這支腳本的每一行「## 開頭」都會被註解掃描器當成註解讀,
# 頁名直接寫在那種行上就會被判成註解夾帶頁面編號。
page='ANALYZE_20260821_100552_104F0709'
cat > "$work/old" <<EOF
# 分析目錄
> 引言。
| 計畫名稱 | 分析頁 | HASH |
| --- | --- | --- |
| 某計畫 | [假 CLI 核心程式庫](https://example.test/knowledges/ANALYZE/wiki/$page) | 104F0709 |
EOF
printf '## %s\n\n- 計畫名稱:某計畫\n' "$page" > "$work/entry"
run 'link text differs' 2 "$page" keep
expect_eq "$(cat "$work/action")" updated 'link text differs action'
expect_eq "$(cat "$work/new")" "$(printf '# 分析目錄\n\n> 引言。\n\n## %s\n\n- 計畫名稱:某計畫' "$page")" 'link text differs'
# ---- 10. 轉檔後重跑同一筆:仍是 updated,不多出區塊 ----
cp "$work/new" "$work/old"
run 'link text differs twice' 2 "$page" keep
expect_eq "$(cat "$work/action")" updated 'link text differs twice action'
expect_eq "$(cat "$work/new")" "$(cat "$work/old")" 'link text differs twice'
# ---- 11. 連結文字剛好等於頁名:結果與連結文字不是頁名時一致 ----
page='PLAN_104F0709'
cat > "$work/old" <<EOF
# 計畫目錄
> 引言。
| 計畫頁 | 狀態 |
| --- | --- |
| [$page](https://example.test/knowledges/PLAN/wiki/$page) | 進行中 |
EOF
printf '## %s\n\n- 狀態:已完成\n' "$page" > "$work/entry"
run 'link text equals page' 1 "$page" keep
expect_eq "$(cat "$work/action")" updated 'link text equals page action'
expect_eq "$(cat "$work/new")" "$(printf '# 計畫目錄\n\n> 引言。\n\n## %s\n\n- 狀態:已完成' "$page")" 'link text equals page'
# ---- 12. 身分欄是純文字沒有連結:標題就是那段文字 ----
page='plugins/meta'
cat > "$work/old" <<EOF
# 維護目錄
> 引言。
| 存取庫 | 維護期限 |
| --- | --- |
| \`$page\` | 2026-12-31 |
EOF
printf '## %s\n\n- 維護期限:2027-06-30\n' "$page" > "$work/entry"
run 'plain identity cell' 1 "$page" keep
expect_eq "$(cat "$work/action")" updated 'plain identity cell action'
expect_eq "$(cat "$work/new")" "$(printf '# 維護目錄\n\n> 引言。\n\n## %s\n\n- 維護期限:2027-06-30' "$page")" 'plain identity cell'
# ---- 13. 網址帶百分號編碼:解碼後取最後一段 ----
page='ANALYZE_中文'
cat > "$work/old" <<'EOF'
# 分析目錄
> 引言。
| 分析頁 | HASH |
| --- | --- |
| [某工作包](https://example.test/knowledges/ANALYZE/wiki/ANALYZE_%E4%B8%AD%E6%96%87) | 104F0709 |
EOF
printf '## %s\n\n- HASH:104F0709\n' "$page" > "$work/entry"
run 'percent encoded url' 1 "$page" keep
expect_eq "$(cat "$work/action")" updated 'percent encoded url action'
expect_eq "$(cat "$work/new")" "$(printf '# 分析目錄\n\n> 引言。\n\n## %s\n\n- HASH:104F0709' "$page")" 'percent encoded url'
# ---- 14. 轉檔且有範本:引言換成範本那一份,紀錄區塊一筆不少、順序照舊 ----
# 範本的示範區塊不得混進來,所以範本檔也放一個示範區塊。
cat > "$work/tpl" <<'EOF'
# 日誌目錄
> 由 `jsc-log:worklog` 維護。每個存取庫一個區塊;頁面上不留 markdown 表格。
>
> 版面:H1 頁名、這段引言,然後一筆紀錄一個 H2 區塊。
## {日誌頁頁名}
- 存取庫:{owner}/{repo}
- 條目數:{數字}
EOF
cat > "$work/old" <<'EOF'
# 日誌目錄
> 由 `jsc-log:worklog` 維護。每個存取庫一列;`{HASH}` 為 SHA-1 前 8 碼。
| 日誌頁 | 存取庫 | 條目數 |
| --- | --- | --- |
| [LOG_AAA](https://example.test/wiki/LOG_AAA) | plugins/meta | 3 |
| [LOG_BBB](https://example.test/wiki/LOG_BBB) | plugins/ask | 5 |
EOF
cat > "$work/entry" <<'EOF'
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
- 條目數:9
EOF
run 'converting with template' 1 LOG_BBB keep "$work/tpl"
expect_eq "$(cat "$work/action")" updated 'converting with template action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 由 `jsc-log:worklog` 維護。每個存取庫一個區塊;頁面上不留 markdown 表格。
>
> 版面:H1 頁名、這段引言,然後一筆紀錄一個 H2 區塊。
## LOG_AAA
- 日誌頁:[LOG_AAA](https://example.test/wiki/LOG_AAA)
- 存取庫:plugins/meta
- 條目數:3
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
- 條目數:9' 'converting with template'
expect_eq "$(grep -c '^## ' "$work/new")" 2 'converting with template block count'
# ---- 15. 轉檔但沒給範本:沒有正本可換,引言保留舊的 ----
run 'converting without template' 1 LOG_BBB keep
expect_eq "$(cat "$work/action")" updated 'converting without template action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 由 `jsc-log:worklog` 維護。每個存取庫一列;`{HASH}` 為 SHA-1 前 8 碼。
## LOG_AAA
- 日誌頁:[LOG_AAA](https://example.test/wiki/LOG_AAA)
- 存取庫:plugins/meta
- 條目數:3
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
- 條目數:9' 'converting without template'
# ---- 16. 已是條列還給了範本:不必轉檔,引言一字不動 ----
# 這一筆只是更新自己那一塊,沒有理由改掉別人寫的散文。
cat > "$work/old" <<'EOF'
# 日誌目錄
> 這段引言是頁面主人自己寫的,不准被範本蓋掉。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
EOF
cat > "$work/entry" <<'EOF'
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:7
EOF
run 'list page with template' 1 LOG_AAA keep "$work/tpl"
expect_eq "$(cat "$work/action")" updated 'list page with template action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 這段引言是頁面主人自己寫的,不准被範本蓋掉。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:7' 'list page with template'
# ---- 17. --fresh 建新頁:整份用範本,示範區塊剝掉,行為與加了範本參數之前一樣 ----
cp "$work/tpl" "$work/old"
cat > "$work/entry" <<'EOF'
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3
EOF
run 'fresh page with template intro' 1 LOG_AAA fresh
expect_eq "$(cat "$work/action")" added 'fresh page with template intro action'
expect_eq "$(cat "$work/new")" '# 日誌目錄
> 由 `jsc-log:worklog` 維護。每個存取庫一個區塊;頁面上不留 markdown 表格。
>
> 版面:H1 頁名、這段引言,然後一筆紀錄一個 H2 區塊。
## LOG_AAA
- 存取庫:plugins/meta
- 條目數:3' 'fresh page with template intro'
# ---- 18. 拿轉檔結果再跑同一筆:已經沒有表格,引言不再變動 ----
cat > "$work/old" <<'EOF'
# 日誌目錄
> 由 `jsc-log:worklog` 維護。每個存取庫一列;`{HASH}` 為 SHA-1 前 8 碼。
| 日誌頁 | 存取庫 | 條目數 |
| --- | --- | --- |
| [LOG_AAA](https://example.test/wiki/LOG_AAA) | plugins/meta | 3 |
| [LOG_BBB](https://example.test/wiki/LOG_BBB) | plugins/ask | 5 |
EOF
cat > "$work/entry" <<'EOF'
## LOG_BBB
- 日誌頁:[LOG_BBB](https://example.test/wiki/LOG_BBB)
- 存取庫:plugins/ask
- 條目數:9
EOF
run 'template intro once' 1 LOG_BBB keep "$work/tpl"
cp "$work/new" "$work/old"
run 'template intro twice' 1 LOG_BBB keep "$work/tpl"
expect_eq "$(cat "$work/action")" updated 'template intro twice action'
expect_eq "$(cat "$work/new")" "$(cat "$work/old")" 'template intro twice'
printf '%s\n' 'OK'