magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::support::*;
use super::*;
use crate::tui::sessions::commands::execute_switch_session_command;

#[test]
fn sessions_command_opens_modal_without_provider_submission() {
    let temp = tempfile::TempDir::new().unwrap();
    let manager = SessionManager::new(temp.path().join("sessions"));
    let session = manager.open("saved-session").unwrap();
    session
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            session.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({}),
        ))
        .unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(4);
    let mut app = test_app(&temp, sender);
    let mut ui_state = state::MissionControlState::default();

    app.submit(
        "/sessions".to_string(),
        &mut ui_state,
        &receiver,
        test_area(),
    );

    assert!(ui_state.session_picker_visible());
    assert!(matches!(
        receiver.recv_timeout(Duration::from_secs(2)).unwrap(),
        TuiEvent::WorkerOutcomeReady
    ));
    reap_owned_worker(
        &mut app,
        &mut ui_state,
        MissionControlApp::reap_session_maintenance,
    );
    assert_eq!(
        ui_state.modals.session_picker.as_ref().unwrap().rows.len(),
        1
    );
    assert!(app.worker.is_none());
    assert!(!app.active_run);
    assert!(matches!(
        receiver.recv_timeout(Duration::from_secs(2)).unwrap(),
        TuiEvent::SessionPreviewLoaded { session_id, .. } if session_id == "saved-session"
    ));
}

#[test]
fn sessions_command_rejects_arguments_without_provider_submission() {
    let temp = tempfile::TempDir::new().unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(4);
    let mut app = test_app(&temp, sender);
    let mut ui_state = state::MissionControlState::default();
    app.submit(
        "/sessions extra".to_string(),
        &mut ui_state,
        &receiver,
        test_area(),
    );
    assert_eq!(ui_state.status, "usage: /sessions");
    assert!(!ui_state.session_picker_visible());
    assert!(app.worker.is_none());
    assert!(!app.active_run);
    assert!(receiver.try_recv().is_err());
}

#[test]
fn sessions_command_rejects_while_run_active() {
    let temp = tempfile::TempDir::new().unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(4);
    let mut app = test_app(&temp, sender);
    app.active_run = true;
    let mut ui_state = state::MissionControlState::default();
    app.submit(
        "/sessions".to_string(),
        &mut ui_state,
        &receiver,
        test_area(),
    );
    assert_eq!(
        ui_state.status,
        "cannot switch sessions while a prompt is running"
    );
    assert!(receiver.try_recv().is_err());
}

#[test]
fn cancelling_session_picker_preserves_active_session() {
    let temp = tempfile::TempDir::new().unwrap();
    let manager = SessionManager::new(temp.path().join("sessions"));
    let active = manager.open("active-session").unwrap();
    let target = manager.open("target-session").unwrap();
    active
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            active.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({}),
        ))
        .unwrap();
    target
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            target.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({}),
        ))
        .unwrap();
    let (sender, _receiver) = bounded::<TuiEvent>(4);
    let app = test_app_with_active_session(&temp, sender, Some(active.clone()));
    let mut ui_state = state::MissionControlState::default();
    ui_state.open_session_picker(
        vec![state::SessionPickerRow {
            id: target.id().to_string(),
            label: target.id().to_string(),
            is_current: false,
            preview: None,
        }],
        1,
    );

    let action = input::handle_key(
        crossterm::event::KeyEvent::new(
            crossterm::event::KeyCode::Esc,
            crossterm::event::KeyModifiers::NONE,
        ),
        &mut ui_state,
    );

    assert_eq!(action, input::InputAction::None);
    assert!(!ui_state.session_picker_visible());
    assert_eq!(app.state.active_session_id(), Some(active.id()));
}
#[test]
fn rewind_result_is_ignored_after_active_session_changes() {
    let temp = tempfile::TempDir::new().unwrap();
    let session = SessionManager::new(temp.path().join("sessions"))
        .create()
        .unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(4);
    let mut app = test_app_with_active_session(&temp, sender, Some(session));
    let mut ui_state = state::MissionControlState::default();

    app.submit(
        "/changes".to_string(),
        &mut ui_state,
        &receiver,
        test_area(),
    );
    app.state.current_session = None;
    reap_rewind_worker(&mut app, &mut ui_state, &receiver);

    assert_eq!(ui_state.status, "rewind result ignored; session changed");
    assert!(!ui_state.rewind_modal_visible());
    assert!(receiver.try_recv().is_err());
}

