feat(analyzer): add Claude CLI JSON format support and session management
- Extend parse_json_response() to support both flat and Claude CLI formats - Extract result, sessionId, and metadata fields - Support metadata.files_changed, metadata.has_errors, completion_status - Parse progress_indicators array for confidence boosting - Add session management functions for continuity tracking: - store_session_id(): Persist session with ISO timestamp - get_last_session_id(): Retrieve stored session ID - should_resume_session(): Check session validity (24-hour expiration) - Add get_epoch_seconds() to date_utils.sh for cross-platform epoch time - Auto-persist sessionId to .session_id file during response analysis - Add 16 new TDD tests for Claude CLI format and session management - Update documentation for v0.9.6 (239 tests total) Test count: 239 (up from 223)
This commit is contained in:
parent
3d91d92691
commit
fdff095c18
4 changed files with 490 additions and 14 deletions
23
CLAUDE.md
23
CLAUDE.md
|
|
@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||
|
||||
This is the Ralph for Claude Code repository - an autonomous AI development loop system that enables continuous development cycles with intelligent exit detection and rate limiting.
|
||||
|
||||
**Version**: v0.9.5 | **Tests**: 223 passing (100% pass rate) | **CI/CD**: GitHub Actions
|
||||
**Version**: v0.9.6 | **Tests**: 239 passing (100% pass rate) | **CI/CD**: GitHub Actions
|
||||
|
||||
## Core Architecture
|
||||
|
||||
|
|
@ -33,7 +33,10 @@ The system uses a modular architecture with reusable components in the `lib/` di
|
|||
2. **lib/response_analyzer.sh** - Intelligent response analysis
|
||||
- Analyzes Claude Code output for completion signals
|
||||
- **JSON output format detection and parsing** (with text fallback)
|
||||
- Supports both flat JSON format and Claude CLI format (`result`, `sessionId`, `metadata`)
|
||||
- Extracts structured fields: status, exit_signal, work_type, files_modified
|
||||
- **Session management**: `store_session_id()`, `get_last_session_id()`, `should_resume_session()`
|
||||
- Automatic session persistence to `.session_id` file with 24-hour expiration
|
||||
- Detects test-only loops and stuck error patterns
|
||||
- Two-stage error filtering to eliminate false positives
|
||||
- Multi-line error matching for accurate stuck loop detection
|
||||
|
|
@ -272,13 +275,13 @@ Ralph uses advanced error detection with two-stage filtering to eliminate false
|
|||
|
||||
## Test Suite
|
||||
|
||||
### Test Files (223 tests total)
|
||||
### Test Files (239 tests total)
|
||||
|
||||
| File | Tests | Description |
|
||||
|------|-------|-------------|
|
||||
| `test_cli_parsing.bats` | 27 | CLI argument parsing for all 12 flags |
|
||||
| `test_cli_modern.bats` | 29 | Modern CLI commands (Phase 1.1) + build_claude_command fix |
|
||||
| `test_json_parsing.bats` | 20 | JSON output format parsing |
|
||||
| `test_json_parsing.bats` | 36 | JSON output format parsing + Claude CLI format + session management |
|
||||
| `test_exit_detection.bats` | 20 | Exit signal detection |
|
||||
| `test_rate_limiting.bats` | 15 | Rate limiting behavior |
|
||||
| `test_loop_execution.bats` | 20 | Integration tests |
|
||||
|
|
@ -301,6 +304,20 @@ bats tests/unit/test_cli_parsing.bats
|
|||
|
||||
## Recent Improvements
|
||||
|
||||
### JSON Output & Session Management (v0.9.6)
|
||||
- Extended `parse_json_response()` to support Claude Code CLI JSON format
|
||||
- Supports `result`, `sessionId`, and `metadata` fields alongside existing flat format
|
||||
- Extracts `metadata.files_changed`, `metadata.has_errors`, `metadata.completion_status`
|
||||
- Parses `metadata.progress_indicators` array for confidence boosting
|
||||
- Added session management functions for continuity tracking:
|
||||
- `store_session_id()` - Persists session with timestamp
|
||||
- `get_last_session_id()` - Retrieves stored session ID
|
||||
- `should_resume_session()` - Checks session validity (24-hour expiration)
|
||||
- Added `get_epoch_seconds()` to date_utils.sh for cross-platform epoch time
|
||||
- Auto-persists sessionId to `.session_id` file during response analysis
|
||||
- Added 16 new tests covering Claude CLI format and session management
|
||||
- Test count: 239 (up from 223)
|
||||
|
||||
### PRD Import Tests (v0.9.5)
|
||||
- Added 22 comprehensive tests for `ralph_import.sh` PRD conversion script
|
||||
- Tests cover: file format support (.md, .txt, .json), output file creation, project naming
|
||||
|
|
|
|||
|
|
@ -39,3 +39,15 @@ get_next_hour_time() {
|
|||
get_basic_timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# Get current Unix epoch time in seconds
|
||||
# Returns: Integer seconds since 1970-01-01 00:00:00 UTC
|
||||
get_epoch_seconds() {
|
||||
date +%s
|
||||
}
|
||||
|
||||
# Export functions for use in other scripts
|
||||
export -f get_iso_timestamp
|
||||
export -f get_next_hour_time
|
||||
export -f get_basic_timestamp
|
||||
export -f get_epoch_seconds
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ detect_output_format() {
|
|||
|
||||
# Parse JSON response and extract structured fields
|
||||
# Creates .json_parse_result with normalized analysis data
|
||||
# Supports TWO JSON formats:
|
||||
# 1. Flat format: { status, exit_signal, work_type, files_modified, ... }
|
||||
# 2. Claude CLI format: { result, sessionId, metadata: { files_changed, has_errors, completion_status, ... } }
|
||||
parse_json_response() {
|
||||
local output_file=$1
|
||||
local result_file="${2:-.json_parse_result}"
|
||||
|
|
@ -67,22 +70,54 @@ parse_json_response() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Extract fields with defaults
|
||||
local status=$(jq -r '.status // "UNKNOWN"' "$output_file" 2>/dev/null)
|
||||
local exit_signal=$(jq -r '.exit_signal // false' "$output_file" 2>/dev/null)
|
||||
local work_type=$(jq -r '.work_type // "UNKNOWN"' "$output_file" 2>/dev/null)
|
||||
local files_modified=$(jq -r '.files_modified // 0' "$output_file" 2>/dev/null)
|
||||
local error_count=$(jq -r '.error_count // 0' "$output_file" 2>/dev/null)
|
||||
local summary=$(jq -r '.summary // ""' "$output_file" 2>/dev/null)
|
||||
# Detect JSON format by checking for Claude CLI fields
|
||||
local has_result_field=$(jq -r 'has("result")' "$output_file" 2>/dev/null)
|
||||
local has_session_id_field=$(jq -r 'has("sessionId")' "$output_file" 2>/dev/null)
|
||||
|
||||
# Extract nested metadata if present
|
||||
# Extract fields - support both flat format and Claude CLI format
|
||||
# Priority: Claude CLI fields first, then flat format fields
|
||||
|
||||
# Status: from flat format OR derived from metadata.completion_status
|
||||
local status=$(jq -r '.status // "UNKNOWN"' "$output_file" 2>/dev/null)
|
||||
local completion_status=$(jq -r '.metadata.completion_status // ""' "$output_file" 2>/dev/null)
|
||||
if [[ "$completion_status" == "complete" || "$completion_status" == "COMPLETE" ]]; then
|
||||
status="COMPLETE"
|
||||
fi
|
||||
|
||||
# Exit signal: from flat format OR derived from completion_status
|
||||
local exit_signal=$(jq -r '.exit_signal // false' "$output_file" 2>/dev/null)
|
||||
|
||||
# Work type: from flat format
|
||||
local work_type=$(jq -r '.work_type // "UNKNOWN"' "$output_file" 2>/dev/null)
|
||||
|
||||
# Files modified: from flat format OR from metadata.files_changed
|
||||
local files_modified=$(jq -r '.metadata.files_changed // .files_modified // 0' "$output_file" 2>/dev/null)
|
||||
|
||||
# Error count: from flat format OR derived from metadata.has_errors
|
||||
local error_count=$(jq -r '.error_count // 0' "$output_file" 2>/dev/null)
|
||||
local has_errors=$(jq -r '.metadata.has_errors // false' "$output_file" 2>/dev/null)
|
||||
if [[ "$has_errors" == "true" && "$error_count" == "0" ]]; then
|
||||
error_count=1 # At least one error if has_errors is true
|
||||
fi
|
||||
|
||||
# Summary: from flat format OR from result field (Claude CLI format)
|
||||
local summary=$(jq -r '.result // .summary // ""' "$output_file" 2>/dev/null)
|
||||
|
||||
# Session ID: from Claude CLI format (sessionId) OR from metadata.session_id
|
||||
local session_id=$(jq -r '.sessionId // .metadata.session_id // ""' "$output_file" 2>/dev/null)
|
||||
|
||||
# Loop number: from metadata
|
||||
local loop_number=$(jq -r '.metadata.loop_number // .loop_number // 0' "$output_file" 2>/dev/null)
|
||||
local session_id=$(jq -r '.metadata.session_id // ""' "$output_file" 2>/dev/null)
|
||||
|
||||
# Confidence: from flat format
|
||||
local confidence=$(jq -r '.confidence // 0' "$output_file" 2>/dev/null)
|
||||
|
||||
# Progress indicators: from Claude CLI metadata (optional)
|
||||
local progress_count=$(jq -r '.metadata.progress_indicators | if . then length else 0 end' "$output_file" 2>/dev/null)
|
||||
|
||||
# Normalize values
|
||||
# Convert exit_signal to boolean string
|
||||
if [[ "$exit_signal" == "true" || "$status" == "COMPLETE" ]]; then
|
||||
if [[ "$exit_signal" == "true" || "$status" == "COMPLETE" || "$completion_status" == "complete" || "$completion_status" == "COMPLETE" ]]; then
|
||||
exit_signal="true"
|
||||
else
|
||||
exit_signal="false"
|
||||
|
|
@ -94,7 +129,7 @@ parse_json_response() {
|
|||
is_test_only="true"
|
||||
fi
|
||||
|
||||
# Determine is_stuck from error_count
|
||||
# Determine is_stuck from error_count (threshold >5)
|
||||
local is_stuck="false"
|
||||
error_count=$((error_count + 0)) # Ensure integer
|
||||
if [[ $error_count -gt 5 ]]; then
|
||||
|
|
@ -104,12 +139,23 @@ parse_json_response() {
|
|||
# Ensure files_modified is integer
|
||||
files_modified=$((files_modified + 0))
|
||||
|
||||
# Ensure progress_count is integer
|
||||
progress_count=$((progress_count + 0))
|
||||
|
||||
# Calculate has_completion_signal
|
||||
local has_completion_signal="false"
|
||||
if [[ "$status" == "COMPLETE" || "$exit_signal" == "true" ]]; then
|
||||
has_completion_signal="true"
|
||||
fi
|
||||
|
||||
# Boost confidence based on structured data availability
|
||||
if [[ "$has_result_field" == "true" ]]; then
|
||||
confidence=$((confidence + 20)) # Structured response boost
|
||||
fi
|
||||
if [[ $progress_count -gt 0 ]]; then
|
||||
confidence=$((confidence + progress_count * 5)) # Progress indicators boost
|
||||
fi
|
||||
|
||||
# Write normalized result using jq for safe JSON construction
|
||||
# String fields use --arg (auto-escapes), numeric/boolean use --argjson
|
||||
jq -n \
|
||||
|
|
@ -184,6 +230,12 @@ analyze_response() {
|
|||
work_summary=$(jq -r '.summary' .json_parse_result 2>/dev/null || echo "")
|
||||
files_modified=$(jq -r '.files_modified' .json_parse_result 2>/dev/null || echo "0")
|
||||
local json_confidence=$(jq -r '.confidence' .json_parse_result 2>/dev/null || echo "0")
|
||||
local session_id=$(jq -r '.session_id' .json_parse_result 2>/dev/null || echo "")
|
||||
|
||||
# Persist session ID if present
|
||||
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
||||
store_session_id "$session_id"
|
||||
fi
|
||||
|
||||
# JSON parsing provides high confidence
|
||||
if [[ "$exit_signal" == "true" ]]; then
|
||||
|
|
@ -511,6 +563,102 @@ detect_stuck_loop() {
|
|||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SESSION MANAGEMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Session file location
|
||||
SESSION_FILE=".session_id"
|
||||
# Session expiration time in seconds (24 hours)
|
||||
SESSION_EXPIRATION_SECONDS=86400
|
||||
|
||||
# Store session ID to file with timestamp
|
||||
# Usage: store_session_id "session-uuid-123"
|
||||
store_session_id() {
|
||||
local session_id=$1
|
||||
|
||||
if [[ -z "$session_id" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Write session with timestamp using jq for safe JSON construction
|
||||
jq -n \
|
||||
--arg session_id "$session_id" \
|
||||
--arg timestamp "$(get_iso_timestamp)" \
|
||||
'{
|
||||
session_id: $session_id,
|
||||
timestamp: $timestamp
|
||||
}' > "$SESSION_FILE"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Get the last stored session ID
|
||||
# Returns: session ID string or empty if not found
|
||||
get_last_session_id() {
|
||||
if [[ ! -f "$SESSION_FILE" ]]; then
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Extract session_id from JSON file
|
||||
local session_id=$(jq -r '.session_id // ""' "$SESSION_FILE" 2>/dev/null)
|
||||
echo "$session_id"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check if the stored session should be resumed
|
||||
# Returns: 0 (true) if session is valid and recent, 1 (false) otherwise
|
||||
should_resume_session() {
|
||||
if [[ ! -f "$SESSION_FILE" ]]; then
|
||||
echo "false"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Get session timestamp
|
||||
local timestamp=$(jq -r '.timestamp // ""' "$SESSION_FILE" 2>/dev/null)
|
||||
|
||||
if [[ -z "$timestamp" ]]; then
|
||||
echo "false"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Calculate session age using date utilities
|
||||
local now=$(get_epoch_seconds)
|
||||
local session_time
|
||||
|
||||
# Parse ISO timestamp to epoch - try multiple formats for cross-platform compatibility
|
||||
if command -v gdate &>/dev/null; then
|
||||
# macOS with coreutils
|
||||
session_time=$(gdate -d "$timestamp" +%s 2>/dev/null)
|
||||
elif date --version 2>&1 | grep -q GNU; then
|
||||
# GNU date (Linux)
|
||||
session_time=$(date -d "$timestamp" +%s 2>/dev/null)
|
||||
else
|
||||
# BSD date (macOS without coreutils) - try parsing ISO format
|
||||
# Format: 2026-01-09T10:30:00+00:00 or similar
|
||||
session_time=$(date -j -f "%Y-%m-%dT%H:%M:%S" "${timestamp%[+-]*}" +%s 2>/dev/null)
|
||||
fi
|
||||
|
||||
# If we couldn't parse the timestamp, consider session expired
|
||||
if [[ -z "$session_time" || ! "$session_time" =~ ^[0-9]+$ ]]; then
|
||||
echo "false"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Calculate age in seconds
|
||||
local age=$((now - session_time))
|
||||
|
||||
# Check if session is still valid (less than expiration time)
|
||||
if [[ $age -lt $SESSION_EXPIRATION_SECONDS ]]; then
|
||||
echo "true"
|
||||
return 0
|
||||
else
|
||||
echo "false"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Export functions for use in ralph_loop.sh
|
||||
export -f detect_output_format
|
||||
export -f parse_json_response
|
||||
|
|
@ -518,3 +666,6 @@ export -f analyze_response
|
|||
export -f update_exit_signals
|
||||
export -f log_analysis_summary
|
||||
export -f detect_stuck_loop
|
||||
export -f store_session_id
|
||||
export -f get_last_session_id
|
||||
export -f should_resume_session
|
||||
|
|
|
|||
|
|
@ -441,3 +441,299 @@ EOF
|
|||
local is_test_only=$(jq -r '.analysis.is_test_only' .response_analysis)
|
||||
assert_equal "$is_test_only" "true"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# CLAUDE CODE CLI JSON STRUCTURE TESTS
|
||||
# =============================================================================
|
||||
# Tests for the modernized Claude Code CLI output format with:
|
||||
# - result: Actual Claude response content
|
||||
# - sessionId: Session UUID for continuity
|
||||
# - metadata: Structured information about the execution
|
||||
|
||||
@test "detect_output_format identifies Claude CLI JSON with result field" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Implemented authentication module with JWT tokens.",
|
||||
"sessionId": "session-abc123",
|
||||
"metadata": {
|
||||
"files_changed": 3,
|
||||
"has_errors": false,
|
||||
"completion_status": "in_progress"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run detect_output_format "$output_file"
|
||||
assert_equal "$output" "json"
|
||||
}
|
||||
|
||||
@test "parse_json_response extracts result field from Claude CLI format" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "All tasks completed successfully. Project ready for review.",
|
||||
"sessionId": "session-xyz789"
|
||||
}
|
||||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
# Result should be captured in summary field
|
||||
local summary=$(jq -r '.summary' "$result_file")
|
||||
[[ "$summary" == *"All tasks completed"* ]]
|
||||
}
|
||||
|
||||
@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=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
local session_id=$(jq -r '.session_id' "$result_file")
|
||||
assert_equal "$session_id" "session-unique-123"
|
||||
}
|
||||
|
||||
@test "parse_json_response extracts metadata.files_changed" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Modified configuration files.",
|
||||
"sessionId": "session-001",
|
||||
"metadata": {
|
||||
"files_changed": 5,
|
||||
"has_errors": false
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
local files=$(jq -r '.files_modified' "$result_file")
|
||||
assert_equal "$files" "5"
|
||||
}
|
||||
|
||||
@test "parse_json_response extracts metadata.has_errors" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Encountered compilation errors.",
|
||||
"sessionId": "session-002",
|
||||
"metadata": {
|
||||
"files_changed": 0,
|
||||
"has_errors": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
# has_errors should map to error tracking
|
||||
local is_stuck=$(jq -r '.is_stuck' "$result_file")
|
||||
# Single error shouldn't trigger stuck (threshold is >5)
|
||||
# But we should track error state
|
||||
[[ -f "$result_file" ]]
|
||||
}
|
||||
|
||||
@test "parse_json_response detects completion from metadata.completion_status" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Project implementation finished.",
|
||||
"sessionId": "session-003",
|
||||
"metadata": {
|
||||
"files_changed": 10,
|
||||
"has_errors": false,
|
||||
"completion_status": "complete"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
local exit_signal=$(jq -r '.exit_signal' "$result_file")
|
||||
assert_equal "$exit_signal" "true"
|
||||
}
|
||||
|
||||
@test "parse_json_response handles progress_indicators array" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Made significant progress.",
|
||||
"sessionId": "session-004",
|
||||
"metadata": {
|
||||
"files_changed": 3,
|
||||
"has_errors": false,
|
||||
"progress_indicators": ["implemented auth", "added tests", "updated docs"]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
# Progress indicators should boost confidence or be stored
|
||||
[[ -f "$result_file" ]]
|
||||
}
|
||||
|
||||
@test "parse_json_response extracts usage metadata" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Completed task.",
|
||||
"sessionId": "session-005",
|
||||
"metadata": {
|
||||
"files_changed": 2,
|
||||
"usage": {
|
||||
"input_tokens": 1500,
|
||||
"output_tokens": 800
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
# Usage info should be preserved in metadata
|
||||
[[ -f "$result_file" ]]
|
||||
}
|
||||
|
||||
@test "analyze_response handles Claude CLI JSON and detects completion" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "All requested features have been implemented. The project is complete.",
|
||||
"sessionId": "session-complete-001",
|
||||
"metadata": {
|
||||
"files_changed": 8,
|
||||
"has_errors": false,
|
||||
"completion_status": "complete"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
assert_file_exists ".response_analysis"
|
||||
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
assert_equal "$exit_signal" "true"
|
||||
|
||||
local output_format=$(jq -r '.output_format' .response_analysis)
|
||||
assert_equal "$output_format" "json"
|
||||
}
|
||||
|
||||
@test "analyze_response persists sessionId to .session_id file" {
|
||||
local output_file="$LOG_DIR/test_output.log"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
{
|
||||
"result": "Working on implementation.",
|
||||
"sessionId": "session-persist-test-123"
|
||||
}
|
||||
EOF
|
||||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
# Session ID should be persisted for continuity
|
||||
[[ -f ".session_id" ]] || skip "Session persistence not yet implemented"
|
||||
|
||||
local stored_session=$(cat .session_id)
|
||||
[[ "$stored_session" == *"session-persist-test-123"* ]]
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SESSION MANAGEMENT FUNCTION TESTS
|
||||
# =============================================================================
|
||||
|
||||
@test "store_session_id writes session to file with timestamp" {
|
||||
run store_session_id "session-test-abc"
|
||||
|
||||
[[ -f ".session_id" ]] || skip "store_session_id not yet implemented"
|
||||
|
||||
local content=$(cat .session_id)
|
||||
[[ "$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"}' > .session_id
|
||||
|
||||
run get_last_session_id
|
||||
|
||||
[[ "$output" == *"session-retrieve-test"* ]] || skip "get_last_session_id not yet implemented"
|
||||
}
|
||||
|
||||
@test "get_last_session_id returns empty when no session file" {
|
||||
rm -f .session_id
|
||||
|
||||
run get_last_session_id
|
||||
|
||||
# Should return empty string, not error
|
||||
[[ "$status" -eq 0 ]] || skip "get_last_session_id not yet implemented"
|
||||
[[ -z "$output" || "$output" == "" || "$output" == "null" ]]
|
||||
}
|
||||
|
||||
@test "should_resume_session returns true for recent session" {
|
||||
# Store a recent session (simulated as current timestamp)
|
||||
local now=$(date +%s)
|
||||
echo "{\"session_id\": \"session-recent\", \"timestamp\": \"$(date -Iseconds)\"}" > .session_id
|
||||
|
||||
run should_resume_session
|
||||
|
||||
# Should indicate session can be resumed
|
||||
[[ "$status" -eq 0 ]] || skip "should_resume_session not yet implemented"
|
||||
}
|
||||
|
||||
@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"}' > .session_id
|
||||
|
||||
run should_resume_session
|
||||
|
||||
# Should indicate session expired
|
||||
[[ "$status" -ne 0 || "$output" == "false" ]] || skip "should_resume_session not yet implemented"
|
||||
}
|
||||
|
||||
@test "should_resume_session returns false when no session file" {
|
||||
rm -f .session_id
|
||||
|
||||
run should_resume_session
|
||||
|
||||
# Should indicate no session to resume
|
||||
[[ "$status" -ne 0 || "$output" == "false" ]] || skip "should_resume_session not yet implemented"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue