Move variable exports before first use in the #91 regression test to prevent "No such file or directory" failures in CI environments where setup() state may not persist. Co-authored-by: Test User <test@example.com>
654 lines
23 KiB
Bash
654 lines
23 KiB
Bash
#!/usr/bin/env bats
|
|
# Unit tests for session continuity enhancements
|
|
# TDD: Tests for session lifecycle management across Ralph loops
|
|
|
|
load '../helpers/test_helper'
|
|
load '../helpers/fixtures'
|
|
|
|
setup() {
|
|
# Create temporary test directory
|
|
TEST_DIR="$(mktemp -d)"
|
|
cd "$TEST_DIR"
|
|
|
|
# Initialize git repo
|
|
git init > /dev/null 2>&1
|
|
git config user.email "test@example.com"
|
|
git config user.name "Test User"
|
|
|
|
# Set up environment with .ralph/ subfolder structure
|
|
export RALPH_DIR=".ralph"
|
|
export PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
|
export LOG_DIR="$RALPH_DIR/logs"
|
|
export DOCS_DIR="$RALPH_DIR/docs/generated"
|
|
export STATUS_FILE="$RALPH_DIR/status.json"
|
|
export EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
|
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
|
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
|
export CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id"
|
|
export RALPH_SESSION_FILE="$RALPH_DIR/.ralph_session"
|
|
export RALPH_SESSION_HISTORY_FILE="$RALPH_DIR/.ralph_session_history"
|
|
export RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
|
export CLAUDE_MIN_VERSION="2.0.76"
|
|
export CLAUDE_CODE_CMD="claude"
|
|
export CLAUDE_USE_CONTINUE="true"
|
|
|
|
mkdir -p "$LOG_DIR" "$DOCS_DIR"
|
|
echo "0" > "$CALL_COUNT_FILE"
|
|
echo "$(date +%Y%m%d%H)" > "$TIMESTAMP_FILE"
|
|
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
|
|
|
# Create sample project files in .ralph/ directory
|
|
create_sample_prompt "$RALPH_DIR/PROMPT.md"
|
|
create_sample_fix_plan "$RALPH_DIR/fix_plan.md" 10 3
|
|
|
|
# Source library components
|
|
source "${BATS_TEST_DIRNAME}/../../lib/date_utils.sh"
|
|
source "${BATS_TEST_DIRNAME}/../../lib/response_analyzer.sh"
|
|
source "${BATS_TEST_DIRNAME}/../../lib/circuit_breaker.sh"
|
|
|
|
# Define color variables for log_status
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
PURPLE='\033[0;35m'
|
|
NC='\033[0m'
|
|
|
|
# Define log_status function for tests
|
|
log_status() {
|
|
local level=$1
|
|
local message=$2
|
|
echo "[$level] $message"
|
|
}
|
|
export -f log_status
|
|
}
|
|
|
|
teardown() {
|
|
if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
|
|
cd /
|
|
rm -rf "$TEST_DIR"
|
|
fi
|
|
}
|
|
|
|
# =============================================================================
|
|
# HELPER: Check if function exists in ralph_loop.sh
|
|
# =============================================================================
|
|
|
|
function_exists_in_ralph() {
|
|
local func_name=$1
|
|
grep -qE "^${func_name}\s*\(\)|^function\s+${func_name}" "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" 2>/dev/null
|
|
}
|
|
|
|
# =============================================================================
|
|
# SESSION RESET FUNCTION TESTS
|
|
# =============================================================================
|
|
|
|
@test "reset_session function exists in ralph_loop.sh" {
|
|
run function_exists_in_ralph "reset_session"
|
|
[[ $status -eq 0 ]] || skip "reset_session function not yet implemented"
|
|
}
|
|
|
|
@test "get_session_id function exists in ralph_loop.sh" {
|
|
run function_exists_in_ralph "get_session_id"
|
|
[[ $status -eq 0 ]] || skip "get_session_id function not yet implemented"
|
|
}
|
|
|
|
@test "log_session_transition function exists in ralph_loop.sh" {
|
|
run function_exists_in_ralph "log_session_transition"
|
|
[[ $status -eq 0 ]] || skip "log_session_transition function not yet implemented"
|
|
}
|
|
|
|
# =============================================================================
|
|
# --reset-session CLI FLAG TESTS
|
|
# =============================================================================
|
|
|
|
@test "--reset-session flag is recognized in help" {
|
|
run bash "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" --help
|
|
|
|
[[ "$output" == *"reset-session"* ]] || skip "--reset-session flag not yet implemented"
|
|
}
|
|
|
|
@test "--reset-session flag in argument parser" {
|
|
# Check if the flag exists in the argument parsing section
|
|
run grep -E '\-\-reset-session' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]] || skip "--reset-session flag not yet implemented"
|
|
}
|
|
|
|
@test "--reset-session resets session file" {
|
|
# Create a session file
|
|
echo '{"session_id": "session-to-reset", "timestamp": "2026-01-09T10:00:00Z"}' > "$RALPH_SESSION_FILE"
|
|
echo 'session-to-reset' > "$CLAUDE_SESSION_FILE"
|
|
|
|
# Run with --reset-session flag (should exit quickly)
|
|
run timeout 5 bash "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" --reset-session 2>&1
|
|
|
|
# If flag not recognized, skip
|
|
if [[ "$output" == *"Unknown option"* ]]; then
|
|
skip "--reset-session flag not yet implemented"
|
|
fi
|
|
|
|
# Check that session was reset
|
|
if [[ -f "$RALPH_SESSION_FILE" ]]; then
|
|
local session=$(jq -r '.session_id // ""' "$RALPH_SESSION_FILE" 2>/dev/null || echo "")
|
|
[[ -z "$session" || "$session" == "" || "$session" == "null" ]]
|
|
fi
|
|
}
|
|
|
|
# =============================================================================
|
|
# CIRCUIT BREAKER SESSION INTEGRATION TESTS
|
|
# =============================================================================
|
|
|
|
@test "circuit breaker reset code includes session reset" {
|
|
# Check if reset_circuit_breaker mentions reset_session
|
|
run grep -A10 'reset_circuit_breaker' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ "$output" == *"reset_session"* ]] || skip "Circuit breaker session integration not yet implemented"
|
|
}
|
|
|
|
@test "cleanup function includes session reset" {
|
|
# Check if cleanup function includes reset_session
|
|
run grep -A5 'cleanup()' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ "$output" == *"reset_session"* ]] || skip "Cleanup session reset not yet implemented"
|
|
}
|
|
|
|
# =============================================================================
|
|
# SESSION HISTORY TESTS
|
|
# =============================================================================
|
|
|
|
@test "RALPH_SESSION_HISTORY_FILE constant defined" {
|
|
run grep 'RALPH_SESSION_HISTORY_FILE' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]] || skip "Session history file constant not yet defined"
|
|
}
|
|
|
|
# =============================================================================
|
|
# RESPONSE ANALYZER SESSION FUNCTIONS (already implemented)
|
|
# =============================================================================
|
|
|
|
@test "store_session_id writes session to file with timestamp" {
|
|
run store_session_id "session-test-abc"
|
|
|
|
[[ -f "$CLAUDE_SESSION_FILE" ]] || skip "store_session_id not yet implemented"
|
|
|
|
local content=$(cat "$CLAUDE_SESSION_FILE")
|
|
[[ "$content" == *"session-test-abc"* ]]
|
|
}
|
|
|
|
@test "get_last_session_id retrieves stored session" {
|
|
# First store a session
|
|
echo '{"session_id": "session-retrieve-test", "timestamp": "2026-01-09T10:00:00Z"}' > "$CLAUDE_SESSION_FILE"
|
|
|
|
run get_last_session_id
|
|
|
|
[[ "$output" == *"session-retrieve-test"* ]]
|
|
}
|
|
|
|
@test "get_last_session_id returns empty when no session file" {
|
|
rm -f "$CLAUDE_SESSION_FILE"
|
|
|
|
run get_last_session_id
|
|
|
|
# Should return empty string, not error
|
|
[[ $status -eq 0 ]]
|
|
[[ -z "$output" || "$output" == "" || "$output" == "null" ]]
|
|
}
|
|
|
|
@test "should_resume_session returns true for recent session" {
|
|
# Store a recent session
|
|
local now_iso=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S%z)
|
|
echo "{\"session_id\": \"session-recent\", \"timestamp\": \"$now_iso\"}" > "$CLAUDE_SESSION_FILE"
|
|
|
|
run should_resume_session
|
|
|
|
# Should indicate session can be resumed
|
|
[[ "$output" == "true" ]]
|
|
}
|
|
|
|
@test "should_resume_session returns false for old session" {
|
|
# Store an old session (24+ hours ago)
|
|
echo '{"session_id": "session-old", "timestamp": "2020-01-01T00:00:00Z"}' > "$CLAUDE_SESSION_FILE"
|
|
|
|
run should_resume_session
|
|
|
|
# Should indicate session expired
|
|
[[ "$output" == "false" ]]
|
|
}
|
|
|
|
@test "should_resume_session returns false when no session file" {
|
|
rm -f "$CLAUDE_SESSION_FILE"
|
|
|
|
run should_resume_session
|
|
|
|
# Should indicate no session to resume
|
|
[[ "$output" == "false" ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# SESSION ID EXTRACTION FROM CLAUDE OUTPUT
|
|
# =============================================================================
|
|
|
|
@test "parse_json_response extracts sessionId from Claude CLI format" {
|
|
local output_file="$LOG_DIR/test_output.log"
|
|
|
|
cat > "$output_file" << 'EOF'
|
|
{
|
|
"result": "Working on feature implementation.",
|
|
"sessionId": "session-unique-123"
|
|
}
|
|
EOF
|
|
|
|
run parse_json_response "$output_file"
|
|
local result_file="$RALPH_DIR/.json_parse_result"
|
|
|
|
[[ -f "$result_file" ]]
|
|
|
|
local session_id=$(jq -r '.session_id' "$result_file")
|
|
assert_equal "$session_id" "session-unique-123"
|
|
}
|
|
|
|
@test "analyze_response persists sessionId to session file" {
|
|
local output_file="$LOG_DIR/test_output.log"
|
|
|
|
cat > "$output_file" << 'EOF'
|
|
{
|
|
"result": "Working on implementation.",
|
|
"sessionId": "session-persist-test-456"
|
|
}
|
|
EOF
|
|
|
|
analyze_response "$output_file" 1
|
|
|
|
# Session ID should be persisted
|
|
[[ -f "$CLAUDE_SESSION_FILE" ]]
|
|
|
|
local stored=$(cat "$CLAUDE_SESSION_FILE" 2>/dev/null)
|
|
[[ "$stored" == *"session-persist-test-456"* ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# SESSION CONTINUITY IN CLAUDE CLI COMMAND
|
|
# =============================================================================
|
|
|
|
@test "--continue flag is added to Claude CLI command" {
|
|
# Check that --continue is used in build_claude_command
|
|
run grep -E '\-\-continue' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]]
|
|
[[ "$output" == *"--continue"* ]]
|
|
}
|
|
|
|
@test "CLAUDE_USE_CONTINUE configuration controls session continuity" {
|
|
# Check that CLAUDE_USE_CONTINUE is defined and controls --continue
|
|
run grep 'CLAUDE_USE_CONTINUE' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# SESSION EXPIRATION HANDLING
|
|
# =============================================================================
|
|
|
|
@test "SESSION_EXPIRATION_SECONDS is defined in response_analyzer" {
|
|
run grep 'SESSION_EXPIRATION_SECONDS' "${BATS_TEST_DIRNAME}/../../lib/response_analyzer.sh"
|
|
|
|
[[ $status -eq 0 ]]
|
|
[[ "$output" == *"86400"* ]] # 24 hours in seconds
|
|
}
|
|
|
|
@test "expired session (24+ hours) is not resumed" {
|
|
# Create old session
|
|
echo '{"session_id": "old-session", "timestamp": "2020-01-01T00:00:00Z"}' > "$CLAUDE_SESSION_FILE"
|
|
|
|
run should_resume_session
|
|
|
|
[[ "$output" == "false" ]]
|
|
}
|
|
|
|
@test "CLAUDE_SESSION_EXPIRY_HOURS is defined in ralph_loop.sh" {
|
|
run grep 'CLAUDE_SESSION_EXPIRY_HOURS' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]] || skip "CLAUDE_SESSION_EXPIRY_HOURS not yet implemented"
|
|
}
|
|
|
|
@test "CLAUDE_SESSION_EXPIRY_HOURS defaults to 24" {
|
|
# Source ralph_loop.sh in a subshell to get the default
|
|
run bash -c "source '${BATS_TEST_DIRNAME}/../../ralph_loop.sh'; echo \$CLAUDE_SESSION_EXPIRY_HOURS"
|
|
|
|
# Should contain 24 as default
|
|
[[ "$output" == *"24"* ]] || skip "CLAUDE_SESSION_EXPIRY_HOURS not yet implemented"
|
|
}
|
|
|
|
@test "--session-expiry flag is recognized in help" {
|
|
run bash "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" --help
|
|
|
|
[[ "$output" == *"session-expiry"* ]] || skip "--session-expiry flag not yet implemented"
|
|
}
|
|
|
|
@test "--session-expiry flag accepts positive integer" {
|
|
# Just check the flag is parsed (don't run full loop)
|
|
run grep -E '\-\-session-expiry' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]] || skip "--session-expiry flag not yet implemented"
|
|
}
|
|
|
|
@test "--session-expiry rejects non-integer value" {
|
|
run timeout 5 bash "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" --session-expiry abc 2>&1
|
|
|
|
# Should fail with error about invalid value
|
|
if [[ "$output" == *"Unknown option"* ]]; then
|
|
skip "--session-expiry flag not yet implemented"
|
|
fi
|
|
|
|
[[ "$output" == *"positive integer"* ]] || [[ "$output" == *"Error"* ]]
|
|
}
|
|
|
|
@test "--session-expiry rejects zero value" {
|
|
run timeout 5 bash "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" --session-expiry 0 2>&1
|
|
|
|
# Should fail with error about invalid value
|
|
if [[ "$output" == *"Unknown option"* ]]; then
|
|
skip "--session-expiry flag not yet implemented"
|
|
fi
|
|
|
|
[[ "$output" == *"positive integer"* ]] || [[ "$output" == *"Error"* ]]
|
|
}
|
|
|
|
@test "--session-expiry rejects negative value" {
|
|
run timeout 5 bash "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" --session-expiry -5 2>&1
|
|
|
|
# Should fail with error about invalid value
|
|
if [[ "$output" == *"Unknown option"* ]]; then
|
|
skip "--session-expiry flag not yet implemented"
|
|
fi
|
|
|
|
[[ "$output" == *"positive integer"* ]] || [[ "$output" == *"Error"* ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# INIT_CLAUDE_SESSION EXPIRATION TESTS (Behavioral)
|
|
# =============================================================================
|
|
|
|
@test "init_claude_session checks session expiration" {
|
|
# Check that init_claude_session includes expiration logic
|
|
run grep -A30 'init_claude_session' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Should reference expiration or age checking
|
|
[[ "$output" == *"expir"* ]] || [[ "$output" == *"age"* ]] || [[ "$output" == *"stat"* ]] || skip "Session expiration not yet implemented in init_claude_session"
|
|
}
|
|
|
|
@test "init_claude_session uses cross-platform stat command" {
|
|
# Check for uname or Darwin/Linux detection in get_session_file_age_hours
|
|
run grep -A30 'get_session_file_age_hours' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Should have cross-platform handling
|
|
[[ "$output" == *"Darwin"* ]] || [[ "$output" == *"uname"* ]] || skip "Cross-platform stat not yet implemented"
|
|
}
|
|
|
|
@test "get_session_file_age_hours returns correct age" {
|
|
# Check if helper function exists
|
|
run grep 'get_session_file_age_hours' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
[[ $status -eq 0 ]] || skip "get_session_file_age_hours function not yet implemented"
|
|
}
|
|
|
|
@test "get_session_file_age_hours returns 0 for missing file" {
|
|
# Source the script to get the function
|
|
source "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Test with non-existent file
|
|
run get_session_file_age_hours "/nonexistent/path/file"
|
|
|
|
[[ "$output" == "0" ]]
|
|
}
|
|
|
|
@test "get_session_file_age_hours returns -1 for stat failure" {
|
|
# Source the script to get the function
|
|
source "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Create a file then make it inaccessible (simulate stat failure via directory permissions)
|
|
local test_file="$TEST_DIR/unreadable_file"
|
|
echo "test" > "$test_file"
|
|
|
|
# Verify the function code handles stat failure by checking the implementation
|
|
run grep -A25 'get_session_file_age_hours' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
[[ "$output" == *'echo "-1"'* ]]
|
|
}
|
|
|
|
@test "init_claude_session removes expired session file" {
|
|
# Source the script to get the function
|
|
source "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Create an old session file (simulate by setting low expiry)
|
|
echo '{"session_id": "old-session", "timestamp": 1000000000}' > "$CLAUDE_SESSION_FILE"
|
|
touch -d "2020-01-01" "$CLAUDE_SESSION_FILE" 2>/dev/null || touch -t 202001010000 "$CLAUDE_SESSION_FILE"
|
|
|
|
# Set very short expiry to trigger expiration
|
|
CLAUDE_SESSION_EXPIRY_HOURS=1
|
|
|
|
run init_claude_session
|
|
|
|
# Session file should be removed
|
|
[[ ! -f "$CLAUDE_SESSION_FILE" ]] || [[ "$output" == *"expired"* ]]
|
|
}
|
|
|
|
@test "init_claude_session logs expiration with age info" {
|
|
# Source the script to get the function
|
|
source "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Verify code structure includes age logging
|
|
run grep -A40 'init_claude_session()' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
[[ "$output" == *'age_hours'* ]] && [[ "$output" == *'expired'* ]]
|
|
}
|
|
|
|
@test "init_claude_session logs session age when resuming" {
|
|
# Source the script to get the function
|
|
source "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Verify code structure includes resume logging
|
|
run grep -A50 'init_claude_session()' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
[[ "$output" == *'Resuming'* ]] && [[ "$output" == *'old'* ]]
|
|
}
|
|
|
|
@test "init_claude_session handles stat failure gracefully" {
|
|
# Source the script to get the function
|
|
source "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
|
|
# Verify code structure handles -1 return
|
|
run grep -A40 'init_claude_session()' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh"
|
|
[[ "$output" == *"-1"* ]] && [[ "$output" == *"WARN"* ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# EDGE CASES
|
|
# =============================================================================
|
|
|
|
@test "store_session_id handles empty session ID" {
|
|
run store_session_id ""
|
|
|
|
# Should fail or return error status
|
|
[[ $status -ne 0 ]]
|
|
}
|
|
|
|
@test "get_last_session_id handles corrupted JSON file" {
|
|
echo "not valid json at all {{{" > "$CLAUDE_SESSION_FILE"
|
|
|
|
run get_last_session_id
|
|
|
|
# Should not error, should return empty
|
|
[[ $status -eq 0 ]]
|
|
[[ -z "$output" || "$output" == "" || "$output" == "null" ]]
|
|
}
|
|
|
|
@test "should_resume_session handles corrupted JSON file" {
|
|
echo "corrupted json {{{" > "$CLAUDE_SESSION_FILE"
|
|
|
|
run should_resume_session
|
|
|
|
# Should return false, not error
|
|
[[ $status -eq 0 || $status -eq 1 ]] # Either is acceptable
|
|
[[ "$output" == "false" ]]
|
|
}
|
|
|
|
@test "should_resume_session handles missing timestamp field" {
|
|
echo '{"session_id": "session-no-time"}' > "$CLAUDE_SESSION_FILE"
|
|
|
|
run should_resume_session
|
|
|
|
# Should return false since no timestamp to validate
|
|
[[ "$output" == "false" ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# INTEGRATION: FULL SESSION LIFECYCLE
|
|
# =============================================================================
|
|
|
|
@test "full session lifecycle: store -> get -> check -> expires" {
|
|
# 1. Store a session
|
|
store_session_id "lifecycle-session-001"
|
|
|
|
# 2. Get it back
|
|
local stored=$(get_last_session_id)
|
|
[[ "$stored" == "lifecycle-session-001" ]]
|
|
|
|
# 3. Check if resumable (should be true since just created)
|
|
run should_resume_session
|
|
[[ "$output" == "true" ]]
|
|
|
|
# 4. Simulate expiration by setting old timestamp
|
|
echo '{"session_id": "lifecycle-session-001", "timestamp": "2020-01-01T00:00:00Z"}' > "$CLAUDE_SESSION_FILE"
|
|
|
|
# 5. Check again (should be expired)
|
|
run should_resume_session
|
|
[[ "$output" == "false" ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# SESSION RESET CLEARS EXIT SIGNALS (Issue #91 Fix)
|
|
# =============================================================================
|
|
|
|
@test "reset_session clears exit_signals file to prevent premature exit" {
|
|
# Setup: Create stale exit signals that would cause premature exit
|
|
echo '{"test_only_loops": [1,2], "done_signals": [1], "completion_indicators": [1,2,3]}' > "$EXIT_SIGNALS_FILE"
|
|
|
|
# Verify stale signals exist
|
|
local completion_count=$(jq '.completion_indicators | length' "$EXIT_SIGNALS_FILE")
|
|
[[ "$completion_count" == "3" ]]
|
|
|
|
# Source ralph_loop.sh to get reset_session function
|
|
# We need to mock some things to prevent full initialization
|
|
export RALPH_SESSION_HISTORY_FILE="$RALPH_DIR/.ralph_session_history"
|
|
export RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
|
|
|
# Create a mock response analysis file
|
|
echo '{"analysis": {"exit_signal": true}}' > "$RESPONSE_ANALYSIS_FILE"
|
|
[[ -f "$RESPONSE_ANALYSIS_FILE" ]]
|
|
|
|
# Define reset_session inline for testing (extracted from ralph_loop.sh)
|
|
reset_session() {
|
|
local reason=${1:-"manual_reset"}
|
|
local reset_timestamp
|
|
reset_timestamp=$(get_iso_timestamp)
|
|
|
|
jq -n \
|
|
--arg session_id "" \
|
|
--arg created_at "" \
|
|
--arg last_used "" \
|
|
--arg reset_at "$reset_timestamp" \
|
|
--arg reset_reason "$reason" \
|
|
'{
|
|
session_id: $session_id,
|
|
created_at: $created_at,
|
|
last_used: $last_used,
|
|
reset_at: $reset_at,
|
|
reset_reason: $reset_reason
|
|
}' > "$RALPH_SESSION_FILE"
|
|
|
|
rm -f "$CLAUDE_SESSION_FILE" 2>/dev/null
|
|
|
|
# Issue #91 fix: Clear exit signals
|
|
if [[ -f "$EXIT_SIGNALS_FILE" ]]; then
|
|
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
|
fi
|
|
|
|
# Clear response analysis
|
|
rm -f "$RESPONSE_ANALYSIS_FILE" 2>/dev/null
|
|
}
|
|
|
|
# Call reset_session
|
|
reset_session "test_reset"
|
|
|
|
# Verify exit signals were cleared
|
|
local new_completion_count=$(jq '.completion_indicators | length' "$EXIT_SIGNALS_FILE")
|
|
[[ "$new_completion_count" == "0" ]]
|
|
|
|
local new_test_loops=$(jq '.test_only_loops | length' "$EXIT_SIGNALS_FILE")
|
|
[[ "$new_test_loops" == "0" ]]
|
|
|
|
local new_done_signals=$(jq '.done_signals | length' "$EXIT_SIGNALS_FILE")
|
|
[[ "$new_done_signals" == "0" ]]
|
|
|
|
# Verify response analysis was cleared
|
|
[[ ! -f "$RESPONSE_ANALYSIS_FILE" ]]
|
|
}
|
|
|
|
@test "reset_session prevents issue #91 scenario (stale completion indicators)" {
|
|
# Issue #91: Ralph exits immediately when stale completion_indicators exist
|
|
|
|
# Ensure variables are set before use (defensive against env differences)
|
|
export RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
|
export RALPH_SESSION_HISTORY_FILE="$RALPH_DIR/.ralph_session_history"
|
|
|
|
# Simulate the issue scenario:
|
|
# 1. Previous session ended with completion_indicators: [1,2]
|
|
# 2. Previous session had EXIT_SIGNAL: true
|
|
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": [1,2]}' > "$EXIT_SIGNALS_FILE"
|
|
echo '{"analysis": {"exit_signal": true, "has_completion_signal": true}}' > "$RESPONSE_ANALYSIS_FILE"
|
|
|
|
# Verify the problematic state exists
|
|
local completion_count=$(jq '.completion_indicators | length' "$EXIT_SIGNALS_FILE")
|
|
[[ "$completion_count" == "2" ]]
|
|
|
|
local exit_signal=$(jq -r '.analysis.exit_signal' "$RESPONSE_ANALYSIS_FILE")
|
|
[[ "$exit_signal" == "true" ]]
|
|
|
|
# Define reset_session with the fix
|
|
reset_session() {
|
|
local reason=${1:-"manual_reset"}
|
|
local reset_timestamp
|
|
reset_timestamp=$(get_iso_timestamp)
|
|
|
|
jq -n \
|
|
--arg session_id "" \
|
|
--arg created_at "" \
|
|
--arg last_used "" \
|
|
--arg reset_at "$reset_timestamp" \
|
|
--arg reset_reason "$reason" \
|
|
'{
|
|
session_id: $session_id,
|
|
created_at: $created_at,
|
|
last_used: $last_used,
|
|
reset_at: $reset_at,
|
|
reset_reason: $reset_reason
|
|
}' > "$RALPH_SESSION_FILE"
|
|
|
|
rm -f "$CLAUDE_SESSION_FILE" 2>/dev/null
|
|
|
|
# Issue #91 fix
|
|
if [[ -f "$EXIT_SIGNALS_FILE" ]]; then
|
|
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
|
fi
|
|
rm -f "$RESPONSE_ANALYSIS_FILE" 2>/dev/null
|
|
}
|
|
|
|
# User runs --reset-session
|
|
reset_session "manual_reset"
|
|
|
|
# Verify the fix: completion indicators should be cleared
|
|
local new_completion_count=$(jq '.completion_indicators | length' "$EXIT_SIGNALS_FILE")
|
|
[[ "$new_completion_count" == "0" ]]
|
|
|
|
# Verify response analysis is gone (no stale EXIT_SIGNAL)
|
|
[[ ! -f "$RESPONSE_ANALYSIS_FILE" ]]
|
|
}
|