Files
claude-mode/tests/cli/test_sessions.sh
T
smoidoandClaude Opus 5 51e42823d1 Tests, licence, and the tooling to keep the repo consistent
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>
2026-09-15 01:42:38 +03:00

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