* feat(structure): migrate Ralph files to .ralph/ subfolder BREAKING CHANGE: Ralph configuration files now live in .ralph/ subfolder This refactoring moves all Ralph-specific files into a hidden .ralph/ directory while keeping src/ at the project root. This improves compatibility with existing tooling and keeps the project root clean. Changes: - Move PROMPT.md, @fix_plan.md, @AGENT.md to .ralph/ - Move specs/, logs/, docs/generated/, examples/ to .ralph/ - Move state files (.response_analysis, .circuit_breaker_state, etc.) to .ralph/ - Keep src/ at project root (unchanged) - Add RALPH_DIR=".ralph" configuration variable - Add ralph-migrate command for existing projects - Create migrate_to_ralph_folder.sh migration script - Update all path references in scripts and tests - Update documentation (README.md, CLAUDE.md) New project structure: project/ ├── .ralph/ # Ralph configuration │ ├── PROMPT.md │ ├── @fix_plan.md │ ├── @AGENT.md │ ├── specs/ │ ├── logs/ │ └── docs/generated/ └── src/ # Source code (unchanged) Migration: Run `ralph-migrate` in existing projects to upgrade. All 310 tests pass (100% pass rate). * chore: add .claude/settings.local.json to .gitignore * fix: address code review feedback for .ralph/ subfolder structure Fixes multiple path-related issues identified in code review: Test fixes: - Fix create_sample_prompt to use $RALPH_DIR/PROMPT.md in test_session_continuity.bats - Fix result_file path to use $RALPH_DIR/.json_parse_result in test_json_parsing.bats - Fix @fix_plan.md and .response_analysis paths in test_cli_modern.bats - Update templates directory missing test to account for global fallback Template fix: - Fix @fix_plan.md reference in templates/PROMPT.md to use .ralph/ prefix Script fixes: - Fix PROMPT_FILE comparison in ralph_loop.sh to use $RALPH_DIR/PROMPT.md - Fix examples migration logic in migrate_to_ralph_folder.sh (remove premature mkdir) - Move templates directory check AFTER cd in setup.sh (was checking wrong location) - Add template directory validation with fallback to global templates All 310 tests pass. * Update migrate_to_ralph_folder.sh Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com> * fix: address code review feedback for .ralph/ subfolder structure Code Review Fixes: - Fix test_json_parsing.bats: all result_file and session file paths now use $RALPH_DIR prefix - Fix ralph_loop.sh help text: paths now show .ralph/.ralph_session, .ralph/.call_count, etc. - Fix migrate_to_ralph_folder.sh: - Proper error handling for date command (separate local declaration) - Use cp -a source/. dest/ pattern to preserve dotfiles and attributes - Remove 2>/dev/null suppression to surface copy errors - Update create_files.sh to use .ralph/ structure for embedded scripts - Update .gitignore with all .ralph/ state file paths - Add old structure detection in ralph_loop.sh with helpful migration message Version Update: - Bump to v0.10.0 (breaking change: structural reorganization) - Update README.md and CLAUDE.md with new version and release notes - Add ralph-migrate documentation to Key Commands section All 310 tests pass. --------- Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
207 lines
5.4 KiB
Bash
Executable file
207 lines
5.4 KiB
Bash
Executable file
#!/usr/bin/env bats
|
|
# Unit Tests for Rate Limiting Logic
|
|
|
|
load '../helpers/test_helper'
|
|
|
|
# Source ralph functions (we need to extract these first)
|
|
setup() {
|
|
# Source helper functions
|
|
source "$(dirname "$BATS_TEST_FILENAME")/../helpers/test_helper.bash"
|
|
|
|
# Set up environment with .ralph/ subfolder structure
|
|
export RALPH_DIR=".ralph"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
|
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
|
|
|
# Create temp test directory
|
|
export TEST_TEMP_DIR="$(mktemp -d /tmp/ralph-test.XXXXXX)"
|
|
cd "$TEST_TEMP_DIR"
|
|
mkdir -p "$RALPH_DIR"
|
|
|
|
# Initialize files
|
|
echo "0" > "$CALL_COUNT_FILE"
|
|
echo "$(date +%Y%m%d%H)" > "$TIMESTAMP_FILE"
|
|
}
|
|
|
|
teardown() {
|
|
# Clean up
|
|
cd /
|
|
rm -rf "$TEST_TEMP_DIR"
|
|
}
|
|
|
|
# Helper function: can_make_call (extracted from ralph_loop.sh)
|
|
can_make_call() {
|
|
local calls_made=0
|
|
if [[ -f "$CALL_COUNT_FILE" ]]; then
|
|
calls_made=$(cat "$CALL_COUNT_FILE")
|
|
fi
|
|
|
|
if [[ $calls_made -ge $MAX_CALLS_PER_HOUR ]]; then
|
|
return 1 # Cannot make call
|
|
else
|
|
return 0 # Can make call
|
|
fi
|
|
}
|
|
|
|
# Helper function: increment_call_counter (extracted from ralph_loop.sh)
|
|
increment_call_counter() {
|
|
local calls_made=0
|
|
if [[ -f "$CALL_COUNT_FILE" ]]; then
|
|
calls_made=$(cat "$CALL_COUNT_FILE")
|
|
fi
|
|
|
|
((calls_made++))
|
|
echo "$calls_made" > "$CALL_COUNT_FILE"
|
|
echo "$calls_made"
|
|
}
|
|
|
|
# Test 1: can_make_call returns success when under limit
|
|
@test "can_make_call returns success when under limit" {
|
|
echo "50" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
|
|
run can_make_call
|
|
assert_success
|
|
}
|
|
|
|
# Test 2: can_make_call returns success when exactly at limit minus 1
|
|
@test "can_make_call returns success when at limit minus 1" {
|
|
echo "99" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
|
|
run can_make_call
|
|
assert_success
|
|
}
|
|
|
|
# Test 3: can_make_call returns failure when at limit
|
|
@test "can_make_call returns failure when at limit" {
|
|
echo "100" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
|
|
run can_make_call
|
|
assert_failure
|
|
}
|
|
|
|
# Test 4: can_make_call returns failure when over limit
|
|
@test "can_make_call returns failure when over limit" {
|
|
echo "150" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
|
|
run can_make_call
|
|
assert_failure
|
|
}
|
|
|
|
# Test 5: can_make_call returns success when file doesn't exist (0 calls)
|
|
@test "can_make_call returns success when call count file missing" {
|
|
rm -f "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
|
|
run can_make_call
|
|
assert_success
|
|
}
|
|
|
|
# Test 6: increment_call_counter increases from 0
|
|
@test "increment_call_counter increases from 0 to 1" {
|
|
echo "0" > "$CALL_COUNT_FILE"
|
|
|
|
result=$(increment_call_counter)
|
|
assert_equal "$result" "1"
|
|
assert_equal "$(cat $CALL_COUNT_FILE)" "1"
|
|
}
|
|
|
|
# Test 7: increment_call_counter increases from middle value
|
|
@test "increment_call_counter increases from 42 to 43" {
|
|
echo "42" > "$CALL_COUNT_FILE"
|
|
|
|
result=$(increment_call_counter)
|
|
assert_equal "$result" "43"
|
|
assert_equal "$(cat $CALL_COUNT_FILE)" "43"
|
|
}
|
|
|
|
# Test 8: increment_call_counter works near limit
|
|
@test "increment_call_counter increases from 99 to 100" {
|
|
echo "99" > "$CALL_COUNT_FILE"
|
|
|
|
result=$(increment_call_counter)
|
|
assert_equal "$result" "100"
|
|
assert_equal "$(cat $CALL_COUNT_FILE)" "100"
|
|
}
|
|
|
|
# Test 9: increment_call_counter works when file missing
|
|
@test "increment_call_counter creates file and sets to 1 when missing" {
|
|
rm -f "$CALL_COUNT_FILE"
|
|
|
|
result=$(increment_call_counter)
|
|
assert_equal "$result" "1"
|
|
assert_equal "$(cat $CALL_COUNT_FILE)" "1"
|
|
}
|
|
|
|
# Test 10: Rate limit with different MAX_CALLS value (50)
|
|
@test "can_make_call respects MAX_CALLS_PER_HOUR of 50" {
|
|
echo "49" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=50
|
|
|
|
run can_make_call
|
|
assert_success
|
|
|
|
echo "50" > "$CALL_COUNT_FILE"
|
|
run can_make_call
|
|
assert_failure
|
|
}
|
|
|
|
# Test 11: Rate limit with different MAX_CALLS value (25)
|
|
@test "can_make_call respects MAX_CALLS_PER_HOUR of 25" {
|
|
echo "24" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=25
|
|
|
|
run can_make_call
|
|
assert_success
|
|
|
|
echo "25" > "$CALL_COUNT_FILE"
|
|
run can_make_call
|
|
assert_failure
|
|
}
|
|
|
|
# Test 12: Counter persistence across multiple increments
|
|
@test "counter persists correctly across multiple increments" {
|
|
echo "0" > "$CALL_COUNT_FILE"
|
|
|
|
result1=$(increment_call_counter) # 1
|
|
result2=$(increment_call_counter) # 2
|
|
result3=$(increment_call_counter) # 3
|
|
result4=$(increment_call_counter) # 4
|
|
|
|
assert_equal "$result4" "4"
|
|
assert_equal "$(cat $CALL_COUNT_FILE)" "4"
|
|
}
|
|
|
|
# Test 13: Call count file contains only a number
|
|
@test "call count file contains valid integer" {
|
|
run increment_call_counter
|
|
|
|
# Check the call count file contains a valid integer
|
|
value=$(cat "$CALL_COUNT_FILE")
|
|
[[ "$value" =~ ^[0-9]+$ ]] || {
|
|
echo "Call count file does not contain valid integer: $value"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Test 14: Can make call with zero calls
|
|
@test "can_make_call returns success with zero calls made" {
|
|
echo "0" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=100
|
|
|
|
run can_make_call
|
|
assert_success
|
|
}
|
|
|
|
# Test 15: Edge case - very large MAX_CALLS value
|
|
@test "can_make_call works with large MAX_CALLS value" {
|
|
echo "5000" > "$CALL_COUNT_FILE"
|
|
export MAX_CALLS_PER_HOUR=10000
|
|
|
|
run can_make_call
|
|
assert_success
|
|
}
|