magi-code 0.77.1

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

#[test]
fn logout_picker_escape_cancels_without_provider_selection() {
    let mut state = MissionControlState::default();
    state.open_logout_picker(
        vec![crate::tui::state::LoginProviderEntry {
            id: "openai-codex".to_string(),
            label: "OpenAI Codex".to_string(),
            description: "codex".to_string(),
            status: "configured".to_string(),
        }],
        None,
        DEFAULT_PICKER_VISIBLE_ROWS as usize,
    );

    let action = handle_key(key(KeyCode::Esc), &mut state);

    assert!(matches!(action, InputAction::None));
    assert!(state.modals.logout_picker.is_none());
}

#[test]
fn logout_picker_up_down_navigation_scrolls_and_selects_logout_provider() {
    let mut state = MissionControlState::default();
    let entries = (0..12)
        .map(|index| crate::tui::state::LoginProviderEntry {
            id: format!("provider-{index}"),
            label: format!("Provider {index}"),
            description: "test provider".to_string(),
            status: "configured".to_string(),
        })
        .collect::<Vec<_>>();
    state.open_logout_picker(entries, None, DEFAULT_PICKER_VISIBLE_ROWS as usize);

    for _ in 0..11 {
        assert!(matches!(
            handle_key(key(KeyCode::Down), &mut state),
            InputAction::None
        ));
    }
    let picker = state.modals.logout_picker.as_ref().unwrap();
    assert_eq!(picker.selected, 11);
    assert!(matches!(
        handle_key(key(KeyCode::Up), &mut state),
        InputAction::None
    ));
    let picker = state.modals.logout_picker.as_ref().unwrap();
    assert_eq!(picker.selected, 10);

    let action = handle_key(key(KeyCode::Enter), &mut state);

    assert!(
        matches!(action, InputAction::SelectLogoutProvider(provider) if provider == "provider-10")
    );
    assert!(state.modals.logout_picker.is_none());
}

#[test]
fn logout_picker_uses_actual_viewport_to_keep_selection_visible() {
    let mut state = MissionControlState::default();
    let entries = (0..6)
        .map(|index| crate::tui::state::LoginProviderEntry {
            id: format!("provider-{index}"),
            label: format!("Provider {index}"),
            description: "test provider".to_string(),
            status: "configured".to_string(),
        })
        .collect::<Vec<_>>();
    state.open_logout_picker(entries, None, 2);

    for _ in 0..3 {
        assert!(matches!(
            handle_key_with_all_viewports(
                key(KeyCode::Down),
                &mut state,
                KeyViewports {
                    logout_picker: Some(PickerViewport { visible_rows: 2 }),
                    ..KeyViewports::default()
                },
                &[]
            ),
            InputAction::None
        ));
    }

    let picker = state.modals.logout_picker.as_ref().unwrap();
    assert_eq!(picker.selected, 3);
    let offset = state.scroll_offset(&state.scroll_views.logout_picker);
    assert!(picker.selected >= offset);
    assert!(picker.selected < offset + 2);
}

#[test]
fn model_picker_keys_take_priority_over_autocomplete_and_prompt() {
    let mut state = MissionControlState {
        provider: "openai-codex".to_string(),
        model: "gpt-a".to_string(),
        prompt_editor: crate::tui::state::PromptEditor::new("/m", 0),
        focus_pane: crate::tui::state::TuiFocusPane::Prompt,
        ..Default::default()
    };
    state.autocomplete = Some(crate::tui::state::AutocompleteState {
        candidates: vec![AutocompleteCandidate::slash_command("model", "select")],
        selected: 0,
        token_start: 0,
        token_end: 2,
        token: "/m".to_string(),
    });
    state.open_model_picker(
        vec![
            crate::model_catalog::ModelCatalogEntry::new_codex("gpt-a"),
            crate::model_catalog::ModelCatalogEntry::new_codex("gpt-b"),
        ],
        std::collections::BTreeSet::new(),
        None,
        1,
    );

    assert!(matches!(
        handle_key(key(KeyCode::Down), &mut state),
        InputAction::None
    ));
    assert!(matches!(
        handle_key(key(KeyCode::Enter), &mut state),
        InputAction::SelectModel(model) if model == "openai-codex/gpt-b"
    ));
    assert!(state.modals.model_picker.is_none());
    assert_eq!(state.prompt_plain_text(), "/m");
}

#[test]
fn model_picker_uses_actual_viewport_to_keep_selection_visible() {
    let mut state = MissionControlState {
        provider: "openai-codex".to_string(),
        model: "gpt-0".to_string(),
        ..Default::default()
    };
    state.open_model_picker(
        (0..6)
            .map(|index| crate::model_catalog::ModelCatalogEntry::new_codex(format!("gpt-{index}")))
            .collect(),
        std::collections::BTreeSet::new(),
        None,
        2,
    );

    for _ in 0..3 {
        assert!(matches!(
            handle_key_with_all_viewports(
                key(KeyCode::Down),
                &mut state,
                KeyViewports {
                    model_picker: Some(PickerViewport { visible_rows: 2 }),
                    ..KeyViewports::default()
                },
                &[]
            ),
            InputAction::None
        ));
    }

    let picker = state.modals.model_picker.as_ref().unwrap();
    assert_eq!(picker.selected, 3);
    assert_eq!(state.scroll_offset(&state.scroll_views.model_picker), 2);
}

