ralph-claude-code/tests/unit/test_ralph_enable.bats
Frank Bria 910f794fcc
feat(enable): add ralph-enable wizard for existing projects (v0.11.0) (#124)
* feat(enable): add ralph-enable wizard for existing projects (v0.11.0)

Add interactive wizard and CI version for enabling Ralph in existing projects.

New commands:
- ralph-enable: Interactive 5-phase wizard for humans
- ralph-enable-ci: Non-interactive version with JSON output for CI/automation

New library components:
- lib/enable_core.sh: Shared logic for idempotency, project detection, templates
- lib/wizard_utils.sh: Interactive prompt utilities
- lib/task_sources.sh: Task import from beads, GitHub Issues, PRD documents

Features:
- Auto-detects project type (TypeScript, Python, Rust, Go)
- Auto-detects framework (Next.js, FastAPI, Django, Express)
- Imports tasks from beads, GitHub Issues, or PRD documents
- Generates .ralphrc project configuration file
- Idempotent: safe to run multiple times, respects existing files
- Exit codes: 0 (success), 1 (error), 2 (already enabled)

Updated:
- install.sh: Added new commands to global installation
- ralph_loop.sh: Loads .ralphrc configuration at startup

Tests: 75 new tests (30 enable_core + 23 task_sources + 22 integration)
Total: 396 tests passing (100% pass rate)

Closes #85, #121, #64, #87, #99

* fix(enable): address code review feedback

Fixes from PR #124 review:

1. sed -i portability (ralph_enable.sh:456)
   - Use portable sed + mv pattern instead of GNU-only sed -i

2. sed regex portability (lib/task_sources.sh)
   - Replace \s with POSIX [[:space:]] character class
   - Add sed -E flag for extended regex

3. jq availability check (ralph_enable_ci.sh:177)
   - Add check for jq when --json flag is used

4. Unused filter parameter (lib/task_sources.sh:44)
   - Pass filter to bd list --filter command

5. Word-splitting in select_multiple (ralph_enable.sh:322)
   - Return comma-separated indices instead of space-separated text
   - Update caller to parse indices correctly

6. Missing || true for check_existing_ralph (ralph_enable.sh:185)
   - Prevent set -e from exiting on non-zero return

7. select_multiple stdout corruption (lib/wizard_utils.sh)
   - Redirect interactive output to stderr
   - Only final result goes to stdout

8. Color variables not exported (lib/wizard_utils.sh:12)
   - Export WIZARD_* color variables for subshells

9. select_option infinite loop (lib/wizard_utils.sh:179)
   - Add guard for empty options array

* fix(tests): add missing mocks and exports for new enable feature

- Add RESPONSE_ANALYSIS_FILE export to test_session_continuity.bats setup
- Add mock ralph_enable.sh and ralph_enable_ci.sh to test_installation.bats
- Add mock lib files: enable_core.sh, wizard_utils.sh, task_sources.sh, timeout_utils.sh

All 396 tests now pass.

* fix(config): fix critical issues from PR review

1. .ralphrc Configuration Loading Fix:
   - Captured env var state BEFORE setting defaults with _env_* variables
   - load_ralphrc now only restores values explicitly set by environment
   - .ralphrc settings are now properly applied (not overwritten by defaults)

2. sed Command Injection Fix:
   - Replaced sed with awk for .ralphrc updates in ralph_enable.sh
   - awk -v pattern safely handles user input without shell injection risk

3. Shell Injection Fix in safe_create_file():
   - Replaced echo with printf '%s\n' for safer content handling
   - Prevents issues with backslashes, -n, and special characters

4. Specific Error Codes:
   - Added ENABLE_INVALID_ARGS=3 for argument errors
   - Added ENABLE_FILE_NOT_FOUND=4 for missing files
   - Added ENABLE_DEPENDENCY_MISSING=5 for missing deps (e.g., jq)
   - Added ENABLE_PERMISSION_DENIED=6 for permission errors
   - Updated ralph_enable.sh and ralph_enable_ci.sh to use specific codes

5. Added tests for .ralphrc loading pattern verification

Test count: 398 (up from 396)

* fix(enable): make --force flag actually overwrite existing files

The --force flag was accepted but safe_create_file() always skipped
existing files regardless of ENABLE_FORCE value.

Changes:
- safe_create_file() now checks ENABLE_FORCE environment variable
- When ENABLE_FORCE="true", overwrites existing files instead of skipping
- Added proper logging for overwrite operations

Added tests:
- Verify enable_ralph_in_directory actually changes file contents with --force
- Test safe_create_file overwrites when ENABLE_FORCE is true
- Test safe_create_file skips when ENABLE_FORCE is false

Test count: 400 (up from 398)

---------

Co-authored-by: Test User <test@example.com>
2026-01-25 14:37:25 -07:00

268 lines
7.1 KiB
Bash

#!/usr/bin/env bats
# Integration tests for ralph_enable.sh and ralph_enable_ci.sh
# Tests the full enable wizard flow and CI version
load '../helpers/test_helper'
load '../helpers/fixtures'
# Paths to scripts
RALPH_ENABLE="${BATS_TEST_DIRNAME}/../../ralph_enable.sh"
RALPH_ENABLE_CI="${BATS_TEST_DIRNAME}/../../ralph_enable_ci.sh"
setup() {
# Create temporary test directory
TEST_DIR="$(mktemp -d)"
cd "$TEST_DIR"
# Initialize git repo (required by some detection)
git init > /dev/null 2>&1
git config user.email "test@example.com"
git config user.name "Test User"
}
teardown() {
if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
cd /
rm -rf "$TEST_DIR"
fi
}
# =============================================================================
# HELP AND VERSION (4 tests)
# =============================================================================
@test "ralph enable --help shows usage information" {
run bash "$RALPH_ENABLE" --help
assert_success
[[ "$output" =~ "Usage:" ]]
[[ "$output" =~ "--from" ]]
[[ "$output" =~ "--force" ]]
}
@test "ralph enable --version shows version" {
run bash "$RALPH_ENABLE" --version
assert_success
[[ "$output" =~ "version" ]]
}
@test "ralph enable-ci --help shows usage information" {
run bash "$RALPH_ENABLE_CI" --help
assert_success
[[ "$output" =~ "Usage:" ]]
[[ "$output" =~ "Exit Codes:" ]]
}
@test "ralph enable-ci --version shows version" {
run bash "$RALPH_ENABLE_CI" --version
assert_success
[[ "$output" =~ "version" ]]
}
# =============================================================================
# CI VERSION TESTS (8 tests)
# =============================================================================
@test "ralph enable-ci creates .ralph structure in empty directory" {
run bash "$RALPH_ENABLE_CI" --from none
assert_success
[[ -d ".ralph" ]]
[[ -f ".ralph/PROMPT.md" ]]
[[ -f ".ralph/@fix_plan.md" ]]
[[ -f ".ralph/@AGENT.md" ]]
}
@test "ralph enable-ci creates .ralphrc configuration" {
run bash "$RALPH_ENABLE_CI" --from none
assert_success
[[ -f ".ralphrc" ]]
}
@test "ralph enable-ci detects TypeScript project" {
cat > package.json << 'EOF'
{
"name": "test-ts-project",
"devDependencies": {
"typescript": "^5.0.0"
}
}
EOF
run bash "$RALPH_ENABLE_CI" --from none
assert_success
grep -q "PROJECT_TYPE=\"typescript\"" .ralphrc
}
@test "ralph enable-ci detects Python project" {
cat > pyproject.toml << 'EOF'
[project]
name = "test-python-project"
EOF
run bash "$RALPH_ENABLE_CI" --from none
assert_success
grep -q "PROJECT_TYPE=\"python\"" .ralphrc
}
@test "ralph enable-ci respects --project-name override" {
run bash "$RALPH_ENABLE_CI" --from none --project-name "custom-name"
assert_success
grep -q "PROJECT_NAME=\"custom-name\"" .ralphrc
}
@test "ralph enable-ci respects --project-type override" {
run bash "$RALPH_ENABLE_CI" --from none --project-type "rust"
assert_success
grep -q "PROJECT_TYPE=\"rust\"" .ralphrc
}
@test "ralph enable-ci returns exit code 2 when already enabled" {
# First enable
bash "$RALPH_ENABLE_CI" --from none >/dev/null 2>&1
# Second enable without force
run bash "$RALPH_ENABLE_CI" --from none
assert_equal "$status" 2
}
@test "ralph enable-ci --force overwrites existing configuration" {
# First enable
bash "$RALPH_ENABLE_CI" --from none --project-name "old-name" >/dev/null 2>&1
# Second enable with force
run bash "$RALPH_ENABLE_CI" --from none --force --project-name "new-name"
assert_success
}
# =============================================================================
# JSON OUTPUT TESTS (3 tests)
# =============================================================================
@test "ralph enable-ci --json outputs valid JSON on success" {
run bash "$RALPH_ENABLE_CI" --from none --json
assert_success
# Validate JSON structure
echo "$output" | jq -e '.success == true'
echo "$output" | jq -e '.project_name'
echo "$output" | jq -e '.files_created'
}
@test "ralph enable-ci --json includes project info" {
cat > package.json << 'EOF'
{"name": "json-test"}
EOF
run bash "$RALPH_ENABLE_CI" --from none --json
assert_success
echo "$output" | jq -e '.project_name == "json-test"'
}
@test "ralph enable-ci --json returns proper structure when already enabled" {
bash "$RALPH_ENABLE_CI" --from none >/dev/null 2>&1
run bash "$RALPH_ENABLE_CI" --from none --json
assert_equal "$status" 2
echo "$output" | jq -e '.code == 2'
}
# =============================================================================
# PRD IMPORT TESTS (2 tests)
# =============================================================================
@test "ralph enable-ci imports tasks from PRD file" {
mkdir -p docs
cat > docs/requirements.md << 'EOF'
# Project Requirements
- [ ] Implement user authentication
- [ ] Add API endpoints
- [ ] Create database schema
EOF
run bash "$RALPH_ENABLE_CI" --from prd --prd docs/requirements.md
assert_success
# Check that tasks were imported
grep -q "authentication\|API\|database" .ralph/@fix_plan.md
}
@test "ralph enable-ci fails gracefully with missing PRD file" {
run bash "$RALPH_ENABLE_CI" --from prd --prd nonexistent.md
assert_failure
}
# =============================================================================
# IDEMPOTENCY TESTS (3 tests)
# =============================================================================
@test "ralph enable-ci is idempotent with force flag" {
bash "$RALPH_ENABLE_CI" --from none >/dev/null 2>&1
# Add a file to .ralph
echo "custom file" > .ralph/custom.txt
run bash "$RALPH_ENABLE_CI" --from none --force
assert_success
# Custom file should still exist (we don't delete extra files)
[[ -f ".ralph/custom.txt" ]]
}
@test "ralph enable-ci preserves existing .ralph subdirectories" {
bash "$RALPH_ENABLE_CI" --from none >/dev/null 2>&1
# Add custom content
echo "spec content" > .ralph/specs/custom_spec.md
run bash "$RALPH_ENABLE_CI" --from none --force
assert_success
[[ -f ".ralph/specs/custom_spec.md" ]]
}
@test "ralph enable-ci does not overwrite existing files without force" {
mkdir -p .ralph
echo "original prompt" > .ralph/PROMPT.md
echo "original fix plan" > .ralph/@fix_plan.md
echo "original agent" > .ralph/@AGENT.md
run bash "$RALPH_ENABLE_CI" --from none
assert_equal "$status" 2
# Verify original content preserved
assert_equal "$(cat .ralph/PROMPT.md)" "original prompt"
}
# =============================================================================
# QUIET MODE TESTS (2 tests)
# =============================================================================
@test "ralph enable-ci --quiet suppresses output" {
run bash "$RALPH_ENABLE_CI" --from none --quiet
assert_success
# Output should be minimal
[[ -z "$output" ]] || [[ ! "$output" =~ "Detected" ]]
}
@test "ralph enable-ci --quiet still creates files" {
run bash "$RALPH_ENABLE_CI" --from none --quiet
assert_success
[[ -f ".ralph/PROMPT.md" ]]
}