A test suite with no dependencies beyond bash and the Python standard library, run by one command: scripts/test.sh (or: make test) scripts/test.sh --windows [host] adds the PowerShell suite over SSH - tests/static.sh: script syntax, JSON validity, VERSION against the widget manifest and CHANGELOG, providers.json against the kinds, probes and checks the code implements (and ids that would shadow a command), qmllint and shellcheck when installed. - tests/python: unit tests for cm-json.py - providers and the column contract bash depends on, the original three scaffolds byte for byte, every catalogue parser and the cache, session scan/dismiss/repair, rename, defaults, health, the cost guard. - tests/cli: the CLI in a sandbox with its own HOME, CM_ROOT, CLAUDE_CONFIG_DIR and a file-only vault, against fake Ollama, LM Studio, keyed-gateway and proxy servers - preflight, presets, models, doctor, a real switch, sessions, and the key helper under a rename race. - tests/windows: the same idea on a Windows host, with USERPROFILE pointed at a temp folder so the real install is never touched. Also: MIT licence, CHANGELOG.md reconstructed from history, .editorconfig and .gitattributes pinning LF everywhere (what the tree already is), and scripts/bump-version.sh, since the version lives in two files and drifted once before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
56 lines
2.5 KiB
Bash
Executable File
56 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Broken transcripts: the scan, dismissing and restoring, the age rule, repair.
|
|
. "$(dirname "$0")/../lib.sh"
|
|
|
|
proj="$CLAUDE_CONFIG_DIR/projects/-tmp-fixture"
|
|
mkdir -p "$proj"
|
|
transcript() { # <session-id> [age-in-hours]
|
|
printf '%s\n' \
|
|
'{"type":"assistant","message":{"id":"msg_1","model":"claude-opus-5","content":[{"type":"text","text":"hi"}]}}' \
|
|
'{"type":"assistant","message":{"id":"gen-1-a","model":"deepseek/x","content":[{"type":"text","text":"yo"}]}}' \
|
|
> "$proj/$1.jsonl"
|
|
python3 -c 'import os, sys, time; t = time.time() - float(sys.argv[2]) * 3600; os.utime(sys.argv[1], (t, t))' \
|
|
"$proj/$1.jsonl" "${2:-0}"
|
|
}
|
|
transcript aaaa-new
|
|
transcript bbbb-old 240
|
|
transcript dddd-fix 1
|
|
printf '%s\n' '{"type":"assistant","message":{"id":"msg_1"}}' > "$proj/cccc-ok.jsonl"
|
|
|
|
ids() { # <broken|ignored> - the session ids in that list of the scan
|
|
"$CM" repair-session --json "${@:2}" | python3 -c '
|
|
import json, sys
|
|
print(" ".join(x["sessionId"] for x in json.load(sys.stdin)[sys.argv[1]]))' "$1"
|
|
}
|
|
|
|
expect_eq "$(ids broken)" 'aaaa-new dddd-fix' 'broken transcripts are found'
|
|
expect_eq "$(ids ignored)" 'bbbb-old' 'one untouched for 10 days is hidden'
|
|
expect_eq "$(ids broken --max-age 0)" 'aaaa-new bbbb-old dddd-fix' '--max-age 0 shows all'
|
|
|
|
run_cm repair-session --ignore aaaa-new
|
|
expect_out 'hidden aaaa-new' 'a session is dismissed'
|
|
expect_eq "$(ids broken)" 'dddd-fix' 'and leaves the broken list'
|
|
run_cm repair-session --all
|
|
expect_out '2 hidden: 1 ignored, 1 older than 7 days' 'hidden sessions are always counted'
|
|
run_cm repair-session --ignored
|
|
expect_out 'still broken' 'the dismissed list reconciles against the disk'
|
|
run_cm repair-session --ignore zzzz
|
|
expect_rc 1 'an unknown session cannot be dismissed'
|
|
run_cm repair-session --unignore aaaa-new
|
|
expect_eq "$(ids broken)" 'aaaa-new dddd-fix' 'restoring brings it back'
|
|
|
|
run_cm repair-session --ignore dddd-fix
|
|
run_cm repair-session dddd-fix --apply
|
|
expect_rc 0 'a broken session is repaired'
|
|
expect_eq "$(ls "$proj" | grep -c 'dddd-fix.jsonl.pre-repair-backup')" 1 'the original is backed up'
|
|
expect_eq "$(ids ignored)" 'bbbb-old' 'a repair clears its dismissal'
|
|
|
|
run_cm repair-session --ignore bbbb-old
|
|
rm -f "$proj/bbbb-old.jsonl"
|
|
run_cm repair-session --ignore aaaa-new
|
|
expect_out 'forgot 1 whose transcript no longer exists' 'entries for deleted transcripts are pruned'
|
|
run_cm repair-session --unignore-all
|
|
expect_out 'restored every dismissed session' 'unignore-all empties the list'
|
|
|
|
finish
|