#![cfg(all(test, feature = "state_machine"))]
#![allow(clippy::all)]
use assert_cmd::Command;
use std::path::PathBuf;
fn get_test_data_root() -> PathBuf {
let root = std::env::var("CQLITE_DATASETS_ROOT")
.map(PathBuf::from)
.unwrap_or_else(|_| {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
PathBuf::from(manifest_dir)
.parent()
.expect("Failed to get parent directory")
.join("test-data/datasets")
});
let sstables_path = root.join("sstables");
if sstables_path.exists() {
sstables_path
} else {
root
}
}
fn get_simple_table_path() -> PathBuf {
let root = get_test_data_root();
let table_dir = root
.join("test_basic")
.join("simple_table-6aa08200a25111f0a3fef1a551383fb9");
table_dir.join("nb-1-big-Data.db")
}
fn get_schemas_dir() -> PathBuf {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
PathBuf::from(manifest_dir)
.parent()
.expect("Failed to get parent directory")
.join("test-data/schemas")
}
#[test]
fn test_fallback_disabled_by_default() {
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let query = format!("SELECT * FROM {}", table_path.display());
cmd.arg("-e").arg(&query).arg("--format").arg("json");
let output = cmd.output().expect("Failed to execute command");
assert!(
!output.status.success(),
"Command should fail when fallback disabled and ingestion unavailable"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("Using experimental read-sstable fallback"),
"Should not use fallback when disabled by default"
);
}
#[test]
fn test_fallback_enabled_with_flag() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = format!("SELECT * FROM {}", table_path.display());
cmd.arg("--enable-select-fallback")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json");
let output = cmd.output().expect("Failed to execute command");
assert!(
output.status.success(),
"Command should succeed with fallback enabled. stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("Using experimental read-sstable fallback"),
"Should show fallback warning when enabled. stderr: {}",
stderr
);
}
#[test]
fn test_fallback_enabled_with_env() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = format!("SELECT * FROM {}", table_path.display());
cmd.env("CQLITE_ENABLE_SELECT_FALLBACK", "true")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json");
let output = cmd.output().expect("Failed to execute command");
assert!(
output.status.success(),
"Command should succeed with fallback enabled via env. stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("Using experimental read-sstable fallback"),
"Should show fallback warning when enabled via env. stderr: {}",
stderr
);
}
#[test]
fn test_fallback_requires_ingestion_unavailable() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let schema_file = get_schemas_dir().join("basic-types.cql");
assert!(
schema_file.exists(),
"Test requires full SSTable dataset: schema file not found at {}",
schema_file.display()
);
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = format!("SELECT * FROM {}", table_path.display());
cmd.arg("--enable-select-fallback")
.arg("--schema")
.arg(&schema_file)
.arg("--dataset")
.arg("test_basic")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json");
let output = cmd.output().expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("Using experimental read-sstable fallback"),
"Should not use fallback when ingestion is available (schema + dataset provided). stderr: {}",
stderr
);
}
#[test]
fn test_fallback_only_for_select_queries() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let query = format!("DESCRIBE TABLE {}", table_path.display());
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
cmd.arg("--enable-select-fallback")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json");
let output = cmd.output().expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("Using experimental read-sstable fallback"),
"Should not use fallback for non-SELECT queries. stderr: {}",
stderr
);
}
#[test]
fn test_fallback_simple_select_parsing() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let queries = vec![
format!("SELECT * FROM {}", table_path.display()),
format!("select * from {}", table_path.display()),
format!(" SELECT * FROM {} ", table_path.display()),
format!("SELECT * FROM {};", table_path.display()),
];
for query in queries {
let mut test_cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
test_cmd
.arg("--enable-select-fallback")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json");
let output = test_cmd.output().expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("Using experimental read-sstable fallback"),
"Query '{}' should trigger fallback. stderr: {}",
query,
stderr
);
assert!(
stderr.contains("Extracted table path"),
"Query '{}' should extract table path. stderr: {}",
query,
stderr
);
}
}
#[test]
fn test_fallback_invalid_path_error() {
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = "SELECT * FROM /nonexistent/path/to/table";
cmd.arg("--enable-select-fallback")
.arg("-e")
.arg(query)
.arg("--format")
.arg("json");
let output = cmd.output().expect("Failed to execute command");
assert!(
!output.status.success(),
"Command should fail when table path doesn't exist"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("Table path does not exist")
|| stderr.contains("SELECT fallback failed")
|| stderr.contains("not found"),
"Should show error about invalid path. stderr: {}",
stderr
);
}
#[test]
fn test_fallback_json_output() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = format!("SELECT * FROM {}", table_path.display());
cmd.arg("--enable-select-fallback")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json")
.arg("--limit")
.arg("3");
let output = cmd.output().expect("Failed to execute command");
assert!(
output.status.success(),
"Command should succeed. stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8_lossy(&output.stdout);
let json_output: Vec<&str> = stdout
.lines()
.filter(|line| {
let trimmed = line.trim();
!trimmed.is_empty()
&& !line.starts_with("📖")
&& !line.starts_with("Displaying")
&& !line.starts_with("✅")
&& !(line.starts_with('[') && line.contains("202"))
})
.collect();
let json_str = json_output.join("\n");
let parse_result = serde_json::from_str::<serde_json::Value>(&json_str);
assert!(
parse_result.is_ok(),
"Output should be valid JSON. Got: {}",
json_str
);
}
#[test]
fn test_fallback_csv_output() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = format!("SELECT * FROM {}", table_path.display());
cmd.arg("--enable-select-fallback")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("csv")
.arg("--limit")
.arg("3");
let output = cmd.output().expect("Failed to execute command");
assert!(
output.status.success(),
"Command should succeed. stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8_lossy(&output.stdout);
let csv_output: Vec<&str> = stdout
.lines()
.filter(|line| {
let trimmed = line.trim();
!trimmed.is_empty()
&& !line.starts_with("📖")
&& !line.starts_with("Displaying")
&& !(line.starts_with('[') && line.contains("202"))
})
.collect();
let csv_str = csv_output.join("\n");
assert!(
csv_str.contains(','),
"CSV output should contain commas. Got: {}",
csv_str
);
let lines: Vec<&str> = csv_str.lines().collect();
assert!(
lines.len() >= 2,
"CSV should have at least header + 1 data row. Got {} lines",
lines.len()
);
}
#[test]
fn test_fallback_warning_message() {
let table_path = get_simple_table_path();
assert!(
table_path.exists(),
"Test requires full SSTable dataset: test data not found at {}",
table_path.display()
);
let mut cmd = Command::cargo_bin("cqlite").expect("Failed to find cqlite binary");
let query = format!("SELECT * FROM {}", table_path.display());
cmd.arg("--enable-select-fallback")
.arg("-e")
.arg(&query)
.arg("--format")
.arg("json");
let output = cmd.output().expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("⚠️"),
"Warning should include warning emoji. stderr: {}",
stderr
);
assert!(
stderr.contains("experimental"),
"Warning should mention 'experimental'. stderr: {}",
stderr
);
assert!(
stderr.contains("read-sstable fallback"),
"Warning should mention 'read-sstable fallback'. stderr: {}",
stderr
);
assert!(
stderr.contains("temporary feature"),
"Warning should mention 'temporary feature'. stderr: {}",
stderr
);
assert!(
stderr.contains("disabled by default"),
"Warning should mention 'disabled by default'. stderr: {}",
stderr
);
}