#![allow(clippy::all)]
use anyhow::Result;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Stdio};
fn get_cli_binary() -> &'static str {
env!("CARGO_BIN_EXE_cqlite")
}
fn get_datasets_root() -> Result<PathBuf> {
std::env::var("CQLITE_DATASETS_ROOT")
.map(PathBuf::from)
.map_err(|_| anyhow::anyhow!("CQLITE_DATASETS_ROOT environment variable not set"))
}
fn get_schemas_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("test-data/schemas")
}
fn get_sstables_data_dir() -> Result<PathBuf> {
let datasets_root = get_datasets_root()?;
Ok(datasets_root.join("sstables"))
}
fn run_repl_with_input(
input: &str,
schema_file: &PathBuf,
data_dir: &PathBuf,
) -> std::io::Result<std::process::Output> {
let mut child = Command::new(get_cli_binary())
.args(&[
"--schema",
schema_file.to_str().unwrap(),
"--data-dir",
data_dir.to_str().unwrap(),
"repl",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
if let Some(mut stdin) = child.stdin.take() {
stdin.write_all(input.as_bytes())?;
drop(stdin); }
let output = child.wait_with_output()?;
Ok(output)
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_repl_select_query_basic() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = "SELECT * FROM test_basic.simple_table LIMIT 3;\n:quit\n";
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
eprintln!("Exit status: {}", output.status);
eprintln!("STDOUT:\n{}", String::from_utf8_lossy(&output.stdout));
eprintln!("STDERR:\n{}", String::from_utf8_lossy(&output.stderr));
assert!(
output.status.success(),
"REPL should exit with code 0, got {:?}",
output.status.code()
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty(), "REPL should produce output");
let has_data_indicators = stdout.contains("id")
|| stdout.contains("name")
|| stdout.contains("rows")
|| stdout.len() > 50;
assert!(
has_data_indicators,
"REPL output should contain query results or column indicators"
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_repl_select_query_with_columns() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = "SELECT id, name, age FROM test_basic.simple_table LIMIT 5;\n:quit\n";
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
assert!(output.status.success(), "REPL should exit successfully");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty(), "REPL should produce output for SELECT");
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_repl_config_command() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = ":config\n:quit\n";
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
assert!(
output.status.success(),
"REPL should exit successfully after :config"
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty(), "REPL :config should produce output");
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_repl_schema_command() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = ":schema\n:quit\n";
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
assert!(
output.status.success(),
"REPL should exit successfully after :schema"
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty(), "REPL :schema should produce output");
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
#[ignore] async fn test_repl_status_command() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = ":status\n:quit\n";
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stdout.is_empty() || !stderr.is_empty(),
"REPL :status should produce some output"
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_repl_multiple_queries() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = r#"
SELECT * FROM test_basic.simple_table LIMIT 2;
SELECT id, name FROM test_basic.simple_table LIMIT 3;
:config
:quit
"#;
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stdout.is_empty() || !stderr.is_empty(),
"REPL should produce output for multiple queries"
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_repl_returns_non_empty_rows() -> Result<()> {
let data_dir = get_sstables_data_dir()?;
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema not found at {:?}",
schema_file
);
let input = "SELECT * FROM test_basic.simple_table LIMIT 10;\n:quit\n";
let output = run_repl_with_input(input, &schema_file, &data_dir)?;
assert!(
output.status.success(),
"REPL should execute query successfully"
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty(), "REPL must return non-empty output");
let has_row_data = stdout.contains("id")
|| stdout.contains("name")
|| stdout.contains("-")
|| stdout.contains("|")
|| stdout.len() > 100;
assert!(
has_row_data,
"REPL output should contain row data (acceptance criteria: non-empty rows)"
);
Ok(())
}