#[test]
fn export_command_writes_archive_for_persisted_session() {
    let temp = tempfile::TempDir::new().unwrap();
    let manager = SessionManager::new(temp.path().join("sessions"));
    let session = manager.create().unwrap();
    session
        .append(&crate::sessions::SessionEvent::new(
            "user_input",
            session.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({"text": "hello"}),
        ))
        .unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(8);
    let mut app = test_app_with_active_session(&temp, sender, Some(session.clone()));
    let mut ui_state = state::MissionControlState::default();

    app.submit("/export".to_string(), &mut ui_state, &receiver, test_area());
    assert_eq!(ui_state.status, "exporting session…");
    reap_export_worker(&mut app, &mut ui_state, &receiver);

    assert!(
        ui_state
            .status
            .starts_with("UNENCRYPTED — NOT SAFE TO SHARE; session export complete: ")
    );
    let exports = std::fs::read_dir(app.config.paths.root.join("exports"))
        .unwrap()
        .collect::<Result<Vec<_>, _>>()
        .unwrap();
    assert_eq!(exports.len(), 1);
    assert!(exports[0].path().is_file());
}

#[test]
fn export_command_rejects_arguments_and_duplicate_requests() {
    let temp = tempfile::TempDir::new().unwrap();
    let manager = SessionManager::new(temp.path().join("sessions"));
    let session = manager.create().unwrap();
    session
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            session.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({}),
        ))
        .unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(8);
    let mut app = test_app_with_active_session(&temp, sender, Some(session));
    let mut ui_state = state::MissionControlState::default();

    app.submit(
        "/export extra".to_string(),
        &mut ui_state,
        &receiver,
        test_area(),
    );
    assert_eq!(ui_state.status, "usage: /export");
    assert!(receiver.try_recv().is_err());

    app.submit("/export".to_string(), &mut ui_state, &receiver, test_area());
    app.submit("/export".to_string(), &mut ui_state, &receiver, test_area());
    assert_eq!(ui_state.status, "export request already in progress");
    reap_export_worker(&mut app, &mut ui_state, &receiver);
}

#[test]
fn export_result_is_ignored_after_active_session_changes() {
    let temp = tempfile::TempDir::new().unwrap();
    let manager = SessionManager::new(temp.path().join("sessions"));
    let session = manager.create().unwrap();
    session
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            session.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({}),
        ))
        .unwrap();
    let (sender, receiver) = bounded::<TuiEvent>(8);
    let mut app = test_app_with_active_session(&temp, sender, Some(session));
    let mut ui_state = state::MissionControlState::default();

    app.submit("/export".to_string(), &mut ui_state, &receiver, test_area());
    app.state.current_session = None;
    reap_export_worker(&mut app, &mut ui_state, &receiver);

    assert_eq!(ui_state.status, "export result ignored; session changed");
}
#[test]
fn selected_session_is_used_for_next_provider_run() {
    let temp = tempfile::TempDir::new().unwrap();
    let manager = SessionManager::new(temp.path().join("sessions"));
    let active = manager.open("active-session").unwrap();
    let target = manager.open("target-session").unwrap();
    active
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            active.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({"seed":"active"}),
        ))
        .unwrap();
    target
        .append(&crate::sessions::SessionEvent::new(
            "diagnostic",
            target.id().to_string(),
            temp.path().to_path_buf(),
            serde_json::json!({"seed":"target"}),
        ))
        .unwrap();
    let active_before = active.read_events().unwrap().len();
    let target_before = target.read_events().unwrap().len();
    let (sender, receiver) = bounded::<TuiEvent>(8);
    let mut app = test_app_with_active_session(&temp, sender.clone(), Some(active.clone()));
    let mut ui_state = state::MissionControlState::default();

    execute_switch_session_command(
        &mut app.state,
        &mut ui_state,
        target.id(),
        &receiver,
        &mut std::collections::VecDeque::new(),
    )
    .unwrap();
    assert!(app.submit(
        "next prompt".to_string(),
        &mut ui_state,
        &receiver,
        test_area(),
    ));
    assert!(app.worker.is_some());
    assert!(app.active_run);
    while app
        .worker
        .as_ref()
        .is_some_and(|worker| !worker.handle.is_finished())
    {
        std::thread::yield_now();
    }
    assert!(drain_tui_events(&receiver, &mut ui_state).run_finished);
    assert!(app.finish_worker_if_ready(&mut ui_state));

    assert_eq!(app.state.active_session_id(), Some(target.id()));
    assert_eq!(active.read_events().unwrap().len(), active_before);
    let target_events = target.read_events().unwrap();
    assert_eq!(target_events.len(), target_before + 1);
    assert_eq!(target_events.last().unwrap().session_id, target.id());
    assert_eq!(target_events.last().unwrap().event_type, "diagnostic");
}