cueloop 0.6.0

A Rust CLI for managing AI agent loops with a structured JSON task queue
Documentation
//! Code review, iteration, and completion checklist tests.
//!
//! Purpose:
//! - Code review, iteration, and completion checklist tests.
//!
//! Responsibilities: validate code review prompt rendering and checklist loading.
//!
//! Scope:
//! - Limited to this file's owning feature boundary.
//!
//! Not handled: worker prompts, phase rendering, or variable expansion.
//!
//! Usage:
//! - Used through the crate module tree or integration test harness.
//!
//! Invariants/assumptions: embedded defaults contain expected markers; overrides take precedence.

use super::*;

#[test]
fn render_code_review_prompt_replaces_placeholders() -> Result<()> {
    let template = "ID={{TASK_ID}}\n";
    let config = default_config();
    let rendered = render_code_review_prompt(template, "RQ-0001", ProjectType::Code, &config)?;
    assert!(rendered.contains("ID=RQ-0001"));
    assert!(rendered.contains("PROJECT TYPE: CODE"));
    Ok(())
}

#[test]
fn render_code_review_prompt_allows_placeholder_like_text() -> Result<()> {
    let template = "ID={{TASK_ID}}\nSome text with {{TASK_ID}} in it\n";
    let config = default_config();
    let rendered = render_code_review_prompt(template, "RQ-0001", ProjectType::Code, &config)?;
    assert!(rendered.contains("ID=RQ-0001"));
    assert!(rendered.contains("Some text with RQ-0001 in it"));
    Ok(())
}

#[test]
fn render_code_review_prompt_fails_missing_task_id() -> Result<()> {
    let template = "{{TASK_ID}}\n";
    let config = default_config();
    let result = render_code_review_prompt(template, "", ProjectType::Code, &config);
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("task id"));
    Ok(())
}

#[test]
fn load_code_review_prompt_clarifies_disabled_ci_gate_is_not_review_disabled() -> Result<()> {
    let dir = TempDir::new()?;
    let prompt = load_code_review_prompt(dir.path())?;
    assert!(prompt.contains(
        "If you make ANY modifications during Phase 3 and `agent.ci_gate.enabled=false`"
    ));
    assert!(prompt.contains("skip only the configured CI command/requirement"));
    assert!(prompt.contains("continue review/completion work"));
    assert!(prompt.contains("configured CI validation was skipped by configuration"));
    assert!(!prompt.contains("before completion if enabled"));
    Ok(())
}

#[test]
fn load_completion_checklist_falls_back_to_embedded_default_when_missing() -> Result<()> {
    let dir = TempDir::new()?;
    let checklist = load_completion_checklist(dir.path())?;
    assert!(checklist.contains("IMPLEMENTATION COMPLETION CHECKLIST"));
    Ok(())
}

#[test]
fn default_completion_checklist_includes_followup_proposal_flow() -> Result<()> {
    let dir = TempDir::new()?;
    let checklist = load_completion_checklist(dir.path())?;
    assert!(checklist.contains("follow-ups cannot substitute"));
    assert!(checklist.contains(".cueloop/cache/followups/{{TASK_ID}}.json"));
    assert!(checklist.contains("cueloop task followups apply --task {{TASK_ID}}"));
    assert!(checklist.contains("do not apply the proposal"));
    assert!(checklist.contains("RUN_MODE=parallel-worker"));
    assert!(checklist.contains("CI Gate (configured validation only; never a run toggle)"));
    assert!(
        checklist
            .contains("`agent.ci_gate.enabled=false` skips CueLoop-managed CI validation only")
    );
    assert!(checklist.contains("It does NOT disable this run"));
    assert!(checklist.contains("task execution, queue bookkeeping, or git publish behavior"));
    assert!(
        checklist
            .contains("configured CI validation was skipped because `agent.ci_gate.enabled=false`")
    );
    assert!(!checklist.contains("if the CI gate is disabled"));
    assert!(!checklist.contains("CI-clean only when the CI gate is enabled"));
    Ok(())
}

#[test]
fn load_iteration_checklist_falls_back_to_embedded_default_when_missing() -> Result<()> {
    let dir = TempDir::new()?;
    let checklist = load_iteration_checklist(dir.path())?;
    assert!(checklist.contains("ITERATION CHECKLIST"));
    assert!(checklist.contains("agent.ci_gate.enabled=false"));
    assert!(checklist.contains("skip only the configured CI command/requirement"));
    assert!(checklist.contains("continue the iteration"));
    assert!(checklist.contains("configured CI validation was skipped by configuration"));
    Ok(())
}

#[test]
fn load_phase2_handoff_checklist_falls_back_to_embedded_default_when_missing() -> Result<()> {
    let dir = TempDir::new()?;
    let checklist = load_phase2_handoff_checklist(dir.path())?;
    assert!(checklist.contains("PHASE 2 HANDOFF CHECKLIST"));
    assert!(!checklist.contains("follow-ups Phase 3 must close"));
    assert!(!checklist.contains("stop after CI passes"));
    assert!(checklist.contains("configured Phase 2 validation"));
    assert!(checklist.contains("agent.ci_gate.enabled=false"));
    assert!(checklist.contains("only the configured CI command is skipped; Phase 2 implementation and handoff still continue"));
    assert!(checklist.contains("configured CI validation was skipped by configuration"));
    assert!(!checklist.contains("CI gate is disabled or no changes were made"));
    assert!(checklist.contains("If you are truly blocked"));
    Ok(())
}

#[test]
fn load_completion_checklist_uses_override_when_present() -> Result<()> {
    let dir = TempDir::new()?;
    let overrides = dir.path().join(".cueloop/prompts");
    fs::create_dir_all(&overrides)?;
    fs::write(overrides.join("completion_checklist.md"), "override")?;
    let checklist = load_completion_checklist(dir.path())?;
    assert_eq!(checklist, "override");
    Ok(())
}

#[test]
fn load_iteration_checklist_uses_override_when_present() -> Result<()> {
    let dir = TempDir::new()?;
    let overrides = dir.path().join(".cueloop/prompts");
    fs::create_dir_all(&overrides)?;
    fs::write(overrides.join("iteration_checklist.md"), "override")?;
    let checklist = load_iteration_checklist(dir.path())?;
    assert_eq!(checklist, "override");
    Ok(())
}

#[test]
fn load_phase2_handoff_checklist_uses_override_when_present() -> Result<()> {
    let dir = TempDir::new()?;
    let overrides = dir.path().join(".cueloop/prompts");
    fs::create_dir_all(&overrides)?;
    fs::write(overrides.join("phase2_handoff_checklist.md"), "override")?;
    let checklist = load_phase2_handoff_checklist(dir.path())?;
    assert_eq!(checklist, "override");
    Ok(())
}

#[test]
fn render_completion_checklist_sets_normal_run_mode() -> Result<()> {
    let template = "task={{TASK_ID}} mode={{RUN_MODE}}";
    let config = default_config();
    let rendered = render_completion_checklist(template, "RQ-0001", &config, false)?;
    assert_eq!(rendered, "task=RQ-0001 mode=normal");
    Ok(())
}

#[test]
fn render_completion_checklist_sets_parallel_worker_run_mode() -> Result<()> {
    let template = "task={{TASK_ID}} mode={{RUN_MODE}}";
    let config = default_config();
    let rendered = render_completion_checklist(template, "RQ-0001", &config, true)?;
    assert_eq!(rendered, "task=RQ-0001 mode=parallel-worker");
    Ok(())
}