sabiql 3.0.1

Fast, safe-by-design, Vim-first DB TUI with ER diagrams
use super::*;
use sabiql_ui::theme::SyntaxTokens;

fn contrast_theme() -> ThemePalette {
    ThemePalette {
        component: ComponentTokens {
            syntax: SyntaxTokens {
                sql_keyword: Color::Rgb(0x7d, 0xc4, 0xff),
                sql_string: Color::Rgb(0x9b, 0xf0, 0x8f),
                sql_number: Color::Rgb(0xff, 0xb8, 0x6b),
                sql_comment: Color::Rgb(0x7c, 0x8a, 0xa5),
                sql_operator: Color::Rgb(0x5e, 0xe0, 0xd5),
                ..DEFAULT_THEME.component.syntax
            },
            ..DEFAULT_THEME.component
        },
        ..DEFAULT_THEME
    }
}

#[test]
fn sql_modal_keyword_and_number_use_syntax_colors() {
    let mut state = create_test_state();
    let mut terminal = create_test_terminal();

    state.modal.set_mode(InputMode::SqlModal);
    state
        .sql_modal
        .editor_mut_for_input()
        .set_content("SELECT 42".to_string());
    state.sql_modal.enter_normal();

    let buffer = render_and_get_buffer(&mut terminal, &mut state);

    let keyword_cell = (0..TEST_HEIGHT)
        .flat_map(|y| (0..TEST_WIDTH).map(move |x| (x, y)))
        .find_map(|(x, y)| {
            buffer.cell((x, y)).and_then(|cell| {
                (cell.symbol() == "S" && cell.fg == DEFAULT_THEME.component.syntax.sql_keyword)
                    .then_some(cell)
            })
        });
    let number_cell = (0..TEST_HEIGHT)
        .flat_map(|y| (0..TEST_WIDTH).map(move |x| (x, y)))
        .find_map(|(x, y)| {
            buffer.cell((x, y)).and_then(|cell| {
                (cell.symbol() == "4" && cell.fg == DEFAULT_THEME.component.syntax.sql_number)
                    .then_some(cell)
            })
        });

    assert!(keyword_cell.is_some(), "Expected a blue SQL keyword cell");
    assert!(
        keyword_cell
            .expect("keyword cell should exist")
            .modifier
            .contains(Modifier::BOLD)
    );
    assert!(number_cell.is_some(), "Expected a yellow SQL number cell");
}
#[test]
fn sql_modal_string_comment_and_operator_use_syntax_colors() {
    let mut state = create_test_state();
    let mut terminal = create_test_terminal();

    state.modal.set_mode(InputMode::SqlModal);
    state
        .sql_modal
        .editor_mut_for_input()
        .set_content("SELECT 'x'::text -- note".to_string());
    state.sql_modal.enter_editing();

    let buffer = render_and_get_buffer(&mut terminal, &mut state);

    let has_string = has_cell(&buffer, |cell| {
        cell.symbol() == "'" && cell.fg == DEFAULT_THEME.component.syntax.sql_string
    });
    let has_operator = has_cell(&buffer, |cell| {
        cell.symbol() == ":" && cell.fg == DEFAULT_THEME.component.syntax.sql_operator
    });
    let has_comment = has_cell(&buffer, |cell| {
        cell.symbol() == "-" && cell.fg == DEFAULT_THEME.component.syntax.sql_comment
    });

    assert!(has_string, "Expected a green SQL string cell");
    assert!(has_operator, "Expected a cyan SQL operator cell");
    assert!(has_comment, "Expected a dark gray SQL comment cell");
}

#[test]
fn test_contrast_theme_applies_sql_syntax_colors() {
    let mut state = create_test_state();
    let mut terminal = create_test_terminal();

    state.modal.set_mode(InputMode::SqlModal);
    state
        .sql_modal
        .editor_mut_for_input()
        .set_content("SELECT 'x' + 42 -- note".to_string());
    state.sql_modal.enter_editing();

    let theme = contrast_theme();
    let buffer =
        render_and_get_buffer_at_with_theme(&mut terminal, &mut state, Instant::now(), &theme);

    let has_keyword = has_cell(&buffer, |cell| {
        cell.symbol() == "S" && cell.fg == theme.component.syntax.sql_keyword
    });
    let has_string = has_cell(&buffer, |cell| {
        cell.symbol() == "'" && cell.fg == theme.component.syntax.sql_string
    });
    let has_comment = has_cell(&buffer, |cell| {
        cell.symbol() == "-" && cell.fg == theme.component.syntax.sql_comment
    });
    let has_number = has_cell(&buffer, |cell| {
        cell.symbol() == "4" && cell.fg == theme.component.syntax.sql_number
    });
    let has_operator = has_cell(&buffer, |cell| {
        cell.symbol() == "+" && cell.fg == theme.component.syntax.sql_operator
    });

    assert!(
        has_keyword,
        "Expected SQL keyword color to resolve from the contrast theme"
    );
    assert!(
        has_string,
        "Expected SQL string color to resolve from the contrast theme"
    );
    assert!(
        has_comment,
        "Expected SQL comment color to resolve from the contrast theme"
    );
    assert!(
        has_number,
        "Expected SQL number color to resolve from the contrast theme"
    );
    assert!(
        has_operator,
        "Expected SQL operator color to resolve from the contrast theme"
    );
}

#[test]
fn sql_modal_unterminated_string_keeps_string_highlight() {
    let mut state = create_test_state();
    let mut terminal = create_test_terminal();

    state.modal.set_mode(InputMode::SqlModal);
    state
        .sql_modal
        .editor_mut_for_input()
        .set_content("SELECT 'unterminated".to_string());
    state.sql_modal.enter_editing();

    let buffer = render_and_get_buffer(&mut terminal, &mut state);

    let has_string = has_cell(&buffer, |cell| {
        (cell.symbol() == "'" || cell.symbol() == "u")
            && cell.fg == DEFAULT_THEME.component.syntax.sql_string
    });

    assert!(
        has_string,
        "Expected unterminated string input to keep SQL string highlight"
    );
}

#[test]
fn sql_modal_unterminated_block_comment_keeps_comment_highlight() {
    let mut state = create_test_state();
    let mut terminal = create_test_terminal();

    state.modal.set_mode(InputMode::SqlModal);
    state
        .sql_modal
        .editor_mut_for_input()
        .set_content("SELECT /* pending".to_string());
    state.sql_modal.enter_editing();

    let buffer = render_and_get_buffer(&mut terminal, &mut state);

    let has_comment = has_cell(&buffer, |cell| {
        (cell.symbol() == "/" || cell.symbol() == "*")
            && cell.fg == DEFAULT_THEME.component.syntax.sql_comment
    });

    assert!(
        has_comment,
        "Expected unterminated block comment input to keep SQL comment highlight"
    );
}