session-tools/tests/test_rclaude_helpers.sh

172 lines
6.6 KiB
Bash
Raw Permalink Normal View History

# test_rclaude_helpers.sh — unit tests for rclaude's pure helpers.
#
# Strategy: source rclaude with a guard so the dispatch block doesn't fire,
# then call the individual helpers directly. The guard is `RCLAUDE_LIB_ONLY=1`
# — rclaude checks it at the top of its dispatch and returns early.
# Source rclaude as a library. The dispatch block is bypassed by the guard.
RCLAUDE_LIB_ONLY=1 . "$ROOT/bin/rclaude" 2>/dev/null || true
# ---------------------------------------------------------------------------
# claude_slug
# ---------------------------------------------------------------------------
test_claude_slug_basic() {
assert_eq "-Users-natalie-Code--projects--lilith" \
"$(claude_slug "/Users/natalie/Code/@projects/@lilith")"
}
test_claude_slug_no_special() {
# Leading `/` becomes leading `-` (claude's own behavior — every
# non-alphanumeric char is replaced, including the leading slash).
assert_eq "-tmp-foo" "$(claude_slug "/tmp/foo")"
}
test_claude_slug_empty() {
assert_eq "" "$(claude_slug "")"
}
# ---------------------------------------------------------------------------
# is_local
# ---------------------------------------------------------------------------
test_is_local_keywords() {
assert_exit 0 is_local "local"
assert_exit 0 is_local "localhost"
assert_exit 0 is_local "127.0.0.1"
assert_exit 0 is_local "::1"
}
test_is_local_unknown_host() {
assert_exit 1 is_local "definitely-not-a-real-host-12345"
}
# ---------------------------------------------------------------------------
# dedupe_sessions (keeps highest-mtime row per uuid)
# ---------------------------------------------------------------------------
test_dedupe_sessions_keeps_newest() {
# Two rows with the same uuid (col 3), different mtimes (col 6).
# Should retain only the row with the higher mtime.
_in=$(printf 'apricot\tsession\tUUID-A\tsnip\tcwd\t100\nlocal\tsession\tUUID-A\tsnip2\tcwd\t200\n')
_out=$(printf '%s' "$_in" | dedupe_sessions)
_count=$(printf '%s\n' "$_out" | wc -l | tr -d ' ')
assert_eq "1" "$_count" "expected 1 deduped row" || return 1
assert_contains "$_out" "200" "should keep mtime=200 row" || return 1
}
test_dedupe_sessions_passes_unique() {
_in=$(printf 'apricot\tsession\tA\ts\tc\t100\nlocal\tsession\tB\ts\tc\t100\n')
_out=$(printf '%s' "$_in" | dedupe_sessions)
_count=$(printf '%s\n' "$_out" | wc -l | tr -d ' ')
assert_eq "2" "$_count"
}
# ---------------------------------------------------------------------------
# get_home — always returns 0 even on failure (regression test)
# ---------------------------------------------------------------------------
test_get_home_unknown_returns_zero() {
# Use a clearly invalid host. The function must not abort `set -e`
# callers; previously this caused silent exits in cmd_resume.
_v=$(get_home "definitely-not-a-real-host-12345-zzz" 2>/dev/null)
_rc=$?
assert_eq "0" "$_rc" "get_home must return 0 on failure" || return 1
assert_eq "" "$_v" "should produce empty stdout on failure" || return 1
}
test_get_home_local_returns_HOME() {
assert_eq "$HOME" "$(get_home local)"
}
# ---------------------------------------------------------------------------
# caller_hostname — used by build_inner to tell remote MCPs where to send
# things back (audio playback, etc.)
# ---------------------------------------------------------------------------
test_caller_hostname_env_override() {
assert_eq "wg1.10.9.0.3" "$(RCLAUDE_BACK_HOST=wg1.10.9.0.3 caller_hostname)"
}
test_caller_hostname_default_adds_lan() {
# Default behavior should produce a fqdn-ish form (either already
# has a dot, or .lan got appended). We don't assert the exact host,
# just that the result is non-empty and dotted.
_out=$(unset RCLAUDE_BACK_HOST; caller_hostname)
assert_contains "$_out" "." "caller_hostname output should be dotted"
}
# ---------------------------------------------------------------------------
# sh_quote — POSIX single-quote escape for safe remote shell interpolation
# ---------------------------------------------------------------------------
test_sh_quote_empty() {
assert_eq "''" "$(sh_quote "")"
}
test_sh_quote_plain() {
assert_eq "'hello'" "$(sh_quote "hello")"
}
test_sh_quote_spaces() {
assert_eq "'hello world'" "$(sh_quote "hello world")"
}
test_sh_quote_dollar_passthrough() {
# `$HOME` inside single quotes must remain literal — that's the whole point.
assert_eq "'\$HOME'" "$(sh_quote '$HOME')"
}
test_sh_quote_embedded_single_quote() {
# The classic '\'' escape: close, escape, reopen.
assert_eq "'it'\\''s'" "$(sh_quote "it's")"
}
# ---------------------------------------------------------------------------
# filter_targets — selector-based row filter for `rclaude send`
# ---------------------------------------------------------------------------
# Two tmux rows + one disk row. Disk rows must always be dropped regardless
# of selector (can't send-keys to on-disk sessions). Col 5 is populated on
# one row to exercise the cwd-match branch.
_targets_fixture() {
printf 'local\ttmux\tclaude-natalie-lilith-123\t1 windows\n'
printf 'apricot\ttmux\tclaude-natalie-scripts-456\t2 windows\t/home/natalie/Code/@scripts\n'
printf 'apricot\tdisk\t/home/natalie/Code/@projects/@lilith\tsessions=3\n'
}
test_filter_targets_all_drops_disk() {
_out=$(_targets_fixture | filter_targets all "")
_count=$(printf '%s\n' "$_out" | grep -c '^' || true)
assert_eq "2" "$_count" "all selector keeps both tmux rows, drops disk"
}
test_filter_targets_host_exact() {
_out=$(_targets_fixture | filter_targets host apricot)
_count=$(printf '%s\n' "$_out" | grep -c '^' || true)
assert_eq "1" "$_count" "host=apricot matches one row" || return 1
assert_contains "$_out" "claude-natalie-scripts-456"
}
test_filter_targets_match_session_name() {
_out=$(_targets_fixture | filter_targets match lilith)
assert_contains "$_out" "claude-natalie-lilith-123" "lilith matches session name"
}
test_filter_targets_match_cwd_column() {
# `@scripts` only appears in col 5 of the apricot row (the session-name
# slug strips @ → -). The row must still match via the col-5 cwd branch.
_out=$(_targets_fixture | filter_targets match "@scripts")
assert_contains "$_out" "claude-natalie-scripts-456" "@scripts matches via cwd col"
}
test_filter_targets_match_no_match() {
_out=$(_targets_fixture | filter_targets match nonexistent-pattern-xyz)
assert_eq "" "$_out" "no match → empty output"
}
test_filter_targets_empty_input() {
_out=$(printf '' | filter_targets all "")
assert_eq "" "$_out" "empty input → empty output"
}