use super::output_capture::CapturedOutput;
use sqlmodel_console::{OutputMode, SqlModelConsole};
use std::collections::HashMap;
#[derive(Default, Clone)]
struct MockEnv {
vars: HashMap<&'static str, &'static str>,
}
impl MockEnv {
fn new() -> Self {
Self::default()
}
fn with(key: &'static str, value: &'static str) -> Self {
let mut env = Self::new();
env.set(key, value);
env
}
fn set(&mut self, key: &'static str, value: &'static str) -> &mut Self {
self.vars.insert(key, value);
self
}
fn lookup(&self, key: &str) -> Option<String> {
self.vars.get(key).map(|v| (*v).to_string())
}
fn console(&self) -> SqlModelConsole {
SqlModelConsole::with_env(|k| self.lookup(k), true)
}
fn is_agent(&self) -> bool {
OutputMode::is_agent_environment_with(|k| self.lookup(k))
}
}
#[test]
fn e2e_plain_mode_produces_no_ansi() {
let env = MockEnv::with("SQLMODEL_PLAIN", "1");
let console = env.console();
assert!(console.is_plain(), "Console should be in plain mode");
assert!(
!console.mode().supports_ansi(),
"Plain mode should not support ANSI"
);
let output = CapturedOutput::from_strings(
"Query completed: 5 rows returned".to_string(),
"Processing...".to_string(),
);
output.assert_plain_mode_clean();
output.assert_stderr_plain();
}
#[test]
fn e2e_agent_detection_triggers_plain_mode() {
let agents = [
("CLAUDE_CODE", "1"),
("CODEX_CLI", "1"),
("CURSOR_SESSION", "test-session"),
("AIDER_MODEL", "gpt-4"),
("GITHUB_COPILOT", "1"),
("GEMINI_CLI", "1"),
];
for (var, value) in agents {
let env = MockEnv::with(var, value);
let console = env.console();
assert!(
console.is_plain(),
"{var}={value} should trigger plain mode, got {:?}",
console.mode()
);
}
}
#[test]
fn e2e_force_rich_overrides_agent() {
let mut env = MockEnv::new();
env.set("CLAUDE_CODE", "1");
env.set("SQLMODEL_RICH", "1");
let console = env.console();
assert!(
console.is_rich(),
"SQLMODEL_RICH should override agent detection"
);
}
#[test]
#[allow(clippy::items_after_statements)]
fn e2e_json_mode_for_structured_output() {
let env = MockEnv::with("SQLMODEL_JSON", "1");
let console = env.console();
assert!(console.is_json(), "Console should be in JSON mode");
assert!(
console.mode().is_structured(),
"JSON mode should be structured"
);
#[derive(serde::Serialize)]
struct TestData {
query: String,
rows: usize,
}
let data = TestData {
query: "SELECT * FROM users".to_string(),
rows: 42,
};
let result = console.print_json(&data);
assert!(result.is_ok(), "JSON serialization should succeed");
}
#[test]
fn e2e_ci_environment_triggers_plain() {
let env = MockEnv::with("CI", "true");
let console = env.console();
assert!(console.is_plain(), "CI=true should trigger plain mode");
}
#[test]
fn e2e_no_color_triggers_plain() {
let env = MockEnv::with("NO_COLOR", "");
let console = env.console();
assert!(
console.is_plain(),
"NO_COLOR presence should trigger plain mode"
);
}
#[test]
fn e2e_dumb_terminal_triggers_plain() {
let env = MockEnv::with("TERM", "dumb");
let console = env.console();
assert!(console.is_plain(), "TERM=dumb should trigger plain mode");
}
#[test]
fn e2e_dynamic_mode_switching() {
let mut console = SqlModelConsole::with_mode(OutputMode::Plain);
assert!(console.is_plain());
console.set_mode(OutputMode::Rich);
assert!(console.is_rich());
assert!(console.mode().supports_ansi());
console.set_mode(OutputMode::Json);
assert!(console.is_json());
assert!(!console.mode().supports_ansi());
console.set_mode(OutputMode::Plain);
assert!(console.is_plain());
}
#[test]
fn e2e_mode_predicates_mutually_exclusive() {
for mode in [OutputMode::Plain, OutputMode::Rich, OutputMode::Json] {
let console = SqlModelConsole::with_mode(mode);
let predicates = [console.is_plain(), console.is_rich(), console.is_json()];
let true_count = predicates.iter().filter(|&&p| p).count();
assert_eq!(
true_count, 1,
"Exactly one predicate should be true for mode {mode:?}"
);
}
}
#[test]
fn e2e_plain_output_parseable() {
let console = SqlModelConsole::with_mode(OutputMode::Plain);
assert!(console.mode().as_str().is_ascii());
assert_eq!(console.mode().as_str(), "plain");
}
#[test]
fn e2e_rich_mode_string() {
let console = SqlModelConsole::with_mode(OutputMode::Rich);
assert_eq!(console.mode().as_str(), "rich");
assert!(console.mode().supports_ansi());
}
#[test]
fn e2e_json_mode_string() {
let console = SqlModelConsole::with_mode(OutputMode::Json);
assert_eq!(console.mode().as_str(), "json");
assert!(console.mode().is_structured());
}
#[test]
fn e2e_console_builder_methods() {
let console = SqlModelConsole::new().plain_width(120);
assert_eq!(console.get_plain_width(), 120);
let console = SqlModelConsole::with_mode(OutputMode::Rich).plain_width(100);
assert!(console.is_rich());
assert_eq!(console.get_plain_width(), 100);
}
#[test]
fn e2e_console_default_equals_new() {
let c1 = SqlModelConsole::default();
let c2 = SqlModelConsole::new();
assert_eq!(c1.mode(), c2.mode());
assert_eq!(c1.get_plain_width(), c2.get_plain_width());
}
#[test]
fn e2e_full_mode_detection_workflow() {
let mut env = MockEnv::new();
assert!(!env.is_agent());
env.set("CLAUDE_CODE", "1");
assert!(env.is_agent());
let console = env.console();
assert!(console.is_plain());
env.set("SQLMODEL_RICH", "1");
let console2 = env.console();
assert!(console2.is_rich());
env.set("SQLMODEL_PLAIN", "1");
let console3 = env.console();
assert!(console3.is_plain());
}
#[test]
fn e2e_multiple_agent_markers() {
let mut env = MockEnv::new();
env.set("CLAUDE_CODE", "1");
env.set("CODEX_CLI", "1");
env.set("CURSOR_SESSION", "test");
env.set("GEMINI_CLI", "1");
assert!(env.is_agent());
let console = env.console();
assert!(console.is_plain());
}