* 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>
269 lines
6.5 KiB
Bash
269 lines
6.5 KiB
Bash
#!/usr/bin/env bats
|
|
# Unit tests for lib/task_sources.sh
|
|
# Tests beads integration, GitHub integration, PRD extraction, and task normalization
|
|
|
|
load '../helpers/test_helper'
|
|
load '../helpers/fixtures'
|
|
|
|
# Path to task_sources.sh
|
|
TASK_SOURCES="${BATS_TEST_DIRNAME}/../../lib/task_sources.sh"
|
|
|
|
setup() {
|
|
# Create temporary test directory
|
|
TEST_DIR="$(mktemp -d)"
|
|
cd "$TEST_DIR"
|
|
|
|
# Source the library
|
|
source "$TASK_SOURCES"
|
|
}
|
|
|
|
teardown() {
|
|
if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
|
|
cd /
|
|
rm -rf "$TEST_DIR"
|
|
fi
|
|
}
|
|
|
|
# =============================================================================
|
|
# BEADS DETECTION (3 tests)
|
|
# =============================================================================
|
|
|
|
@test "check_beads_available returns false when no .beads directory" {
|
|
run check_beads_available
|
|
assert_failure
|
|
}
|
|
|
|
@test "check_beads_available returns false when bd command not found" {
|
|
mkdir -p .beads
|
|
# bd command likely won't exist in test environment
|
|
if command -v bd &>/dev/null; then
|
|
skip "bd command is available"
|
|
fi
|
|
run check_beads_available
|
|
assert_failure
|
|
}
|
|
|
|
@test "get_beads_count returns 0 when beads unavailable" {
|
|
run get_beads_count
|
|
assert_output "0"
|
|
}
|
|
|
|
# =============================================================================
|
|
# GITHUB DETECTION (3 tests)
|
|
# =============================================================================
|
|
|
|
@test "check_github_available returns false when no gh command" {
|
|
# gh command may not exist in test environment
|
|
if ! command -v gh &>/dev/null; then
|
|
run check_github_available
|
|
assert_failure
|
|
else
|
|
skip "gh command is available"
|
|
fi
|
|
}
|
|
|
|
@test "check_github_available returns false when not in git repo" {
|
|
run check_github_available
|
|
assert_failure
|
|
}
|
|
|
|
@test "get_github_issue_count returns 0 when GitHub unavailable" {
|
|
run get_github_issue_count
|
|
assert_output "0"
|
|
}
|
|
|
|
# =============================================================================
|
|
# PRD EXTRACTION (6 tests)
|
|
# =============================================================================
|
|
|
|
@test "extract_prd_tasks extracts checkbox items" {
|
|
cat > prd.md << 'EOF'
|
|
# Requirements
|
|
|
|
- [ ] Implement user authentication
|
|
- [x] Set up database
|
|
- [ ] Add API endpoints
|
|
EOF
|
|
|
|
run extract_prd_tasks "prd.md"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "Implement user authentication" ]]
|
|
[[ "$output" =~ "Add API endpoints" ]]
|
|
}
|
|
|
|
@test "extract_prd_tasks extracts numbered list items" {
|
|
cat > prd.md << 'EOF'
|
|
# Requirements
|
|
|
|
1. Implement user authentication
|
|
2. Set up database
|
|
3. Add API endpoints
|
|
EOF
|
|
|
|
run extract_prd_tasks "prd.md"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "Implement user authentication" ]]
|
|
}
|
|
|
|
@test "extract_prd_tasks returns empty for file without tasks" {
|
|
cat > prd.md << 'EOF'
|
|
# Empty Document
|
|
|
|
This document has no tasks.
|
|
EOF
|
|
|
|
run extract_prd_tasks "prd.md"
|
|
|
|
assert_success
|
|
}
|
|
|
|
@test "extract_prd_tasks returns error for missing file" {
|
|
run extract_prd_tasks "nonexistent.md"
|
|
assert_failure
|
|
}
|
|
|
|
@test "extract_prd_tasks normalizes checked items to unchecked" {
|
|
cat > prd.md << 'EOF'
|
|
- [x] Completed task
|
|
- [X] Another completed
|
|
EOF
|
|
|
|
run extract_prd_tasks "prd.md"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "[ ]" ]]
|
|
[[ ! "$output" =~ "[x]" ]]
|
|
[[ ! "$output" =~ "[X]" ]]
|
|
}
|
|
|
|
@test "extract_prd_tasks limits output to 30 tasks" {
|
|
# Create PRD with 40 tasks
|
|
{
|
|
echo "# Tasks"
|
|
for i in {1..40}; do
|
|
echo "- [ ] Task $i"
|
|
done
|
|
} > prd.md
|
|
|
|
run extract_prd_tasks "prd.md"
|
|
|
|
# Count the number of task lines
|
|
task_count=$(echo "$output" | grep -c '^\- \[' || echo "0")
|
|
[[ "$task_count" -le 30 ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# TASK NORMALIZATION (5 tests)
|
|
# =============================================================================
|
|
|
|
@test "normalize_tasks converts bullet points to checkboxes" {
|
|
input="- First task
|
|
* Second task"
|
|
|
|
run normalize_tasks "$input"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "- [ ] First task" ]]
|
|
[[ "$output" =~ "- [ ] Second task" ]]
|
|
}
|
|
|
|
@test "normalize_tasks converts numbered items to checkboxes" {
|
|
input="1. First task
|
|
2. Second task"
|
|
|
|
run normalize_tasks "$input"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "- [ ]" ]]
|
|
}
|
|
|
|
@test "normalize_tasks preserves existing checkboxes" {
|
|
input="- [ ] Already a task"
|
|
|
|
run normalize_tasks "$input"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "- [ ] Already a task" ]]
|
|
}
|
|
|
|
@test "normalize_tasks handles plain text lines" {
|
|
input="Plain text task"
|
|
|
|
run normalize_tasks "$input"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "- [ ] Plain text task" ]]
|
|
}
|
|
|
|
@test "normalize_tasks handles empty input" {
|
|
run normalize_tasks ""
|
|
assert_success
|
|
}
|
|
|
|
# =============================================================================
|
|
# TASK PRIORITIZATION (3 tests)
|
|
# =============================================================================
|
|
|
|
@test "prioritize_tasks puts critical tasks in High Priority" {
|
|
input="- [ ] Critical bug fix
|
|
- [ ] Normal task"
|
|
|
|
output=$(prioritize_tasks "$input" || true)
|
|
|
|
[[ "$output" =~ "## High Priority" ]]
|
|
# Critical should be before Medium
|
|
high_section="${output%%## Medium*}"
|
|
[[ "$high_section" =~ "Critical bug fix" ]]
|
|
}
|
|
|
|
@test "prioritize_tasks puts optional tasks in Low Priority" {
|
|
input="- [ ] Nice to have feature
|
|
- [ ] Normal task"
|
|
|
|
run prioritize_tasks "$input"
|
|
|
|
assert_success
|
|
[[ "$output" =~ "## Low Priority" ]]
|
|
low_section="${output##*## Low Priority}"
|
|
[[ "$low_section" =~ "Nice to have" ]]
|
|
}
|
|
|
|
@test "prioritize_tasks puts regular tasks in Medium Priority" {
|
|
input="- [ ] Regular task"
|
|
|
|
output=$(prioritize_tasks "$input" || true)
|
|
|
|
[[ "$output" =~ "## Medium Priority" ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# COMBINED IMPORT (3 tests)
|
|
# =============================================================================
|
|
|
|
@test "import_tasks_from_sources handles prd source" {
|
|
mkdir -p docs
|
|
cat > docs/prd.md << 'EOF'
|
|
# Requirements
|
|
- [ ] Test task
|
|
EOF
|
|
|
|
run import_tasks_from_sources "prd" "docs/prd.md" ""
|
|
|
|
assert_success
|
|
[[ "$output" =~ "Test task" ]]
|
|
}
|
|
|
|
@test "import_tasks_from_sources handles empty sources" {
|
|
run import_tasks_from_sources "" "" ""
|
|
|
|
assert_failure
|
|
}
|
|
|
|
@test "import_tasks_from_sources handles none source" {
|
|
run import_tasks_from_sources "none" "" ""
|
|
|
|
# 'none' doesn't import anything, so fails
|
|
assert_failure
|
|
}
|