#![allow(clippy::all)]
use anyhow::Result;
use std::io::Write;
use std::process::Command;
fn get_test_datasets_root() -> String {
std::env::var("CQLITE_DATASETS_ROOT").unwrap_or_else(|_| "/tmp".to_string())
}
#[tokio::test]
async fn test_exit_code_invalid_cli_args() -> Result<()> {
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&["--invalid-flag"])
.output()?;
assert_eq!(
output.status.code(),
Some(2),
"Expected invalid CLI args exit code (2)"
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("unexpected argument")
|| stderr.to_lowercase().contains("invalid")
|| stderr.to_lowercase().contains("unrecognized"),
"Expected invalid argument error message in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
async fn test_exit_code_invalid_format_argument() -> Result<()> {
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&["--format", "invalid_format_type", "--help"])
.output()?;
assert_eq!(
output.status.code(),
Some(2),
"Expected invalid CLI args exit code (2) for bad format"
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("invalid") || stderr.to_lowercase().contains("error"),
"Expected format validation error in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_exit_code_schema_error_nonexistent_file() -> Result<()> {
let datasets_root = get_test_datasets_root();
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&[
"--data-dir",
&datasets_root,
"--schema",
"/nonexistent/path/to/schema.cql",
"--execute",
"SELECT * FROM test.table LIMIT 1",
])
.output()?;
assert_eq!(
output.status.code(),
Some(3),
"Expected schema error exit code (3) for nonexistent schema file"
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("schema")
|| stderr.to_lowercase().contains("not found")
|| stderr.to_lowercase().contains("no such file"),
"Expected schema error message in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_exit_code_data_directory_error_nonexistent() -> Result<()> {
use tempfile::NamedTempFile;
let mut schema_file = NamedTempFile::new()?;
schema_file.write_all(
b"CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};\n\
CREATE TABLE test.users (id int PRIMARY KEY, name text);"
)?;
let schema_path = schema_file.path();
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&[
"--data-dir",
"/nonexistent/data/directory/path",
"--schema",
schema_path.to_str().unwrap(),
"--execute",
"SELECT * FROM test.users LIMIT 1",
])
.output()?;
assert_eq!(
output.status.code(),
Some(4),
"Expected data directory error exit code (4) for nonexistent data dir"
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("data")
|| stderr.to_lowercase().contains("directory")
|| stderr.to_lowercase().contains("not found")
|| stderr.to_lowercase().contains("no such file"),
"Expected data directory error message in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_exit_code_data_directory_error_not_a_directory() -> Result<()> {
use std::io::Write;
use tempfile::NamedTempFile;
let mut schema_file = NamedTempFile::new()?;
schema_file.write_all(
b"CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};\n\
CREATE TABLE test.users (id int PRIMARY KEY, name text);"
)?;
let schema_path = schema_file.path();
let mut not_a_dir_file = NamedTempFile::new()?;
writeln!(not_a_dir_file, "this is a file, not a directory")?;
let not_a_dir_path = not_a_dir_file.path();
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&[
"--data-dir",
not_a_dir_path.to_str().unwrap(),
"--schema",
schema_path.to_str().unwrap(),
"--execute",
"SELECT * FROM test.users LIMIT 1",
])
.output()?;
let exit_code = output.status.code();
assert!(
exit_code == Some(4) || exit_code == Some(5),
"Expected data directory error (4) or query error (5) for file-as-directory, got {:?}",
exit_code
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
#[ignore]
async fn test_exit_code_query_execution_error_unsupported_query() -> Result<()> {
use tempfile::{tempdir, NamedTempFile};
let mut schema_file = NamedTempFile::new()?;
schema_file.write_all(
b"CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};\n\
CREATE TABLE test.users (id int PRIMARY KEY, name text);"
)?;
let schema_path = schema_file.path();
let temp_data_dir = tempdir()?;
let data_dir_path = temp_data_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&[
"--data-dir",
data_dir_path.to_str().unwrap(),
"--schema",
schema_path.to_str().unwrap(),
"--execute",
"SELECT * FROM test.users u JOIN test.posts p ON u.id = p.user_id",
])
.output()?;
assert_eq!(
output.status.code(),
Some(5),
"Expected query execution error exit code (5) for unsupported JOIN query"
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("unsupported")
|| stderr.to_lowercase().contains("not supported")
|| stderr.to_lowercase().contains("error"),
"Expected unsupported query error message in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
async fn test_exit_code_query_execution_error_invalid_syntax() -> Result<()> {
use std::io::Write;
use tempfile::{tempdir, NamedTempFile};
let mut schema_file = NamedTempFile::new()?;
schema_file.write_all(
b"CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};\n\
CREATE TABLE test.users (id int PRIMARY KEY, name text);"
)?;
let schema_path = schema_file.path();
let temp_data_dir = tempdir()?;
let data_dir_path = temp_data_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&[
"--data-dir",
data_dir_path.to_str().unwrap(),
"--schema",
schema_path.to_str().unwrap(),
"--execute",
"INVALID SQL QUERY SYNTAX HERE",
])
.output()?;
assert_eq!(
output.status.code(),
Some(5),
"Expected query execution error exit code (5) for invalid query syntax"
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("error")
|| stderr.to_lowercase().contains("parse")
|| stderr.to_lowercase().contains("syntax"),
"Expected query syntax error message in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
#[cfg(feature = "state_machine")]
#[ignore]
async fn test_exit_code_query_execution_error_nonexistent_table() -> Result<()> {
use tempfile::{tempdir, NamedTempFile};
let mut schema_file = NamedTempFile::new()?;
schema_file.write_all(
b"CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};\n\
CREATE TABLE test.users (id int PRIMARY KEY, name text);"
)?;
let schema_path = schema_file.path();
let temp_data_dir = tempdir()?;
let data_dir_path = temp_data_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&[
"--data-dir",
data_dir_path.to_str().unwrap(),
"--schema",
schema_path.to_str().unwrap(),
"--execute",
"SELECT * FROM test.nonexistent_table LIMIT 1",
])
.output()?;
let exit_code = output.status.code();
assert!(
exit_code == Some(5) || exit_code == Some(3),
"Expected query execution error (5) or schema error (3) for nonexistent table, got {:?}",
exit_code
);
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.to_lowercase().contains("error")
|| stderr.to_lowercase().contains("not found")
|| stderr.to_lowercase().contains("table"),
"Expected table not found error message in stderr: {}",
stderr
);
Ok(())
}
#[tokio::test]
async fn test_exit_code_success_help_command() -> Result<()> {
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&["--help"])
.output()?;
assert_eq!(
output.status.code(),
Some(0),
"Expected success exit code (0) for --help"
);
let stdout = String::from_utf8(output.stdout)?;
assert!(
stdout.to_lowercase().contains("usage") || stdout.to_lowercase().contains("cqlite"),
"Expected help message in stdout"
);
Ok(())
}
#[tokio::test]
async fn test_exit_code_success_version_command() -> Result<()> {
let output = Command::new(env!("CARGO_BIN_EXE_cqlite"))
.args(&["--version"])
.output()?;
assert_eq!(
output.status.code(),
Some(0),
"Expected success exit code (0) for --version"
);
let stdout = String::from_utf8(output.stdout)?;
assert!(!stdout.is_empty(), "Expected version information in stdout");
Ok(())
}