use super::*;
use ratatui::Terminal;
use ratatui::backend::TestBackend;
use sabiql_app::model::app_state::AppState;
use sabiql_app::model::shared::help::HelpOrigin;
use sabiql_app::model::shared::settings::KeymapPreset;
use sabiql_app::model::sql_editor::modal::SqlModalStatus;
use sabiql_app::policy::write::sql_risk::AcknowledgeReason;
use sabiql_domain::query_history::{QueryHistoryEntry, QueryResultStatus};
use sabiql_domain::{ConnectionId, QueryResult};
#[test]
fn sql_modal_with_completion() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state
.sql_modal
.editor_mut_for_input()
.set_content("SELECT * FROM us".to_string());
state.sql_modal.enter_editing();
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_completion_popup_with_scroll() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state
.sql_modal
.editor_mut_for_input()
.set_content("SELECT ".to_string());
state.sql_modal.enter_editing();
let candidates = vec![
CompletionCandidate {
text: "users".into(),
kind: CompletionKind::Table,
score: 100,
},
CompletionCandidate {
text: "posts".into(),
kind: CompletionKind::Table,
score: 90,
},
CompletionCandidate {
text: "comments".into(),
kind: CompletionKind::Table,
score: 80,
},
CompletionCandidate {
text: "id".into(),
kind: CompletionKind::Column,
score: 70,
},
CompletionCandidate {
text: "name".into(),
kind: CompletionKind::Column,
score: 60,
},
CompletionCandidate {
text: "email".into(),
kind: CompletionKind::Column,
score: 50,
},
CompletionCandidate {
text: "created_at".into(),
kind: CompletionKind::Column,
score: 40,
},
CompletionCandidate {
text: "updated_at".into(),
kind: CompletionKind::Column,
score: 30,
},
CompletionCandidate {
text: "COUNT".into(),
kind: CompletionKind::Keyword,
score: 20,
},
CompletionCandidate {
text: "DISTINCT".into(),
kind: CompletionKind::Keyword,
score: 10,
},
];
state
.sql_modal
.apply_completion_update(&candidates, 7, true);
for _ in 0..5 {
state.sql_modal.completion_next();
}
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_unknown_risk_acknowledge() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state
.sql_modal
.editor_mut_for_input()
.set_content("DO $$ BEGIN DELETE FROM users; END $$".to_string());
state
.sql_modal
.set_status_for_test(SqlModalStatus::ConfirmingRisk {
reason: AcknowledgeReason::UnknownRisk,
label: "DO".to_string(),
});
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_high_risk_without_target_acknowledge() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state
.sql_modal
.editor_mut_for_input()
.set_content("DROP TABLE a, b".to_string());
state
.sql_modal
.set_status_for_test(SqlModalStatus::ConfirmingRisk {
reason: AcknowledgeReason::TargetNameUnavailable,
label: "DROP".to_string(),
});
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_non_atomic_transaction_acknowledge() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state
.sql_modal
.editor_mut_for_input()
.set_content("PRAGMA foreign_keys = OFF; CREATE TABLE users(id INTEGER)".to_string());
state
.sql_modal
.set_status_for_test(SqlModalStatus::ConfirmingRisk {
reason: AcknowledgeReason::NonAtomicTransaction,
label: "SQLite transaction".to_string(),
});
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_analyze_unknown_risk_acknowledge() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state
.sql_modal
.set_status_for_test(SqlModalStatus::ConfirmingAnalyzeRisk {
query: "MERGE INTO t USING s ON t.id = s.id WHEN MATCHED THEN DELETE".to_string(),
reason: AcknowledgeReason::UnknownRisk,
});
state.sql_modal.set_active_tab(SqlModalTab::Plan);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_cursor_at_head() {
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_with_cursor("SELECT 1".to_string(), 0);
state.sql_modal.enter_editing();
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_cursor_at_middle() {
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_with_cursor("SELECT 1".to_string(), 4);
state.sql_modal.enter_editing();
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_cursor_at_tail() {
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 1".to_string());
state.sql_modal.enter_editing();
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_ide_editing() {
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 1".to_string());
state.sql_modal.set_status_for_test(SqlModalStatus::Editing);
state.settings.load_keymap_preset(KeymapPreset::Ide);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_normal_cursor_at_tail() {
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 1".to_string());
state.sql_modal.enter_normal();
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_success_select() {
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 * FROM users".to_string());
state.sql_modal.finish_adhoc_success(AdhocSuccessSnapshot {
command_tag: None,
row_count: 2,
execution_time_ms: 15,
});
state
.query
.set_current_result(Arc::new(QueryResult::success(
"SELECT * FROM users".to_string(),
vec!["id".to_string(), "name".to_string()],
vec![
vec!["1".to_string(), "Alice".to_string()],
vec!["2".to_string(), "Bob".to_string()],
],
15,
QuerySource::Adhoc,
)));
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_success_dml_with_command_tag() {
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("DELETE FROM users WHERE id = 1".to_string());
state.sql_modal.finish_adhoc_success(AdhocSuccessSnapshot {
command_tag: Some(CommandTag::Delete(3)),
row_count: 3,
execution_time_ms: 12,
});
state.query.set_current_result(Arc::new(
QueryResult::success(
"DELETE FROM users WHERE id = 1".to_string(),
vec![],
vec![],
12,
QuerySource::Adhoc,
)
.with_row_count(3)
.with_command_tag(CommandTag::Delete(3)),
));
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_success_ddl_create_table() {
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("CREATE TABLE backup AS SELECT * FROM users".to_string());
state.sql_modal.finish_adhoc_success(AdhocSuccessSnapshot {
command_tag: Some(CommandTag::Create("TABLE".to_string())),
row_count: 0,
execution_time_ms: 45,
});
state.query.set_current_result(Arc::new(
QueryResult::success(
"CREATE TABLE backup AS SELECT * FROM users".to_string(),
vec![],
vec![],
45,
QuerySource::Adhoc,
)
.with_command_tag(CommandTag::Create("TABLE".to_string())),
));
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_error_with_message() {
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 * FORM users".to_string());
state.sql_modal.finish_adhoc_error(
"ERROR: syntax error at or near \"FORM\"\nLINE 1: SELECT * FORM users\n ^"
.to_string(),
);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_confirming_high_matched() {
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("DROP TABLE users".to_string());
let mut input = TextInputState::default();
input.set_content("users".to_string());
state.sql_modal.begin_confirming_high(
AdhocRiskDecision {
risk_level: RiskLevel::High,
label: "DROP",
},
"users".to_string(),
);
*state.sql_modal.confirming_high_input_mut().unwrap() = input;
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_confirming_high_unmatched() {
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("DROP TABLE users".to_string());
let mut input = TextInputState::default();
input.set_content("use".to_string());
state.sql_modal.begin_confirming_high(
AdhocRiskDecision {
risk_level: RiskLevel::High,
label: "DROP",
},
"users".to_string(),
);
*state.sql_modal.confirming_high_input_mut().unwrap() = input;
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn help_overlay() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::Help);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn help_overlay_filtered_current_result() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.ui.set_focused_pane(FocusedPane::Result);
state.result_interaction.activate_cell(0, 0);
let origin = HelpOrigin::from_state(&state);
state.ui.help_mut().open(origin);
for ch in "copy".chars() {
state.ui.help_mut().insert_filter_char(ch);
}
state.ui.help_mut().enter_filter_editing();
state.modal.set_mode(InputMode::Help);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn help_overlay_long_key_rows() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::Help);
state.ui.help_mut().set_scroll_offset(58);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn help_overlay_narrow_horizontal_scroll() {
let mut state = connected_state();
let mut terminal = create_test_terminal_sized(50, 24);
state.modal.set_mode(InputMode::Help);
state.ui.set_terminal_width(50);
state.ui.set_terminal_height(24);
state.ui.help_mut().set_scroll_offset(58);
state.ui.help_mut().set_horizontal_offset(18);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn command_palette_overlay() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::CommandPalette);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn settings_overlay() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.settings.open(state.ui.theme_id());
state.modal.set_mode(InputMode::Settings);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn settings_overlay_keymap() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.settings.open(state.ui.theme_id());
state.settings.switch_next_section();
state.modal.set_mode(InputMode::Settings);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn settings_overlay_er_diagram() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.settings.open(state.ui.theme_id());
state.settings.switch_next_section();
state.settings.switch_next_section();
state.modal.set_mode(InputMode::Settings);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn settings_overlay_er_diagram_custom_browser() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state
.settings
.load_er_browser(Some("Brave Browser".to_string()));
state.settings.open(state.ui.theme_id());
state.settings.switch_next_section();
state.settings.switch_next_section();
state.modal.set_mode(InputMode::Settings);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn table_picker_overlay() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::TablePicker);
state.ui.table_picker_mut().insert_filter_str("user");
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn command_line_input() {
let mut state = connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::CommandLine);
state.command_line_input.set_content("sql".to_string());
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn query_history_picker_with_entries() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::QueryHistoryPicker);
state.query_history_picker.replace_entries(&[
QueryHistoryEntry::new(
"SELECT * FROM users WHERE id = 1".to_string(),
"2026-03-13T10:00:00Z".to_string(),
ConnectionId::from_string("test-conn"),
QueryResultStatus::Success,
None,
),
QueryHistoryEntry::new(
"INSERT INTO orders (user_id, total) VALUES (1, 100)".to_string(),
"2026-03-13T11:00:00Z".to_string(),
ConnectionId::from_string("test-conn"),
QueryResultStatus::Success,
Some(1),
),
QueryHistoryEntry::new(
"SELECT count(*) FROM users".to_string(),
"2026-03-13T12:00:00Z".to_string(),
ConnectionId::from_string("test-conn"),
QueryResultStatus::Failed,
None,
),
]);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn query_history_picker_empty() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::QueryHistoryPicker);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn query_history_picker_filter_mode() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::QueryHistoryPicker);
state.query_history_picker.replace_entries(&[
QueryHistoryEntry::new(
"SELECT * FROM users".to_string(),
"2026-03-13T10:00:00Z".to_string(),
ConnectionId::from_string("test-conn"),
QueryResultStatus::Success,
None,
),
QueryHistoryEntry::new(
"SELECT * FROM orders".to_string(),
"2026-03-13T11:00:00Z".to_string(),
ConnectionId::from_string("test-conn"),
QueryResultStatus::Success,
None,
),
]);
state.query_history_picker.insert_filter_str("user");
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_plan_tab_placeholder() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.sql_modal.set_active_tab(SqlModalTab::Plan);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_plan_tab_with_plan_text() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.sql_modal.set_active_tab(SqlModalTab::Plan);
state.explain.set_plan(
"Seq Scan on users (cost=0.00..35.50 rows=2550 width=36)\n Filter: (id > 10)".to_string(),
false,
42,
"SELECT * FROM users WHERE id > 10",
);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_plan_tab_with_error() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.sql_modal.set_active_tab(SqlModalTab::Plan);
state
.explain
.set_error("ERROR: relation \"nonexistent\" does not exist".to_string());
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_compare_tab_empty() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.sql_modal.set_active_tab(SqlModalTab::Compare);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_compare_tab_right_only() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.explain.set_plan(
"Seq Scan on users (cost=0.00..10.20 rows=10 width=3273)\n Filter: email_verified"
.to_string(),
false,
40,
"SELECT * FROM users WHERE email_verified",
);
state.sql_modal.set_active_tab(SqlModalTab::Compare);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_compare_tab_with_verdict() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.explain.set_plan(
"Seq Scan on users (cost=0.00..1000.00 rows=2550 width=36)\n Filter: (id > 10)"
.to_string(),
false,
100,
"SELECT * FROM users WHERE id > 10",
);
state.explain.set_plan(
"Index Scan using idx_users_id on users (cost=0.28..8.30 rows=1 width=36)\n Index Cond: (id > 10)"
.to_string(),
false,
5,
"SELECT * FROM users WHERE id > 10",
);
state.sql_modal.set_active_tab(SqlModalTab::Compare);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_compare_tab_unavailable() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.explain.set_plan(
"CREATE TABLE foo (id int)".to_string(),
false,
0,
"CREATE TABLE foo",
);
state.explain.set_plan(
"ALTER TABLE foo ADD COLUMN bar text".to_string(),
false,
0,
"ALTER TABLE foo",
);
state.sql_modal.set_active_tab(SqlModalTab::Compare);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_compare_tab_narrow_stacked() {
let mut state = create_test_state();
let backend = TestBackend::new(50, 24);
let mut terminal = Terminal::new(backend).unwrap();
state.modal.set_mode(InputMode::SqlModal);
state.explain.set_plan(
"Seq Scan on users (cost=0.00..1000.00 rows=2550 width=36)\n Filter: (id > 10)"
.to_string(),
false,
100,
"SELECT * FROM users WHERE id > 10",
);
state.explain.set_plan(
"Index Scan using idx_users_id on users (cost=0.28..8.30 rows=1 width=36)\n Index Cond: (id > 10)"
.to_string(),
false,
5,
"SELECT * FROM users WHERE id > 10",
);
state.sql_modal.set_active_tab(SqlModalTab::Compare);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sql_modal_normal_initial() {
let mut state = create_test_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
fn sqlite_connected_state() -> AppState {
let mut state = create_test_state();
state.session.activate_connection_with_dsn(
&ConnectionId::new(),
"local",
DatabaseType::SQLite,
"sqlite:///tmp/app.db",
);
state
.session
.mark_connected(Arc::new(fixtures::sample_metadata()));
state
}
#[test]
fn sqlite_sql_modal_omits_compare_tab() {
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_sql_modal_plan_tab_labels_query_plan() {
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
state.modal.set_mode(InputMode::SqlModal);
state.sql_modal.set_active_tab(SqlModalTab::Plan);
state.explain.set_plan(
"SEARCH users USING INDEX idx_users_name\n - SCAN orders".to_string(),
false,
42,
"DELETE FROM users WHERE name = 'alice'",
);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_diagnostics_overlay_loading() {
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
state.sqlite_diagnostics.begin_fetch();
state.modal.set_mode(InputMode::SqliteDiagnostics);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_diagnostics_overlay_loaded() {
use sabiql_domain::{DiagnosticField, SqliteDiagnosticsSnapshot};
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
let run_id = state.sqlite_diagnostics.begin_fetch();
state.sqlite_diagnostics.set_loaded(
run_id,
SqliteDiagnosticsSnapshot {
db_file: DiagnosticField::ok("/tmp/app.db"),
sqlite_version: DiagnosticField::ok("3.45.0"),
feature_summary: DiagnosticField::ok(
"FTS5: available\nFTS4: not available\nRTree: available\nJSON: available",
),
foreign_keys: DiagnosticField::ok("on"),
journal_mode: DiagnosticField::ok("wal"),
query_only: DiagnosticField::ok("off"),
busy_timeout: DiagnosticField::ok("5000"),
database_list: DiagnosticField::ok("0: main @ /tmp/app.db"),
quick_check: DiagnosticField::ok("ok"),
},
);
state.modal.set_mode(InputMode::SqliteDiagnostics);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_diagnostics_overlay_partial_failure() {
use sabiql_domain::{DiagnosticField, SqliteDiagnosticsSnapshot};
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
let run_id = state.sqlite_diagnostics.begin_fetch();
state.sqlite_diagnostics.set_loaded(
run_id,
SqliteDiagnosticsSnapshot {
db_file: DiagnosticField::ok("/tmp/app.db"),
sqlite_version: DiagnosticField::ok("3.45.0"),
feature_summary: DiagnosticField::Unavailable,
foreign_keys: DiagnosticField::err("timeout"),
journal_mode: DiagnosticField::ok("delete"),
query_only: DiagnosticField::ok("on"),
busy_timeout: DiagnosticField::ok("5000"),
database_list: DiagnosticField::ok("0: main @ /tmp/app.db"),
quick_check: DiagnosticField::ok("row 1 missing from index idx_users"),
},
);
state.modal.set_mode(InputMode::SqliteDiagnostics);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_diagnostics_overlay_quick_check_pending() {
use sabiql_domain::{DiagnosticField, SqliteDiagnosticsSnapshot};
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
let run_id = state.sqlite_diagnostics.begin_fetch();
state.sqlite_diagnostics.set_core_loaded(
run_id,
SqliteDiagnosticsSnapshot {
db_file: DiagnosticField::ok("/tmp/app.db"),
sqlite_version: DiagnosticField::ok("3.45.0"),
feature_summary: DiagnosticField::ok(
"FTS5: available\nFTS4: not available\nRTree: available\nJSON: available",
),
foreign_keys: DiagnosticField::ok("on"),
journal_mode: DiagnosticField::ok("wal"),
query_only: DiagnosticField::ok("off"),
busy_timeout: DiagnosticField::ok("5000"),
database_list: DiagnosticField::ok("0: main @ /tmp/app.db"),
quick_check: DiagnosticField::Pending,
},
);
state.sqlite_diagnostics.begin_quick_check();
state.modal.set_mode(InputMode::SqliteDiagnostics);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_diagnostics_overlay_quick_check_not_run() {
use sabiql_domain::{DiagnosticField, SqliteDiagnosticsSnapshot};
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal();
let run_id = state.sqlite_diagnostics.begin_fetch();
state.sqlite_diagnostics.set_core_loaded(
run_id,
SqliteDiagnosticsSnapshot {
db_file: DiagnosticField::ok("/tmp/app.db"),
sqlite_version: DiagnosticField::ok("3.45.0"),
feature_summary: DiagnosticField::ok(
"FTS5: available\nFTS4: not available\nRTree: available\nJSON: available",
),
foreign_keys: DiagnosticField::ok("on"),
journal_mode: DiagnosticField::ok("wal"),
query_only: DiagnosticField::ok("off"),
busy_timeout: DiagnosticField::ok("5000"),
database_list: DiagnosticField::ok("0: main @ /tmp/app.db"),
quick_check: DiagnosticField::Pending,
},
);
state.modal.set_mode(InputMode::SqliteDiagnostics);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}
#[test]
fn sqlite_diagnostics_overlay_wrapped_scroll() {
use sabiql_domain::{DiagnosticField, SqliteDiagnosticsSnapshot};
let mut state = sqlite_connected_state();
let mut terminal = create_test_terminal_sized(50, 24);
let run_id = state.sqlite_diagnostics.begin_fetch();
state.sqlite_diagnostics.set_loaded(
run_id,
SqliteDiagnosticsSnapshot {
db_file: DiagnosticField::ok(
"/tmp/very/long/database/path/that/will/wrap/in/a/narrow/viewport/app.db",
),
sqlite_version: DiagnosticField::ok("3.45.0"),
feature_summary: DiagnosticField::ok(
"FTS5: available\nFTS4: not available\nRTree: available\nJSON: available",
),
foreign_keys: DiagnosticField::ok("on"),
journal_mode: DiagnosticField::ok("wal"),
query_only: DiagnosticField::ok("off"),
busy_timeout: DiagnosticField::ok("5000"),
database_list: DiagnosticField::ok("0: main @ /tmp/app.db"),
quick_check: DiagnosticField::ok("ok"),
},
);
state.sqlite_diagnostics.set_scroll_offset(8);
state.modal.set_mode(InputMode::SqliteDiagnostics);
let output = render_to_string(&mut terminal, &mut state);
insta::assert_snapshot!(output);
}