{
    "sourceFile": "cli/tests/lifecycle_e2e_test.rs",
    "activeCommit": 0,
    "commits": [
        {
            "activePatchIndex": 0,
            "patches": [
                {
                    "date": 1760245450066,
                    "content": "Index: \n===================================================================\n--- \n+++ \n"
                }
            ],
            "date": 1760245450066,
            "name": "Commit-0",
            "content": "//! End-to-end integration tests for lifecycle commands\n//!\n//! Tests the complete user journey through the lifecycle management system:\n//! - Listing phases from make.toml\n//! - Showing phase details\n//! - Running individual phases\n//! - Executing phase pipelines\n//! - State persistence and tracking\n//! - Error handling for edge cases\n\nuse assert_cmd::Command;\nuse predicates::prelude::*;\nuse std::fs;\nuse std::path::PathBuf;\nuse tempfile::TempDir;\n\n/// Helper to create a test project with make.toml\nfn create_test_project() -> TempDir {\n    let temp_dir = TempDir::new().expect(\"Failed to create temp dir\");\n\n    let make_toml = r#\"\n[project]\nname = \"test-project\"\ntype = \"webapp\"\nversion = \"1.0.0\"\ndescription = \"Test project for lifecycle e2e tests\"\n\n[lifecycle.validate]\ndescription = \"Validate environment\"\ncommands = [\"echo '🔍 Validating environment...'\"]\n\n[lifecycle.lint]\ndescription = \"Lint code\"\ncommands = [\"echo '🔍 Linting code...'\"]\n\n[lifecycle.init]\ndescription = \"Initialize project structure\"\ncommands = [\n    \"mkdir -p src tests docs\",\n    \"echo 'Project initialized' > .initialized\"\n]\n\n[lifecycle.setup]\ndescription = \"Install dependencies and configure environment\"\ncommands = [\n    \"echo 'Installing dependencies...'\",\n    \"echo 'Setup complete' > .setup\"\n]\nwatch = true\n\n[lifecycle.dev]\ndescription = \"Start development server\"\ncommands = [\"echo 'Dev server running on port 3000'\"]\nport = 3000\nwatch = true\n\n[lifecycle.build]\ndescription = \"Build production artifacts\"\ncommands = [\n    \"echo 'Building project...'\",\n    \"mkdir -p dist\",\n    \"echo 'Build complete' > dist/output.txt\"\n]\ncache = true\n\n[lifecycle.test]\ndescription = \"Run test suite\"\ncommands = [\n    \"echo 'Running tests...'\",\n    \"echo 'All tests passed' > .test-results\"\n]\n\n[lifecycle.deploy]\ndescription = \"Deploy to production\"\ncommands = [\"echo 'Deploying to production...'\"]\n\n[lifecycle.cleanup]\ndescription = \"Clean up temporary files\"\ncommands = [\"echo '🧹 Cleaning up...'\"]\n\n[hooks]\nbefore_build = [\"lint\"]\nafter_build = [\"test\"]\n\"#;\n\n    fs::write(temp_dir.path().join(\"make.toml\"), make_toml).expect(\"Failed to write make.toml\");\n\n    temp_dir\n}\n\n/// Helper to get the ggen binary path\nfn ggen_cmd() -> Command {\n    Command::cargo_bin(\"ggen\").expect(\"Failed to find ggen binary\")\n}\n\n/// Helper to assert state.json exists and is valid JSON\nfn assert_state_exists(root: &std::path::Path) {\n    let state_path = root.join(\".ggen/state.json\");\n    assert!(\n        state_path.exists(),\n        \"State file should exist at {}\",\n        state_path.display()\n    );\n\n    let state_content = fs::read_to_string(&state_path).expect(\"Failed to read state.json\");\n    serde_json::from_str::<serde_json::Value>(&state_content)\n        .expect(\"State file should be valid JSON\");\n}\n\n#[test]\nfn test_lifecycle_list_shows_all_phases() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"list\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Available lifecycle phases\"))\n        .stdout(predicate::str::contains(\"init\"))\n        .stdout(predicate::str::contains(\"setup\"))\n        .stdout(predicate::str::contains(\"dev\"))\n        .stdout(predicate::str::contains(\"build\"))\n        .stdout(predicate::str::contains(\"test\"))\n        .stdout(predicate::str::contains(\"deploy\"))\n        .stdout(predicate::str::contains(\"Initialize project structure\"));\n}\n\n#[test]\nfn test_lifecycle_list_without_make_toml() {\n    let temp_dir = TempDir::new().unwrap();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"list\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"No make.toml found\"));\n}\n\n#[test]\nfn test_lifecycle_show_displays_phase_details() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"show\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Phase: init\"))\n        .stdout(predicate::str::contains(\"Initialize project structure\"))\n        .stdout(predicate::str::contains(\"Commands:\"))\n        .stdout(predicate::str::contains(\"mkdir -p src tests docs\"));\n}\n\n#[test]\nfn test_lifecycle_show_with_metadata() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"show\")\n        .arg(\"dev\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Watch mode: true\"))\n        .stdout(predicate::str::contains(\"Port: 3000\"));\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"show\")\n        .arg(\"build\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Caching: true\"))\n        .stdout(predicate::str::contains(\"Before hooks:\"))\n        .stdout(predicate::str::contains(\"lint\"))\n        .stdout(predicate::str::contains(\"After hooks:\"))\n        .stdout(predicate::str::contains(\"test\"));\n}\n\n#[test]\nfn test_lifecycle_show_missing_phase() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"show\")\n        .arg(\"nonexistent\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .failure()\n        .stderr(predicate::str::contains(\"Phase 'nonexistent' not found\"));\n}\n\n#[test]\nfn test_lifecycle_run_executes_phase() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Running phase: init\"));\n\n    // Verify files were created\n    assert!(temp_dir.path().join(\"src\").exists());\n    assert!(temp_dir.path().join(\"tests\").exists());\n    assert!(temp_dir.path().join(\"docs\").exists());\n    assert!(temp_dir.path().join(\".initialized\").exists());\n}\n\n#[test]\nfn test_lifecycle_run_creates_state_file() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    assert_state_exists(temp_dir.path());\n\n    // Verify state content\n    let state_path = temp_dir.path().join(\".ggen/state.json\");\n    let state_content = fs::read_to_string(&state_path).unwrap();\n    let state: serde_json::Value = serde_json::from_str(&state_content).unwrap();\n\n    assert_eq!(state[\"last_phase\"], \"init\");\n    assert!(state[\"phase_history\"]\n        .as_array()\n        .unwrap()\n        .iter()\n        .any(|record| record[\"phase\"] == \"init\"));\n}\n\n#[test]\nfn test_lifecycle_run_with_environment() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"setup\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .arg(\"--env\")\n        .arg(\"production\")\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Running phase: setup\"));\n\n    // State should track the execution\n    assert_state_exists(temp_dir.path());\n}\n\n#[test]\nfn test_lifecycle_run_missing_phase() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"invalid-phase\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .failure()\n        .stderr(predicate::str::contains(\"Phase 'invalid-phase' not found\"));\n}\n\n#[test]\nfn test_lifecycle_pipeline_sequential_execution() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"pipeline\")\n        .arg(\"init\")\n        .arg(\"setup\")\n        .arg(\"build\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Running phase: init\"))\n        .stdout(predicate::str::contains(\"Running phase: setup\"))\n        .stdout(predicate::str::contains(\"Running phase: build\"))\n        .stdout(predicate::str::contains(\n            \"Pipeline completed: init → setup → build\",\n        ));\n\n    // Verify all phases executed\n    assert!(temp_dir.path().join(\".initialized\").exists());\n    assert!(temp_dir.path().join(\".setup\").exists());\n    assert!(temp_dir.path().join(\"dist/output.txt\").exists());\n\n    // Verify state tracks all phases\n    let state_path = temp_dir.path().join(\".ggen/state.json\");\n    let state_content = fs::read_to_string(&state_path).unwrap();\n    let state: serde_json::Value = serde_json::from_str(&state_content).unwrap();\n\n    assert_eq!(state[\"last_phase\"], \"test\");\n    let phase_history = state[\"phase_history\"].as_array().unwrap();\n    assert!(phase_history.iter().any(|record| record[\"phase\"] == \"init\"));\n    assert!(phase_history\n        .iter()\n        .any(|record| record[\"phase\"] == \"setup\"));\n    assert!(phase_history\n        .iter()\n        .any(|record| record[\"phase\"] == \"build\"));\n}\n\n#[test]\nfn test_lifecycle_pipeline_with_environment() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"pipeline\")\n        .arg(\"init\")\n        .arg(\"setup\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .arg(\"--env\")\n        .arg(\"staging\")\n        .assert()\n        .success();\n\n    assert_state_exists(temp_dir.path());\n}\n\n#[test]\nfn test_lifecycle_pipeline_stops_on_missing_phase() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"pipeline\")\n        .arg(\"init\")\n        .arg(\"nonexistent\")\n        .arg(\"setup\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .failure()\n        .stderr(predicate::str::contains(\"Phase 'nonexistent' not found\"));\n}\n\n#[test]\nfn test_state_persistence_across_runs() {\n    let temp_dir = create_test_project();\n\n    // First run\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    // Second run - state should persist\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"setup\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    // Verify state includes both phases\n    let state_path = temp_dir.path().join(\".ggen/state.json\");\n    let state_content = fs::read_to_string(&state_path).unwrap();\n    let state: serde_json::Value = serde_json::from_str(&state_content).unwrap();\n\n    assert_eq!(state[\"last_phase\"], \"setup\");\n    let phase_history = state[\"phase_history\"].as_array().unwrap();\n    assert_eq!(phase_history.len(), 2);\n}\n\n#[test]\nfn test_lifecycle_list_shows_last_executed_phase() {\n    let temp_dir = create_test_project();\n\n    // Run a phase first\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"build\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    // List should show last executed\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"list\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Last executed: test\"));\n}\n\n#[test]\nfn test_lifecycle_help_output() {\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"--help\")\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Universal lifecycle management\"))\n        .stdout(predicate::str::contains(\"list\"))\n        .stdout(predicate::str::contains(\"show\"))\n        .stdout(predicate::str::contains(\"run\"))\n        .stdout(predicate::str::contains(\"pipeline\"));\n}\n\n#[test]\nfn test_lifecycle_list_help() {\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"list\")\n        .arg(\"--help\")\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\n            \"List all available lifecycle phases\",\n        ))\n        .stdout(predicate::str::contains(\"--root\"));\n}\n\n#[test]\nfn test_lifecycle_show_help() {\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"show\")\n        .arg(\"--help\")\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Show details of a specific phase\"))\n        .stdout(predicate::str::contains(\"<PHASE>\"))\n        .stdout(predicate::str::contains(\"--root\"));\n}\n\n#[test]\nfn test_lifecycle_run_help() {\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"--help\")\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Run a single lifecycle phase\"))\n        .stdout(predicate::str::contains(\"<PHASE>\"))\n        .stdout(predicate::str::contains(\"--root\"))\n        .stdout(predicate::str::contains(\"--env\"));\n}\n\n#[test]\nfn test_lifecycle_pipeline_help() {\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"pipeline\")\n        .arg(\"--help\")\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Run multiple phases in sequence\"))\n        .stdout(predicate::str::contains(\"[PHASES]...\"))\n        .stdout(predicate::str::contains(\"--root\"))\n        .stdout(predicate::str::contains(\"--env\"));\n}\n\n#[test]\nfn test_hooks_execution_order() {\n    let temp_dir = create_test_project();\n\n    let output = ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"build\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .output()\n        .expect(\"Failed to execute command\");\n\n    let stdout = String::from_utf8_lossy(&output.stdout);\n\n    // Verify hooks run in correct order: lint (before) -> build -> test (after)\n    let lint_pos = stdout.find(\"Running phase: lint\").unwrap();\n    let build_pos = stdout.find(\"Running phase: build\").unwrap();\n    let test_pos = stdout.find(\"Running phase: test\").unwrap();\n\n    assert!(lint_pos < build_pos, \"Lint should run before build\");\n    assert!(build_pos < test_pos, \"Test should run after build\");\n}\n\n#[test]\nfn test_multiple_commands_in_phase() {\n    let temp_dir = create_test_project();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"build\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"Building project...\"));\n\n    // Verify both commands executed\n    assert!(temp_dir.path().join(\"dist\").exists());\n    assert!(temp_dir.path().join(\"dist/output.txt\").exists());\n}\n\n#[test]\nfn test_empty_phase_list() {\n    let temp_dir = TempDir::new().unwrap();\n\n    // Create make.toml with no phases\n    let make_toml = r#\"\n[project]\nname = \"test-project\"\n\"#;\n    fs::write(temp_dir.path().join(\"make.toml\"), make_toml).unwrap();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"list\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success()\n        .stdout(predicate::str::contains(\"(no phases defined)\"));\n}\n\n#[test]\nfn test_state_directory_creation() {\n    let temp_dir = create_test_project();\n\n    // State directory shouldn't exist initially\n    assert!(!temp_dir.path().join(\".ggen\").exists());\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    // State directory and file should be created\n    assert!(temp_dir.path().join(\".ggen\").exists());\n    assert!(temp_dir.path().join(\".ggen\").is_dir());\n    assert_state_exists(temp_dir.path());\n}\n\n#[test]\nfn test_concurrent_safe_state_updates() {\n    let temp_dir = create_test_project();\n\n    // Run multiple phases quickly\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"setup\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"test\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    // State should be valid and contain all phases\n    let state_path = temp_dir.path().join(\".ggen/state.json\");\n    let state_content = fs::read_to_string(&state_path).unwrap();\n    let state: serde_json::Value = serde_json::from_str(&state_content).unwrap();\n\n    let phase_history = state[\"phase_history\"].as_array().unwrap();\n    assert_eq!(phase_history.len(), 3);\n}\n\n#[test]\nfn test_phase_without_hooks() {\n    let temp_dir = create_test_project();\n\n    // Init phase has no hooks defined\n    let output = ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"run\")\n        .arg(\"init\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .output()\n        .expect(\"Failed to execute command\");\n\n    let stdout = String::from_utf8_lossy(&output.stdout);\n\n    // Should run directly without hooks\n    assert!(stdout.contains(\"Running phase: init\"));\n    // Should not run other phases as hooks\n    assert!(!stdout.contains(\"Running phase: lint\"));\n    assert!(!stdout.contains(\"Running phase: test\"));\n}\n\n#[test]\nfn test_performance_fast_execution() {\n    let temp_dir = create_test_project();\n\n    let start = std::time::Instant::now();\n\n    ggen_cmd()\n        .arg(\"lifecycle\")\n        .arg(\"pipeline\")\n        .arg(\"init\")\n        .arg(\"setup\")\n        .arg(\"build\")\n        .arg(\"test\")\n        .arg(\"--root\")\n        .arg(temp_dir.path())\n        .assert()\n        .success();\n\n    let duration = start.elapsed();\n\n    // All tests should complete in under 5 seconds\n    assert!(\n        duration.as_secs() < 5,\n        \"Pipeline took too long: {:?}\",\n        duration\n    );\n}\n"
        }
    ]
}