#[test]
fn session_picker_keys_take_priority_over_prompt_and_autocomplete() {
    let mut state = MissionControlState {
        prompt_editor: crate::tui::state::PromptEditor::new("keep", 0),
        ..Default::default()
    };
    state.open_session_picker(
        vec![
            crate::tui::state::SessionPickerRow {
                id: "session-a".into(),
                label: "session-a".into(),
                is_current: false,
                preview: None,
            },
            crate::tui::state::SessionPickerRow {
                id: "session-b".into(),
                label: "session-b".into(),
                is_current: false,
                preview: None,
            },
        ],
        1,
    );
    assert!(matches!(
        handle_key(key(KeyCode::Down), &mut state),
        InputAction::None
    ));
    assert_eq!(state.prompt_plain_text(), "keep");
    assert_eq!(state.selected_session_id().as_deref(), Some("session-b"));
}

#[test]
fn session_picker_tab_focus_and_preview_arrows_do_not_move_list() {
    let mut state = MissionControlState::default();
    state.open_session_picker(
        vec![
            crate::tui::state::SessionPickerRow {
                id: "session-a".into(),
                label: "session-a".into(),
                is_current: false,
                preview: None,
            },
            crate::tui::state::SessionPickerRow {
                id: "session-b".into(),
                label: "session-b".into(),
                is_current: false,
                preview: None,
            },
        ],
        2,
    );
    state.set_session_preview(
        "session-a",
        crate::tui::state::SessionPreviewState::Ready(crate::tui::state::SessionPreview {
            first: crate::tui::state::PreviewMessage {
                role: "user",
                text: "one two three four five six seven eight nine ten".to_string(),
            },
            most_recent: None,
        }),
    );
    let viewports = KeyViewports {
        sessions_modal: Some(SessionPickerViewport {
            list_rows: 2,
            preview_rows: 1,
            preview_width: 8,
            preview_visible: true,
        }),
        ..KeyViewports::default()
    };

    assert_eq!(
        handle_key_with_all_viewports(key(KeyCode::Tab), &mut state, viewports, &[]),
        InputAction::None
    );
    assert_eq!(
        state.modals.session_picker.as_ref().unwrap().focus,
        crate::tui::state::SessionPickerFocus::Preview
    );
    assert_eq!(
        handle_key_with_all_viewports(key(KeyCode::Down), &mut state, viewports, &[]),
        InputAction::None
    );
    let picker = state.modals.session_picker.as_ref().unwrap();
    assert_eq!(picker.selected, 0);
    assert!(state.scroll_offset(&state.scroll_views.session_preview) > 0);

    assert_eq!(
        handle_key_with_all_viewports(
            modified_key(KeyCode::BackTab, KeyModifiers::SHIFT),
            &mut state,
            viewports,
            &[]
        ),
        InputAction::None
    );
    assert_eq!(
        state.modals.session_picker.as_ref().unwrap().focus,
        crate::tui::state::SessionPickerFocus::List
    );
}

#[test]
fn session_picker_tab_keeps_list_focus_when_preview_column_hidden() {
    let mut state = MissionControlState::default();
    state.open_session_picker(
        vec![crate::tui::state::SessionPickerRow {
            id: "session-a".into(),
            label: "session-a".into(),
            is_current: false,
            preview: None,
        }],
        1,
    );
    let viewports = KeyViewports {
        sessions_modal: Some(SessionPickerViewport {
            list_rows: 1,
            preview_rows: 1,
            preview_width: 1,
            preview_visible: false,
        }),
        ..KeyViewports::default()
    };

    assert_eq!(
        handle_key_with_all_viewports(key(KeyCode::Tab), &mut state, viewports, &[]),
        InputAction::None
    );
    assert_eq!(
        state.modals.session_picker.as_ref().unwrap().focus,
        crate::tui::state::SessionPickerFocus::List
    );
}

#[test]
fn session_picker_enter_selects_focused_row_and_empty_enter_noops() {
    let mut state = MissionControlState::default();
    state.open_session_picker(
        vec![crate::tui::state::SessionPickerRow {
            id: "session-a".into(),
            label: "session-a".into(),
            is_current: false,
            preview: None,
        }],
        1,
    );
    assert!(
        matches!(handle_key(key(KeyCode::Enter), &mut state), InputAction::SelectSession(id) if id == "session-a")
    );
    assert!(!state.session_picker_visible());

    state.open_session_picker(Vec::new(), 1);
    assert!(matches!(
        handle_key(key(KeyCode::Enter), &mut state),
        InputAction::None
    ));
    assert!(state.session_picker_visible());
}

#[test]
fn session_picker_escape_closes_without_selection() {
    let mut state = MissionControlState::default();
    state.open_session_picker(
        vec![crate::tui::state::SessionPickerRow {
            id: "session-a".into(),
            label: "session-a".into(),
            is_current: false,
            preview: None,
        }],
        1,
    );
    assert!(matches!(
        handle_key(key(KeyCode::Esc), &mut state),
        InputAction::None
    ));
    assert!(!state.session_picker_visible());
}