* refactor(naming): remove @ prefix from on-disk filenames BREAKING CHANGE: Renames @fix_plan.md → fix_plan.md and @AGENT.md → AGENT.md This change improves POSIX compliance and compatibility with command-line tools. The @ prefix was originally used to avoid naming conflicts, but with the .ralph/ folder structure introduced in v0.10.0, this convention is no longer necessary. Changes: - Update all scripts to use new naming (fix_plan.md, AGENT.md) - Update migration script to handle both old and new naming conventions - Update templates to use new naming - Update all documentation references - Update all 420 tests to expect new naming (TDD approach) Migration: - Existing projects with @-prefixed files will be automatically renamed when running ralph-migrate - Projects already using the new naming will continue to work unchanged * docs(claude): update file naming conventions section * fix(migrate): prevent orphaned @-prefixed files during migration When both root/@fix_plan.md and .ralph/@fix_plan.md exist, the root file now takes priority and the .ralph/@fix_plan.md is removed (backup exists). This prevents orphaned legacy files after migration. --------- Co-authored-by: Test User <test@example.com>
1200 lines
33 KiB
Bash
1200 lines
33 KiB
Bash
#!/usr/bin/env bats
|
|
# Integration tests for ralph-import command functionality
|
|
# Tests PRD to Ralph format conversion with mocked Claude Code CLI
|
|
|
|
load '../helpers/test_helper'
|
|
load '../helpers/mocks'
|
|
load '../helpers/fixtures'
|
|
|
|
# Root directory of the project (for accessing ralph_import.sh)
|
|
PROJECT_ROOT="${BATS_TEST_DIRNAME}/../.."
|
|
|
|
setup() {
|
|
# Create temporary test directory
|
|
TEST_DIR="$(mktemp -d)"
|
|
ORIGINAL_DIR="$(pwd)"
|
|
cd "$TEST_DIR"
|
|
|
|
# Initialize git repo (required by ralph_import.sh)
|
|
git init > /dev/null 2>&1
|
|
git config user.email "test@example.com"
|
|
git config user.name "Test User"
|
|
|
|
# Set up mock command directory (prepend to PATH)
|
|
MOCK_BIN_DIR="$TEST_DIR/.mock_bin"
|
|
mkdir -p "$MOCK_BIN_DIR"
|
|
export PATH="$MOCK_BIN_DIR:$PATH"
|
|
|
|
# Create mock ralph-setup command (with .ralph/ subfolder structure)
|
|
cat > "$MOCK_BIN_DIR/ralph-setup" << 'MOCK_SETUP_EOF'
|
|
#!/bin/bash
|
|
# Mock ralph-setup that creates project structure with .ralph/ subfolder
|
|
project_name="${1:-test-project}"
|
|
mkdir -p "$project_name"/src
|
|
mkdir -p "$project_name"/.ralph/{specs/stdlib,examples,logs,docs/generated}
|
|
cd "$project_name"
|
|
git init > /dev/null 2>&1
|
|
git config user.email "test@example.com"
|
|
git config user.name "Test User"
|
|
# Create basic template files in .ralph/ subfolder
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
|
|
## Context
|
|
You are Ralph, an autonomous AI development agent.
|
|
|
|
## Current Objectives
|
|
- Study specs/* to learn about the project specifications
|
|
- Review fix_plan.md for current priorities
|
|
|
|
## Key Principles
|
|
- ONE task per loop
|
|
|
|
## Testing Guidelines (CRITICAL)
|
|
- LIMIT testing to ~20% of your total effort
|
|
EOF
|
|
|
|
cat > ".ralph/fix_plan.md" << 'EOF'
|
|
# Ralph Fix Plan
|
|
|
|
## High Priority
|
|
- [ ] Task 1
|
|
|
|
## Medium Priority
|
|
- [ ] Task 2
|
|
|
|
## Low Priority
|
|
- [ ] Task 3
|
|
|
|
## Completed
|
|
- [x] Project initialization
|
|
EOF
|
|
|
|
cat > ".ralph/AGENT.md" << 'EOF'
|
|
# Agent Build Instructions
|
|
|
|
## Project Setup
|
|
npm install
|
|
EOF
|
|
|
|
git add -A > /dev/null 2>&1
|
|
git commit -m "Initial project setup" > /dev/null 2>&1
|
|
echo "Created Ralph project: $project_name"
|
|
MOCK_SETUP_EOF
|
|
chmod +x "$MOCK_BIN_DIR/ralph-setup"
|
|
|
|
# Create mock claude command for PRD conversion
|
|
# Default behavior: create the expected output files
|
|
create_mock_claude_success
|
|
|
|
# Export environment variables
|
|
export CLAUDE_CODE_CMD="claude"
|
|
}
|
|
|
|
teardown() {
|
|
# Return to original directory
|
|
cd "$ORIGINAL_DIR" 2>/dev/null || cd /
|
|
|
|
# Clean up test directory
|
|
if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
|
|
rm -rf "$TEST_DIR"
|
|
fi
|
|
}
|
|
|
|
# Helper: Create mock claude command that succeeds
|
|
create_mock_claude_success() {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_EOF'
|
|
#!/bin/bash
|
|
# Mock Claude Code CLI that creates expected output files in .ralph/ subfolder
|
|
|
|
# Handle --version flag first (before reading stdin)
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
# Read from stdin (conversion prompt)
|
|
cat > /dev/null
|
|
|
|
# Ensure .ralph directory exists
|
|
mkdir -p .ralph/specs
|
|
|
|
# Create PROMPT.md with Ralph format in .ralph/
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
|
|
## Context
|
|
You are Ralph, an autonomous AI development agent working on a Task Management App project.
|
|
|
|
## Current Objectives
|
|
1. Study specs/* to learn about the project specifications
|
|
2. Review fix_plan.md for current priorities
|
|
3. Implement the highest priority item using best practices
|
|
4. Use parallel subagents for complex tasks (max 100 concurrent)
|
|
5. Run tests after each implementation
|
|
6. Update documentation and fix_plan.md
|
|
|
|
## Key Principles
|
|
- ONE task per loop - focus on the most important thing
|
|
- Search the codebase before assuming something isn't implemented
|
|
- Use subagents for expensive operations (file searching, analysis)
|
|
- Write comprehensive tests with clear documentation
|
|
- Update fix_plan.md with your learnings
|
|
- Commit working changes with descriptive messages
|
|
|
|
## Testing Guidelines (CRITICAL)
|
|
- LIMIT testing to ~20% of your total effort per loop
|
|
- PRIORITIZE: Implementation > Documentation > Tests
|
|
- Only write tests for NEW functionality you implement
|
|
- Do NOT refactor existing tests unless broken
|
|
- Focus on CORE functionality first, comprehensive testing later
|
|
|
|
## Project Requirements
|
|
- User authentication and authorization
|
|
- Task CRUD operations
|
|
- Team collaboration features
|
|
- Real-time updates
|
|
|
|
## Technical Constraints
|
|
- Frontend: React.js with TypeScript
|
|
- Backend: Node.js with Express
|
|
- Database: PostgreSQL
|
|
|
|
## Success Criteria
|
|
- Users can create and manage tasks efficiently
|
|
- Team collaboration features work seamlessly
|
|
- App loads quickly (<2s initial load)
|
|
|
|
## Current Task
|
|
Follow fix_plan.md and choose the most important item to implement next.
|
|
EOF
|
|
|
|
# Create fix_plan.md in .ralph/
|
|
cat > ".ralph/fix_plan.md" << 'EOF'
|
|
# Ralph Fix Plan
|
|
|
|
## High Priority
|
|
- [ ] Set up user authentication with JWT
|
|
- [ ] Implement task CRUD API endpoints
|
|
- [ ] Create task list UI component
|
|
|
|
## Medium Priority
|
|
- [ ] Add team/workspace management
|
|
- [ ] Implement task assignment features
|
|
- [ ] Add due date and reminder functionality
|
|
|
|
## Low Priority
|
|
- [ ] Real-time updates with WebSocket
|
|
- [ ] Task comments and attachments
|
|
- [ ] Mobile PWA support
|
|
|
|
## Completed
|
|
- [x] Project initialization
|
|
|
|
## Notes
|
|
- Focus on MVP functionality first
|
|
- Ensure each feature is properly tested
|
|
- Update this file after each major milestone
|
|
EOF
|
|
|
|
# Create specs/requirements.md in .ralph/specs/
|
|
cat > .ralph/specs/requirements.md << 'EOF'
|
|
# Technical Specifications
|
|
|
|
## System Architecture
|
|
- Frontend: React.js SPA with TypeScript
|
|
- Backend: Node.js REST API with Express
|
|
- Database: PostgreSQL with Prisma ORM
|
|
- Authentication: JWT with refresh tokens
|
|
|
|
## Data Models
|
|
|
|
### User
|
|
- id: UUID
|
|
- email: string (unique)
|
|
- password_hash: string
|
|
- name: string
|
|
- avatar_url: string (optional)
|
|
- created_at: timestamp
|
|
|
|
### Task
|
|
- id: UUID
|
|
- title: string
|
|
- description: text (optional)
|
|
- priority: enum (high, medium, low)
|
|
- due_date: timestamp (optional)
|
|
- completed: boolean
|
|
- user_id: UUID (foreign key)
|
|
- created_at: timestamp
|
|
|
|
## API Specifications
|
|
|
|
### Authentication
|
|
- POST /api/auth/register - User registration
|
|
- POST /api/auth/login - User login
|
|
- POST /api/auth/refresh - Refresh token
|
|
- POST /api/auth/logout - User logout
|
|
|
|
### Tasks
|
|
- GET /api/tasks - List user's tasks
|
|
- POST /api/tasks - Create task
|
|
- GET /api/tasks/:id - Get task details
|
|
- PUT /api/tasks/:id - Update task
|
|
- DELETE /api/tasks/:id - Delete task
|
|
|
|
## Performance Requirements
|
|
- Initial page load: <2 seconds
|
|
- API response time: <200ms
|
|
- Support 100 concurrent users
|
|
|
|
## Security Considerations
|
|
- Password hashing with bcrypt
|
|
- HTTPS required in production
|
|
- Rate limiting on auth endpoints
|
|
- Input validation on all endpoints
|
|
EOF
|
|
|
|
echo "Mock: Claude Code conversion completed successfully"
|
|
exit 0
|
|
MOCK_CLAUDE_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
}
|
|
|
|
# Helper: Create mock claude command that fails
|
|
create_mock_claude_failure() {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_FAIL_EOF'
|
|
#!/bin/bash
|
|
# Mock Claude Code CLI that fails
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Error: Mock Claude Code failed"
|
|
exit 1
|
|
MOCK_CLAUDE_FAIL_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
}
|
|
|
|
# Helper: Remove ralph-setup from mock bin (simulate not installed)
|
|
remove_ralph_setup_mock() {
|
|
rm -f "$MOCK_BIN_DIR/ralph-setup"
|
|
}
|
|
|
|
# =============================================================================
|
|
# FILE FORMAT SUPPORT TESTS
|
|
# =============================================================================
|
|
|
|
# Test 1: ralph-import with .md file
|
|
@test "ralph-import accepts and processes .md file format" {
|
|
# Create sample PRD markdown file
|
|
create_sample_prd_md "my-project-prd.md"
|
|
|
|
# Run import
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "my-project-prd.md"
|
|
|
|
# Should succeed
|
|
assert_success
|
|
|
|
# Project directory should be created
|
|
assert_dir_exists "my-project-prd"
|
|
|
|
# Source file should be copied to project
|
|
assert_file_exists "my-project-prd/my-project-prd.md"
|
|
}
|
|
|
|
# Test 2: ralph-import with .txt file
|
|
@test "ralph-import accepts and processes .txt file format" {
|
|
# Create sample .txt PRD
|
|
create_sample_prd_txt "requirements.txt"
|
|
|
|
# Run import
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "requirements.txt"
|
|
|
|
# Should succeed
|
|
assert_success
|
|
|
|
# Project directory should be created (name from filename)
|
|
assert_dir_exists "requirements"
|
|
|
|
# Source file should be copied
|
|
assert_file_exists "requirements/requirements.txt"
|
|
}
|
|
|
|
# Test 3: ralph-import with .json file
|
|
@test "ralph-import accepts and processes .json file format" {
|
|
# Create sample JSON PRD
|
|
create_sample_prd_json "project-spec.json"
|
|
|
|
# Run import
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "project-spec.json"
|
|
|
|
# Should succeed
|
|
assert_success
|
|
|
|
# Project directory should be created
|
|
assert_dir_exists "project-spec"
|
|
|
|
# Source file should be copied
|
|
assert_file_exists "project-spec/project-spec.json"
|
|
}
|
|
|
|
# =============================================================================
|
|
# OUTPUT FILE CREATION TESTS
|
|
# =============================================================================
|
|
|
|
# Test 4: ralph-import creates PROMPT.md
|
|
@test "ralph-import creates PROMPT.md with Ralph instructions" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
assert_success
|
|
|
|
# PROMPT.md should exist in .ralph/ subfolder
|
|
assert_file_exists "test-app/.ralph/PROMPT.md"
|
|
|
|
# Check key sections exist
|
|
run grep -c "Ralph Development Instructions" "test-app/.ralph/PROMPT.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
run grep -c "Current Objectives" "test-app/.ralph/PROMPT.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
run grep -c "Key Principles" "test-app/.ralph/PROMPT.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
run grep -c "Testing Guidelines" "test-app/.ralph/PROMPT.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
}
|
|
|
|
# Test 5: ralph-import creates fix_plan.md
|
|
@test "ralph-import creates fix_plan.md with prioritized tasks" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
assert_success
|
|
|
|
# fix_plan.md should exist in .ralph/ subfolder
|
|
assert_file_exists "test-app/.ralph/fix_plan.md"
|
|
|
|
# Check structure includes priority sections
|
|
run grep -c "High Priority" "test-app/.ralph/fix_plan.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
run grep -c "Medium Priority" "test-app/.ralph/fix_plan.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
run grep -c "Low Priority" "test-app/.ralph/fix_plan.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
run grep -c "Completed" "test-app/.ralph/fix_plan.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
|
|
# Check checkbox format
|
|
run grep -E "^\- \[[ x]\]" "test-app/.ralph/fix_plan.md"
|
|
assert_success
|
|
}
|
|
|
|
# Test 6: ralph-import creates specs/requirements.md
|
|
@test "ralph-import creates specs/requirements.md with technical specs" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
assert_success
|
|
|
|
# specs directory should exist in .ralph/ subfolder
|
|
assert_dir_exists "test-app/.ralph/specs"
|
|
|
|
# requirements.md should exist in .ralph/specs/
|
|
assert_file_exists "test-app/.ralph/specs/requirements.md"
|
|
|
|
# Check technical specification content
|
|
run grep -c "Technical Specifications" "test-app/.ralph/specs/requirements.md"
|
|
assert_success
|
|
[[ "$output" -ge 1 ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# PROJECT NAMING TESTS
|
|
# =============================================================================
|
|
|
|
# Test 7: ralph-import with custom project name
|
|
@test "ralph-import uses custom project name when provided" {
|
|
create_sample_prd_md "generic-prd.md"
|
|
|
|
# Run with custom project name
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "generic-prd.md" "my-custom-project"
|
|
|
|
assert_success
|
|
|
|
# Custom project directory should be created
|
|
assert_dir_exists "my-custom-project"
|
|
|
|
# Files should be in custom-named directory under .ralph/ subfolder
|
|
assert_file_exists "my-custom-project/.ralph/PROMPT.md"
|
|
assert_file_exists "my-custom-project/.ralph/fix_plan.md"
|
|
assert_file_exists "my-custom-project/.ralph/specs/requirements.md"
|
|
|
|
# Default name directory should NOT exist
|
|
[[ ! -d "generic-prd" ]]
|
|
}
|
|
|
|
# Test 8: ralph-import auto-detects name from filename
|
|
@test "ralph-import extracts project name from filename when not provided" {
|
|
create_sample_prd_md "awesome-app-requirements.md"
|
|
|
|
# Run without custom name
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "awesome-app-requirements.md"
|
|
|
|
assert_success
|
|
|
|
# Project name should be extracted from filename (without extension)
|
|
assert_dir_exists "awesome-app-requirements"
|
|
|
|
# Files should be in auto-named directory under .ralph/ subfolder
|
|
assert_file_exists "awesome-app-requirements/.ralph/PROMPT.md"
|
|
}
|
|
|
|
# =============================================================================
|
|
# ERROR HANDLING TESTS
|
|
# =============================================================================
|
|
|
|
# Test 9: ralph-import missing source file error
|
|
@test "ralph-import fails gracefully when source file does not exist" {
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "nonexistent-file.md"
|
|
|
|
# Should fail with error code 1
|
|
assert_failure
|
|
|
|
# Error message should mention missing file
|
|
[[ "$output" == *"Source file does not exist"* ]]
|
|
|
|
# No project directory should be created
|
|
[[ ! -d "nonexistent-file" ]]
|
|
}
|
|
|
|
# Test 10: ralph-import dependency check (ralph not installed)
|
|
@test "ralph-import fails when ralph-setup is not installed" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
# Remove ralph-setup from mock path AND isolate from system PATH
|
|
# Use a completely isolated PATH with only essential system tools
|
|
remove_ralph_setup_mock
|
|
|
|
# Save original PATH and use restricted PATH that excludes ralph-setup
|
|
local ORIGINAL_PATH="$PATH"
|
|
export PATH="$MOCK_BIN_DIR:/usr/bin:/bin"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
# Restore original PATH
|
|
export PATH="$ORIGINAL_PATH"
|
|
|
|
# Should fail
|
|
assert_failure
|
|
|
|
# Error message should mention Ralph not installed
|
|
[[ "$output" == *"Ralph not installed"* ]] || [[ "$output" == *"ralph-setup"* ]]
|
|
}
|
|
|
|
# Test 11: ralph-import conversion failure handling
|
|
@test "ralph-import handles Claude Code conversion failure gracefully" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
# Set up mock to fail
|
|
create_mock_claude_failure
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
# Should fail
|
|
assert_failure
|
|
|
|
# Error message should mention conversion failure
|
|
[[ "$output" == *"PRD conversion failed"* ]] || [[ "$output" == *"failed"* ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# HELP AND USAGE TESTS
|
|
# =============================================================================
|
|
|
|
# Test 12: ralph-import with no arguments shows help
|
|
@test "ralph-import shows help when called with no arguments" {
|
|
run bash "$PROJECT_ROOT/ralph_import.sh"
|
|
|
|
# Should succeed (help is not an error)
|
|
assert_success
|
|
|
|
# Should display usage information
|
|
[[ "$output" == *"Usage"* ]]
|
|
[[ "$output" == *"source-file"* ]]
|
|
}
|
|
|
|
# Test 13: ralph-import --help shows full help
|
|
@test "ralph-import --help shows full help with examples" {
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" --help
|
|
|
|
# Should succeed
|
|
assert_success
|
|
|
|
# Should display help sections
|
|
[[ "$output" == *"Usage"* ]]
|
|
[[ "$output" == *"Arguments"* ]]
|
|
[[ "$output" == *"Examples"* ]]
|
|
[[ "$output" == *"Supported formats"* ]]
|
|
}
|
|
|
|
# Test 14: ralph-import -h shows help (short form)
|
|
@test "ralph-import -h shows help" {
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" -h
|
|
|
|
assert_success
|
|
[[ "$output" == *"Usage"* ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# CONVERSION PROMPT TESTS
|
|
# =============================================================================
|
|
|
|
# Test 15: ralph-import cleans up temporary conversion prompt
|
|
@test "ralph-import cleans up .ralph_conversion_prompt.md after conversion" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
assert_success
|
|
|
|
# Temporary prompt file should NOT exist in project directory
|
|
[[ ! -f "test-app/.ralph_conversion_prompt.md" ]]
|
|
}
|
|
|
|
# Test 16: ralph-import outputs completion message with next steps
|
|
@test "ralph-import shows success message with next steps" {
|
|
create_sample_prd_md "test-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "test-app.md"
|
|
|
|
assert_success
|
|
|
|
# Should show success message
|
|
[[ "$output" == *"successfully"* ]] || [[ "$output" == *"SUCCESS"* ]]
|
|
|
|
# Should show next steps
|
|
[[ "$output" == *"Next steps"* ]] || [[ "$output" == *"ralph --monitor"* ]]
|
|
}
|
|
|
|
# =============================================================================
|
|
# FULL WORKFLOW INTEGRATION TESTS
|
|
# =============================================================================
|
|
|
|
# Test 17: Complete import workflow creates valid Ralph project
|
|
@test "full workflow creates complete Ralph project structure" {
|
|
create_sample_prd_md "my-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "my-app.md"
|
|
|
|
assert_success
|
|
|
|
# Verify complete project structure with .ralph/ subfolder
|
|
assert_dir_exists "my-app"
|
|
assert_dir_exists "my-app/.ralph/specs"
|
|
assert_dir_exists "my-app/src"
|
|
assert_dir_exists "my-app/.ralph/logs"
|
|
assert_dir_exists "my-app/.ralph/docs/generated"
|
|
|
|
# Verify all required files in .ralph/ subfolder
|
|
assert_file_exists "my-app/.ralph/PROMPT.md"
|
|
assert_file_exists "my-app/.ralph/fix_plan.md"
|
|
assert_file_exists "my-app/.ralph/AGENT.md"
|
|
assert_file_exists "my-app/.ralph/specs/requirements.md"
|
|
|
|
# Verify source PRD was copied
|
|
assert_file_exists "my-app/my-app.md"
|
|
}
|
|
|
|
# Test 18: Imported project is a valid git repository
|
|
@test "imported project is initialized as git repository" {
|
|
create_sample_prd_md "git-test.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "git-test.md"
|
|
|
|
assert_success
|
|
|
|
# Project should have .git directory
|
|
assert_dir_exists "git-test/.git"
|
|
|
|
# Should be a valid git repo
|
|
cd "git-test"
|
|
run git rev-parse --is-inside-work-tree
|
|
assert_success
|
|
assert_equal "$output" "true"
|
|
}
|
|
|
|
# =============================================================================
|
|
# EDGE CASE TESTS
|
|
# =============================================================================
|
|
|
|
# Test 19: ralph-import handles project names with hyphens
|
|
@test "ralph-import handles project names with hyphens correctly" {
|
|
create_sample_prd_md "my-awesome-app.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "my-awesome-app.md"
|
|
|
|
assert_success
|
|
assert_dir_exists "my-awesome-app"
|
|
}
|
|
|
|
# Test 20: ralph-import handles uppercase filenames
|
|
@test "ralph-import handles uppercase in filename" {
|
|
create_sample_prd_md "MyProject.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "MyProject.md"
|
|
|
|
assert_success
|
|
assert_dir_exists "MyProject"
|
|
}
|
|
|
|
# Test 21: ralph-import handles path with directories
|
|
@test "ralph-import handles source file in subdirectory" {
|
|
mkdir -p "docs/specs"
|
|
create_sample_prd_md "docs/specs/project-prd.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "docs/specs/project-prd.md"
|
|
|
|
assert_success
|
|
|
|
# Project should be created with basename (without path)
|
|
assert_dir_exists "project-prd"
|
|
}
|
|
|
|
# Test 22: ralph-import preserves original PRD content
|
|
@test "ralph-import preserves original PRD content in project" {
|
|
# Create PRD with unique content
|
|
cat > "unique-prd.md" << 'EOF'
|
|
# Unique Test PRD
|
|
|
|
## Unique Identifier: XYZ-12345
|
|
|
|
This is a unique test PRD with identifiable content.
|
|
|
|
## Requirements
|
|
- Unique requirement A
|
|
- Unique requirement B
|
|
EOF
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "unique-prd.md"
|
|
|
|
assert_success
|
|
|
|
# Original content should be preserved
|
|
run grep "Unique Identifier: XYZ-12345" "unique-prd/unique-prd.md"
|
|
assert_success
|
|
|
|
run grep "Unique requirement A" "unique-prd/unique-prd.md"
|
|
assert_success
|
|
}
|
|
|
|
# =============================================================================
|
|
# MODERN CLI FEATURES TESTS (Phase 1.1)
|
|
# Tests for --output-format json, --allowedTools, and JSON response parsing
|
|
# =============================================================================
|
|
|
|
# Helper: Create mock claude command that outputs JSON format
|
|
create_mock_claude_json_success() {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_JSON_EOF'
|
|
#!/bin/bash
|
|
# Mock Claude Code CLI that outputs JSON format and creates expected files in .ralph/
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
# Read from stdin (conversion prompt)
|
|
cat > /dev/null
|
|
|
|
# Ensure .ralph directory exists
|
|
mkdir -p .ralph/specs
|
|
|
|
# Create PROMPT.md with Ralph format in .ralph/
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
|
|
## Context
|
|
You are Ralph, an autonomous AI development agent working on a Task Management App project.
|
|
|
|
## Current Objectives
|
|
1. Study specs/* to learn about the project specifications
|
|
2. Review fix_plan.md for current priorities
|
|
|
|
## Key Principles
|
|
- ONE task per loop
|
|
|
|
## Testing Guidelines (CRITICAL)
|
|
- LIMIT testing to ~20% of your total effort
|
|
EOF
|
|
|
|
# Create fix_plan.md in .ralph/
|
|
cat > ".ralph/fix_plan.md" << 'EOF'
|
|
# Ralph Fix Plan
|
|
|
|
## High Priority
|
|
- [ ] Set up user authentication with JWT
|
|
|
|
## Medium Priority
|
|
- [ ] Add team/workspace management
|
|
|
|
## Low Priority
|
|
- [ ] Real-time updates with WebSocket
|
|
|
|
## Completed
|
|
- [x] Project initialization
|
|
EOF
|
|
|
|
# Create specs/requirements.md in .ralph/specs/
|
|
cat > .ralph/specs/requirements.md << 'EOF'
|
|
# Technical Specifications
|
|
|
|
## System Architecture
|
|
- Frontend: React.js SPA with TypeScript
|
|
- Backend: Node.js REST API with Express
|
|
|
|
## Data Models
|
|
### User
|
|
- id: UUID
|
|
- email: string (unique)
|
|
EOF
|
|
|
|
# Output JSON response to stdout (mimicking --output-format json)
|
|
cat << 'JSON_OUTPUT'
|
|
{
|
|
"result": "Successfully converted PRD to Ralph format. Created .ralph/PROMPT.md, .ralph/fix_plan.md, and .ralph/specs/requirements.md",
|
|
"sessionId": "session-prd-convert-123",
|
|
"metadata": {
|
|
"files_changed": 3,
|
|
"has_errors": false,
|
|
"completion_status": "complete",
|
|
"files_created": [".ralph/PROMPT.md", ".ralph/fix_plan.md", ".ralph/specs/requirements.md"]
|
|
}
|
|
}
|
|
JSON_OUTPUT
|
|
|
|
exit 0
|
|
MOCK_CLAUDE_JSON_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
}
|
|
|
|
# Helper: Create mock claude command with JSON output but partial file creation
|
|
create_mock_claude_json_partial() {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_PARTIAL_EOF'
|
|
#!/bin/bash
|
|
# Mock Claude Code CLI that outputs JSON but only creates some files in .ralph/
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
cat > /dev/null
|
|
|
|
# Ensure .ralph directory exists
|
|
mkdir -p .ralph
|
|
|
|
# Only create PROMPT.md (missing fix_plan.md and specs/requirements.md)
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
|
|
## Context
|
|
You are Ralph, an autonomous AI development agent.
|
|
EOF
|
|
|
|
# Output JSON response indicating partial success
|
|
cat << 'JSON_OUTPUT'
|
|
{
|
|
"result": "Partial conversion completed. Some files could not be created.",
|
|
"sessionId": "session-prd-partial-456",
|
|
"metadata": {
|
|
"files_changed": 1,
|
|
"has_errors": true,
|
|
"completion_status": "partial",
|
|
"files_created": [".ralph/PROMPT.md"],
|
|
"missing_files": [".ralph/fix_plan.md", ".ralph/specs/requirements.md"]
|
|
}
|
|
}
|
|
JSON_OUTPUT
|
|
|
|
exit 0
|
|
MOCK_CLAUDE_PARTIAL_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
}
|
|
|
|
# Helper: Create mock claude command with JSON error output
|
|
create_mock_claude_json_error() {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_JSON_ERROR_EOF'
|
|
#!/bin/bash
|
|
# Mock Claude Code CLI that outputs JSON error response
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
cat > /dev/null
|
|
|
|
# Output JSON error response
|
|
cat << 'JSON_OUTPUT'
|
|
{
|
|
"result": "",
|
|
"sessionId": "session-error-789",
|
|
"metadata": {
|
|
"files_changed": 0,
|
|
"has_errors": true,
|
|
"completion_status": "failed",
|
|
"error_message": "Failed to parse PRD structure",
|
|
"error_code": "PARSE_ERROR"
|
|
}
|
|
}
|
|
JSON_OUTPUT
|
|
|
|
exit 1
|
|
MOCK_CLAUDE_JSON_ERROR_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
}
|
|
|
|
# Helper: Create mock claude that returns text (backward compatibility)
|
|
create_mock_claude_text_output() {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_TEXT_EOF'
|
|
#!/bin/bash
|
|
# Mock Claude Code CLI that outputs text (older CLI version) - files in .ralph/
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
cat > /dev/null
|
|
|
|
# Ensure .ralph directory exists
|
|
mkdir -p .ralph/specs
|
|
|
|
# Create files in .ralph/
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
|
|
## Context
|
|
You are Ralph, an autonomous AI development agent.
|
|
EOF
|
|
|
|
cat > ".ralph/fix_plan.md" << 'EOF'
|
|
# Ralph Fix Plan
|
|
|
|
## High Priority
|
|
- [ ] Set up project structure
|
|
|
|
## Completed
|
|
- [x] Project initialization
|
|
EOF
|
|
|
|
cat > .ralph/specs/requirements.md << 'EOF'
|
|
# Technical Specifications
|
|
|
|
## Overview
|
|
Basic technical requirements.
|
|
EOF
|
|
|
|
# Output plain text (no JSON)
|
|
echo "Mock: Claude Code conversion completed successfully"
|
|
echo "Created: .ralph/PROMPT.md, .ralph/fix_plan.md, .ralph/specs/requirements.md"
|
|
exit 0
|
|
MOCK_CLAUDE_TEXT_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
}
|
|
|
|
# Test 23: ralph-import parses JSON output format successfully
|
|
@test "ralph-import parses JSON output from Claude CLI" {
|
|
create_sample_prd_md "json-test.md"
|
|
create_mock_claude_json_success
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "json-test.md"
|
|
|
|
assert_success
|
|
|
|
# All files should be created in .ralph/ subfolder
|
|
assert_file_exists "json-test/.ralph/PROMPT.md"
|
|
assert_file_exists "json-test/.ralph/fix_plan.md"
|
|
assert_file_exists "json-test/.ralph/specs/requirements.md"
|
|
}
|
|
|
|
# Test 24: ralph-import handles JSON partial success response
|
|
@test "ralph-import handles JSON partial success and warns about missing files" {
|
|
create_sample_prd_md "partial-test.md"
|
|
create_mock_claude_json_partial
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "partial-test.md"
|
|
|
|
# Should succeed but with warnings
|
|
assert_success
|
|
|
|
# PROMPT.md should exist in .ralph/ subfolder
|
|
assert_file_exists "partial-test/.ralph/PROMPT.md"
|
|
|
|
# Warning should mention missing files
|
|
[[ "$output" == *"WARN"* ]] || [[ "$output" == *"not created"* ]] || [[ "$output" == *"missing"* ]]
|
|
}
|
|
|
|
# Test 25: ralph-import handles JSON error response gracefully
|
|
@test "ralph-import handles JSON error response with structured error message" {
|
|
create_sample_prd_md "error-test.md"
|
|
create_mock_claude_json_error
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "error-test.md"
|
|
|
|
# Should fail
|
|
assert_failure
|
|
|
|
# Error output should be present
|
|
[[ "$output" == *"failed"* ]] || [[ "$output" == *"ERROR"* ]] || [[ "$output" == *"error"* ]]
|
|
}
|
|
|
|
# Test 26: ralph-import maintains backward compatibility with text output
|
|
@test "ralph-import works with text output (backward compatibility)" {
|
|
create_sample_prd_md "text-test.md"
|
|
create_mock_claude_text_output
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "text-test.md"
|
|
|
|
assert_success
|
|
|
|
# All files should be created in .ralph/ subfolder
|
|
assert_file_exists "text-test/.ralph/PROMPT.md"
|
|
assert_file_exists "text-test/.ralph/fix_plan.md"
|
|
assert_file_exists "text-test/.ralph/specs/requirements.md"
|
|
}
|
|
|
|
# Test 27: ralph-import cleans up JSON output file after processing
|
|
@test "ralph-import cleans up temporary JSON output file" {
|
|
create_sample_prd_md "cleanup-test.md"
|
|
create_mock_claude_json_success
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "cleanup-test.md"
|
|
|
|
assert_success
|
|
|
|
# Temporary output file should NOT exist
|
|
[[ ! -f "cleanup-test/.ralph_conversion_output.json" ]]
|
|
|
|
# Temporary prompt file should NOT exist
|
|
[[ ! -f "cleanup-test/.ralph_conversion_prompt.md" ]]
|
|
}
|
|
|
|
# Test 28: ralph-import detects JSON vs text output format correctly
|
|
@test "ralph-import detects output format and uses appropriate parsing" {
|
|
create_sample_prd_md "format-test.md"
|
|
create_mock_claude_json_success
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "format-test.md"
|
|
|
|
assert_success
|
|
|
|
# Success message should indicate completion
|
|
[[ "$output" == *"SUCCESS"* ]] || [[ "$output" == *"successfully"* ]]
|
|
}
|
|
|
|
# Test 29: ralph-import extracts session ID from JSON response
|
|
@test "ralph-import extracts and stores session ID from JSON response" {
|
|
create_sample_prd_md "session-test.md"
|
|
create_mock_claude_json_success
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "session-test.md"
|
|
|
|
assert_success
|
|
|
|
# Check for session file (optional - only if session persistence is implemented)
|
|
# The session ID should be available for potential continuation
|
|
# This test verifies JSON parsing extracts the sessionId field
|
|
}
|
|
|
|
# Test 30: ralph-import reports file creation status from JSON metadata
|
|
@test "ralph-import reports files created based on JSON metadata" {
|
|
create_sample_prd_md "files-test.md"
|
|
create_mock_claude_json_success
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "files-test.md"
|
|
|
|
assert_success
|
|
|
|
# Should show success with next steps
|
|
[[ "$output" == *"Next steps"* ]] || [[ "$output" == *"PROMPT.md"* ]]
|
|
}
|
|
|
|
# Test 31: ralph-import uses modern CLI flags
|
|
@test "ralph-import invokes Claude CLI with modern flags" {
|
|
# Create a wrapper that captures the command invocation
|
|
cat > "$MOCK_BIN_DIR/claude" << 'CAPTURE_ARGS_EOF'
|
|
#!/bin/bash
|
|
# Capture invocation arguments for testing
|
|
echo "INVOCATION_ARGS: $*" >> /tmp/claude_invocation.log
|
|
|
|
# Ensure .ralph directory exists
|
|
mkdir -p .ralph/specs
|
|
|
|
# Create expected files in .ralph/
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
EOF
|
|
|
|
cat > ".ralph/fix_plan.md" << 'EOF'
|
|
# Ralph Fix Plan
|
|
## High Priority
|
|
- [ ] Task 1
|
|
EOF
|
|
|
|
cat > .ralph/specs/requirements.md << 'EOF'
|
|
# Technical Specifications
|
|
EOF
|
|
|
|
# Return JSON output
|
|
cat << 'JSON_OUTPUT'
|
|
{
|
|
"result": "Conversion complete",
|
|
"sessionId": "test-session",
|
|
"metadata": {
|
|
"files_changed": 3,
|
|
"has_errors": false,
|
|
"completion_status": "complete"
|
|
}
|
|
}
|
|
JSON_OUTPUT
|
|
|
|
exit 0
|
|
CAPTURE_ARGS_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
|
|
# Clear previous log
|
|
rm -f /tmp/claude_invocation.log
|
|
|
|
create_sample_prd_md "cli-flags-test.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "cli-flags-test.md"
|
|
|
|
assert_success
|
|
|
|
# Check if modern flags were used (if invocation log exists)
|
|
if [[ -f "/tmp/claude_invocation.log" ]]; then
|
|
# Verify --output-format or similar flag was passed
|
|
run cat /tmp/claude_invocation.log
|
|
# The specific flags depend on implementation
|
|
# This test ensures CLI modernization is in effect
|
|
fi
|
|
|
|
# Clean up
|
|
rm -f /tmp/claude_invocation.log
|
|
}
|
|
|
|
# Test 32: ralph-import handles malformed JSON gracefully
|
|
@test "ralph-import handles malformed JSON and falls back to text parsing" {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'MALFORMED_JSON_EOF'
|
|
#!/bin/bash
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
cat > /dev/null
|
|
|
|
# Ensure .ralph directory exists
|
|
mkdir -p .ralph/specs
|
|
|
|
# Create files in .ralph/
|
|
cat > .ralph/PROMPT.md << 'EOF'
|
|
# Ralph Development Instructions
|
|
EOF
|
|
|
|
cat > ".ralph/fix_plan.md" << 'EOF'
|
|
# Ralph Fix Plan
|
|
## High Priority
|
|
- [ ] Task 1
|
|
EOF
|
|
|
|
cat > .ralph/specs/requirements.md << 'EOF'
|
|
# Technical Specifications
|
|
EOF
|
|
|
|
# Output malformed JSON
|
|
echo '{"result": "Success but json is broken'
|
|
echo "Files created successfully"
|
|
exit 0
|
|
MALFORMED_JSON_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
|
|
create_sample_prd_md "malformed-test.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "malformed-test.md"
|
|
|
|
# Should still succeed (fallback to text parsing)
|
|
assert_success
|
|
|
|
# Files should exist in .ralph/ subfolder
|
|
assert_file_exists "malformed-test/.ralph/PROMPT.md"
|
|
}
|
|
|
|
# Test 33: ralph-import extracts error details from JSON error response
|
|
@test "ralph-import extracts specific error message from JSON error" {
|
|
cat > "$MOCK_BIN_DIR/claude" << 'DETAILED_ERROR_EOF'
|
|
#!/bin/bash
|
|
|
|
# Handle --version flag first
|
|
if [[ "$1" == "--version" ]]; then
|
|
echo "Claude Code CLI version 2.0.80"
|
|
exit 0
|
|
fi
|
|
|
|
cat > /dev/null
|
|
|
|
# Output detailed JSON error
|
|
cat << 'JSON_OUTPUT'
|
|
{
|
|
"result": "",
|
|
"sessionId": "error-session",
|
|
"metadata": {
|
|
"files_changed": 0,
|
|
"has_errors": true,
|
|
"completion_status": "failed",
|
|
"error_message": "Unable to parse PRD: Missing required sections",
|
|
"error_code": "PRD_PARSE_ERROR"
|
|
}
|
|
}
|
|
JSON_OUTPUT
|
|
|
|
exit 1
|
|
DETAILED_ERROR_EOF
|
|
chmod +x "$MOCK_BIN_DIR/claude"
|
|
|
|
create_sample_prd_md "detailed-error-test.md"
|
|
|
|
run bash "$PROJECT_ROOT/ralph_import.sh" "detailed-error-test.md"
|
|
|
|
# Should fail
|
|
assert_failure
|
|
|
|
# Error message should be shown
|
|
[[ "$output" == *"ERROR"* ]] || [[ "$output" == *"failed"* ]]
|
|
}
|