fbc56b9e59
同一台 Mac 有 CLI 與 Desktop 內建兩個 Claude Code,讀同一份 ~/.claude 卻不一定同結論: settings 多了 path 時 Desktop 2.1.266 忽略 inkstone marketplace、CLI 2.1.220 照常載入(c6991)。 第一版看到 path 就對整台判 ❌,在 CLI 上是假陽性。 - 逐一找 CLI(PATH)與 Desktop(最新版本,數字排序),各跑 plugin list --json,依 errors 各判各的 - 設定比對只在某一個真的報錯時當解釋+修法,永遠不單獨判 ❌ - 查不了 ⇒ ◐、離開碼 2,不講成 ✅ 或 ❌ - test-isep-doctor.sh 離線 11 條(假 claude 可執行檔);舊版跑這份 pass=1 fail=10 - test-isep-doctor-live.sh 實機 3 條:真的 CLI 2.1.220/Desktop 2.1.266,隔離 HOME 重現有/無 path,不動 ~/.claude - 盤點表與 plugin.json 描述的腳本數 62→65(這棵樹上實數) 版本號待總管定。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
96 lines
5.6 KiB
Bash
Executable File
96 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 迴歸測試:scripts/isep-doctor(inkstone/ISEP#150)——全離線
|
||
# 用假的 claude 可執行檔(shell 腳本)代替真的 CLI/Desktop,走的是 doctor 真正的 subprocess 那條路。
|
||
# 真的 Claude Code 版本的實測在 scripts/test-isep-doctor-live.sh。
|
||
set -u
|
||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
||
pass=0; fail=0
|
||
URL=https://git.uncle6.me/inkstone/ISEP.git
|
||
|
||
mkfake() { # 路徑 版本 plugin-list 輸出(原樣印出)
|
||
mkdir -p "$(dirname "$1")"
|
||
printf '%s' "$3" > "$1.out"
|
||
cat > "$1" <<EOF
|
||
#!/bin/sh
|
||
if [ "\$1" = "--version" ]; then echo "$2 (Claude Code)"; exit 0; fi
|
||
cat "$1.out"
|
||
EOF
|
||
chmod +x "$1"
|
||
}
|
||
run() { OUT=$(ISEP_DOCTOR_CLAUDE_HOME="$T/home" python3 "$HERE/isep-doctor" 2>&1); CODE=$?; }
|
||
ok() { pass=$((pass+1)); echo "ok $1"; }
|
||
bad() { fail=$((fail+1)); echo "FAIL $1 (exit=$CODE)"; printf '%s\n' "$OUT" | sed 's/^/ /'; }
|
||
expect() { # 名稱 離開碼 要有的樣式(| 分隔,可空) 不准有的樣式(可空)
|
||
local p miss=""
|
||
[ "$CODE" = "$2" ] || miss="exit"
|
||
local IFS='|'
|
||
for p in $3; do printf '%s' "$OUT" | grep -q -- "$p" || miss="$miss 缺[$p]"; done
|
||
if [ -n "${4:-}" ] && printf '%s' "$OUT" | grep -q -- "$4"; then miss="$miss 多了[$4]"; fi
|
||
[ -z "$miss" ] && ok "$1" || { bad "$1 →$miss"; }
|
||
}
|
||
|
||
mkdir -p "$T/home/plugins"
|
||
echo "{\"inkstone\":{\"source\":{\"source\":\"git\",\"url\":\"$URL\"}}}" > "$T/home/plugins/known_marketplaces.json"
|
||
SET_PATH="{\"extraKnownMarketplaces\":{\"inkstone\":{\"source\":{\"source\":\"git\",\"url\":\"$URL\",\"path\":\"/Users/x/ISEP\"}}},\"model\":\"keep\"}"
|
||
SET_CLEAN="{\"extraKnownMarketplaces\":{\"inkstone\":{\"source\":{\"source\":\"git\",\"url\":\"$URL\"}}}}"
|
||
LOADED='[{"id":"isep@inkstone","version":"0.30.1","enabled":true}]'
|
||
NOTFOUND='[{"id":"isep@inkstone","version":"0.30.1","enabled":true,"errors":["Marketplace inkstone not found"]}]'
|
||
|
||
# ① 假陽性(c6991):設定有 path,但這個 Claude Code 自己回報載入正常 → ✅、不准點名 path
|
||
echo "$SET_PATH" > "$T/home/settings.json"
|
||
mkfake "$T/b/cli" 2.1.220 "$LOADED"
|
||
export ISEP_DOCTOR_CLAUDE_BINS="CLI=$T/b/cli"; run
|
||
expect "① 有 path 但實際載入(2.1.220 形狀)→ ✅ 不點名 path" 0 "✅ CLI 2.1.220" "path"
|
||
|
||
# ② 真陽性(c6990):同一份設定,CLI 載得進、Desktop 載不進 → 1;兩邊各講各的;點名 path;給修法
|
||
mkfake "$T/b/desk" 2.1.266 "$NOTFOUND"
|
||
export ISEP_DOCTOR_CLAUDE_BINS="CLI=$T/b/cli:Desktop=$T/b/desk"; run
|
||
expect "② CLI 載得進、Desktop 載不進 → 1,逐一講、點名 path" 1 "✅ CLI 2.1.220|❌ Desktop 2.1.266|Marketplace inkstone not found|多了 path|^ python3 "
|
||
printf '%s' "$OUT" | grep -q "CLI 與 Desktop 都一樣" && bad "② 不准再說 CLI 與 Desktop 都一樣" || ok "② 沒有把整台機器講成同一個結論"
|
||
|
||
# ③ 真陰性:拿掉 path,兩邊都載入 → 0
|
||
echo "$SET_CLEAN" > "$T/home/settings.json"
|
||
mkfake "$T/b/desk" 2.1.266 "$LOADED"; run
|
||
expect "③ 沒有 path、兩邊都載入 → 0" 0 "✅ CLI 2.1.220|✅ Desktop 2.1.266" "❌"
|
||
|
||
# ④ 別的原因失敗(設定沒有多餘欄位)→ 1,照實轉述,不編原因、不給 path 修法
|
||
mkfake "$T/b/desk" 2.1.266 '[{"id":"isep@inkstone","version":"0.30.1","enabled":true,"errors":["Plugin directory missing"]}]'; run
|
||
expect "④ 其他錯誤照實轉述、不編原因" 1 "❌ Desktop|Plugin directory missing" "修法(一行"
|
||
|
||
# ⑤ 沒安裝 → 1
|
||
mkfake "$T/b/desk" 2.1.266 '[]'; run
|
||
expect "⑤ 沒安裝 → 1" 1 "❌ Desktop 2.1.266:isep@inkstone 沒有安裝"
|
||
|
||
# ⑥ 查不了(輸出不是 JSON)+另一個正常 → 2,◐,不准講成 ❌ 或 ✅
|
||
mkfake "$T/b/desk" 2.1.266 'Error: something broke'; run
|
||
expect "⑥ 一個查不了、一個正常 → 2" 2 "◐ Desktop|讀不到不等於沒載入|✅ CLI" "❌"
|
||
|
||
# ⑦ 一個 Claude Code 都找不到 → 2
|
||
export ISEP_DOCTOR_CLAUDE_BINS=""; run
|
||
expect "⑦ 找不到任何 Claude Code → 2" 2 "◐ 查不了"
|
||
|
||
# ⑧ 修法那一行真的修得好(在假 HOME 上原封不動執行印給 leo 的那一行)
|
||
echo "$SET_PATH" > "$T/home/settings.json"
|
||
mkfake "$T/b/desk" 2.1.266 "$NOTFOUND"
|
||
export ISEP_DOCTOR_CLAUDE_BINS="Desktop=$T/b/desk"; run
|
||
fixline=$(printf '%s\n' "$OUT" | grep '^ python3 ' | sed 's/^ //')
|
||
mkdir -p "$T/fake/.claude" && cp "$T/home/settings.json" "$T/fake/.claude/settings.json"
|
||
HOME="$T/fake" bash -c "$fixline"
|
||
if [ -n "$fixline" ] && python3 -c "import json,sys;d=json.load(open('$T/fake/.claude/settings.json'));s=d['extraKnownMarketplaces']['inkstone']['source'];sys.exit(0 if 'path' not in s and d['model']=='keep' and s['url'].endswith('ISEP.git') else 1)"; then
|
||
ok "⑧ 修法一行拿掉 path、其他設定不動"
|
||
else fail=$((fail+1)); echo "FAIL ⑧ 修法一行"; cat "$T/fake/.claude/settings.json"; fi
|
||
# ⑧b 修完再問一次(Desktop 改回報正常)→ 0:出路走得通
|
||
cp "$T/fake/.claude/settings.json" "$T/home/settings.json"; mkfake "$T/b/desk" 2.1.266 "$LOADED"; run
|
||
expect "⑧b 照修法修完再跑 → ✅" 0 "✅ Desktop" "多了 path"
|
||
|
||
# ⑨ 自動尋找:Desktop 取數字上最新的那一份(2.1.99 < 2.1.266,字串排序會選錯)
|
||
unset ISEP_DOCTOR_CLAUDE_BINS
|
||
D="$T/h9/Library/Application Support/Claude/claude-code"
|
||
mkfake "$D/2.1.99/claude.app/Contents/MacOS/claude" 2.1.99 "$NOTFOUND"
|
||
mkfake "$D/2.1.266/claude.app/Contents/MacOS/claude" 2.1.266 "$LOADED"
|
||
OUT=$(HOME="$T/h9" PATH="/usr/bin:/bin" ISEP_DOCTOR_CLAUDE_HOME="$T/home" /usr/bin/env python3 "$HERE/isep-doctor" 2>&1); CODE=$?
|
||
expect "⑨ Desktop 取最新版本(數字排序)" 0 "✅ Desktop 2.1.266" "2.1.99"
|
||
|
||
echo "pass=$pass fail=$fail"; [ "$fail" = 0 ]
|