magi-code 0.61.0

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

#[test]
fn print_mode_joins_already_finished_title_generation_before_returning() {
    let completed = Arc::new(AtomicBool::new(false));
    let completed_for_thread = Arc::clone(&completed);
    let mut handle = Some(std::thread::spawn(move || {
        completed_for_thread.store(true, Ordering::SeqCst);
    }));
    while !handle.as_ref().unwrap().is_finished() {
        std::thread::yield_now();
    }

    finish_title_generation_for_mode(&mut handle, InvocationMode::Print);

    assert!(completed.load(Ordering::SeqCst));
    assert!(handle.is_none());
}

#[test]
fn print_mode_detaches_active_title_generation_non_blocking() {
    let completed = Arc::new(AtomicBool::new(false));
    let completed_for_thread = Arc::clone(&completed);
    let (sender, receiver) = std::sync::mpsc::channel();
    let mut handle = Some(std::thread::spawn(move || {
        receiver.recv().unwrap();
        completed_for_thread.store(true, Ordering::SeqCst);
    }));

    let started = std::time::Instant::now();
    finish_title_generation_for_mode(&mut handle, InvocationMode::Print);

    assert!(started.elapsed() < std::time::Duration::from_millis(200));
    assert!(!completed.load(Ordering::SeqCst));
    assert!(handle.is_none());
    sender.send(()).unwrap();
    while !completed.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }
}

#[test]
fn print_mode_title_guard_finish_detaches_blocked_thread_non_blocking() {
    let completed = Arc::new(AtomicBool::new(false));
    let completed_for_thread = Arc::clone(&completed);
    let (sender, receiver) = std::sync::mpsc::channel();
    let handle = Some(std::thread::spawn(move || {
        receiver.recv().unwrap();
        completed_for_thread.store(true, Ordering::SeqCst);
    }));
    let mut guard =
        TitleGenerationGuard::new(handle, InvocationMode::Print, AgentCancellation::default());

    let started = std::time::Instant::now();
    guard.finish();

    assert!(started.elapsed() < std::time::Duration::from_millis(200));
    assert!(!completed.load(Ordering::SeqCst));
    sender.send(()).unwrap();
    while !completed.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }
}

#[test]
fn print_mode_title_guard_drop_detaches_blocked_thread_non_blocking() {
    let completed = Arc::new(AtomicBool::new(false));
    let completed_for_thread = Arc::clone(&completed);
    let (sender, receiver) = std::sync::mpsc::channel();
    let handle = Some(std::thread::spawn(move || {
        receiver.recv().unwrap();
        completed_for_thread.store(true, Ordering::SeqCst);
    }));

    let started = std::time::Instant::now();
    {
        let _guard =
            TitleGenerationGuard::new(handle, InvocationMode::Print, AgentCancellation::default());
    }

    assert!(started.elapsed() < std::time::Duration::from_millis(200));
    assert!(!completed.load(Ordering::SeqCst));
    sender.send(()).unwrap();
    while !completed.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }
}

#[test]
fn print_mode_title_guard_skips_join_when_canceled() {
    let completed = Arc::new(AtomicBool::new(false));
    let completed_for_thread = Arc::clone(&completed);
    let cancel = Arc::new(AtomicBool::new(true));
    let (sender, receiver) = std::sync::mpsc::channel();
    let handle = Some(std::thread::spawn(move || {
        receiver.recv().unwrap();
        completed_for_thread.store(true, Ordering::SeqCst);
    }));

    {
        let _guard = TitleGenerationGuard::new(
            handle,
            InvocationMode::Print,
            AgentCancellation::new(cancel),
        );
    }

    assert!(!completed.load(Ordering::SeqCst));
    sender.send(()).unwrap();
    while !completed.load(Ordering::SeqCst) {
        std::thread::yield_now();
    }
}

#[test]
fn non_print_modes_leave_active_title_generation_non_blocking() {
    for mode in [
        InvocationMode::Shell,
        InvocationMode::MissionControl,
        InvocationMode::Subagent,
    ] {
        let completed = Arc::new(AtomicBool::new(false));
        let completed_for_thread = Arc::clone(&completed);
        let (sender, receiver) = std::sync::mpsc::channel();
        let mut handle = Some(std::thread::spawn(move || {
            receiver.recv().unwrap();
            completed_for_thread.store(true, Ordering::SeqCst);
        }));

        let started = std::time::Instant::now();
        finish_title_generation_for_mode(&mut handle, mode);

        assert!(started.elapsed() < std::time::Duration::from_millis(200));
        assert!(!completed.load(Ordering::SeqCst));
        let handle = handle.take().unwrap();
        sender.send(()).unwrap();
        handle.join().unwrap();
    }
}

#[test]
fn non_print_modes_join_already_finished_title_generation() {
    for mode in [
        InvocationMode::Shell,
        InvocationMode::MissionControl,
        InvocationMode::Subagent,
    ] {
        let mut handle = Some(std::thread::spawn(|| {}));
        while !handle.as_ref().unwrap().is_finished() {
            std::thread::yield_now();
        }

        finish_title_generation_for_mode(&mut handle, mode);

        assert!(handle.is_none());
    }
}

#[test]
fn herdr_primary_lifecycle_reports_thinking_tool_done_in_order() {
    let temp = tempfile::TempDir::new().unwrap();
    std::fs::write(temp.path().join("file.txt"), "file text").unwrap();
    let provider = ScriptedProvider::new(vec![read_done("herdr_read"), text_done("done")]);
    let tools = ToolRuntime::new(temp.path()).unwrap();
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());
    let (reporter, lines) = crate::herdr::HerdrReporter::new_for_test();

    agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            AgentRunRequest {
                tools: Some(&tools),
                herdr_reporter: Some(reporter),
                ..run_request("read file", temp.path())
            },
        )
        .unwrap();

    assert_eq!(
        herdr_statuses(&lines),
        vec![
            (
                "working".to_string(),
                "thinking".to_string(),
                "magi-code: thinking".to_string(),
            ),
            (
                "working".to_string(),
                "tool".to_string(),
                "magi-code: running read".to_string(),
            ),
            (
                "idle".to_string(),
                "done".to_string(),
                "magi-code: done".to_string(),
            ),
        ]
    );
}

#[test]
fn herdr_subagent_invocation_reports_nothing_even_with_parent_reporter() {
    let temp = tempfile::TempDir::new().unwrap();
    let provider = ScriptedProvider::new(vec![text_done("child done")]);
    let tools = ToolRuntime::new(temp.path()).unwrap();
    let agent = AgentSession::new("model", &[], &SkillDiscovery::default());
    let (reporter, lines) = crate::herdr::HerdrReporter::new_for_test();

    agent
        .run_print_with_tools_streaming_output_cancellable(
            &provider,
            AgentRunRequest {
                tools: Some(&tools),
                invocation_mode: InvocationMode::Subagent,
                herdr_reporter: Some(reporter),
                ..run_request("child task", temp.path())
            },
        )
        .unwrap();

    assert!(lines.lock().unwrap().is_empty());
}