mod common;
use common::TestConnection;
use hyperdb_api::{Catalog, FromRow, Row, ServerVersion};
#[test]
fn test_execute_batch() {
let test = TestConnection::new().expect("Failed to create test connection");
let total = test
.connection
.execute_batch(&[
"CREATE TABLE batch_test (id INT NOT NULL, name TEXT)",
"INSERT INTO batch_test VALUES (1, 'Alice')",
"INSERT INTO batch_test VALUES (2, 'Bob')",
"INSERT INTO batch_test VALUES (3, 'Carol')",
])
.expect("execute_batch");
assert_eq!(total, 3);
let count = test.count_tuples("batch_test").expect("count");
assert_eq!(count, 3);
}
#[test]
fn test_execute_batch_empty() {
let test = TestConnection::new().expect("Failed to create test connection");
let total = test.connection.execute_batch(&[]).expect("empty batch");
assert_eq!(total, 0);
}
#[test]
fn test_execute_batch_skips_blank() {
let test = TestConnection::new().expect("Failed to create test connection");
let total = test
.connection
.execute_batch(&[
"CREATE TABLE blank_test (id INT)",
"",
" ",
"INSERT INTO blank_test VALUES (1)",
])
.expect("batch with blanks");
assert_eq!(total, 1);
}
#[test]
fn test_execute_batch_stops_on_error() {
let test = TestConnection::new().expect("Failed to create test connection");
let result = test.connection.execute_batch(&[
"CREATE TABLE err_test (id INT NOT NULL)",
"INSERT INTO err_test VALUES (1)",
"THIS IS INVALID SQL",
"INSERT INTO err_test VALUES (2)", ]);
assert!(result.is_err(), "Should fail on invalid SQL");
}
#[test]
fn test_catalog_get_row_count() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE count_test (id INT NOT NULL)")
.expect("create");
test.execute_command("INSERT INTO count_test SELECT * FROM GENERATE_SERIES(1, 42)")
.expect("insert");
let catalog = Catalog::new(&test.connection);
let count = catalog.get_row_count("count_test").expect("row count");
assert_eq!(count, 42);
}
#[test]
fn test_catalog_get_column_names() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command(
"CREATE TABLE colname_test (id INT NOT NULL, name TEXT, value DOUBLE PRECISION)",
)
.expect("create");
let catalog = Catalog::new(&test.connection);
let columns = catalog
.get_column_names("colname_test")
.expect("column names");
assert_eq!(columns.len(), 3);
assert_eq!(columns[0], "id");
assert_eq!(columns[1], "name");
assert_eq!(columns[2], "value");
}
#[test]
fn test_catalog_get_database_names() {
let test = TestConnection::new().expect("Failed to create test connection");
let catalog = Catalog::new(&test.connection);
let databases = catalog.get_database_names().expect("database names");
assert!(!databases.is_empty(), "Should have at least one database");
}
#[test]
fn test_server_version_parse() {
let v = ServerVersion::parse("0.0.19038").unwrap();
assert_eq!(v.major(), 0);
assert_eq!(v.minor(), 0);
assert_eq!(v.patch(), 19038);
}
#[test]
fn test_server_version_comparison() {
let v1 = ServerVersion::new(1, 0, 0);
let v2 = ServerVersion::new(1, 0, 1);
let v3 = ServerVersion::new(2, 0, 0);
assert!(v1 < v2);
assert!(v2 < v3);
assert_eq!(v1, ServerVersion::new(1, 0, 0));
}
#[test]
fn test_server_version_from_connection() {
let test = TestConnection::new().expect("Failed to create test connection");
let version = test.connection.server_version();
if let Some(v) = version {
assert!(
v >= ServerVersion::new(0, 0, 1),
"Version should be at least 0.0.1, got: {v}"
);
}
}
#[test]
fn test_copy_database() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE copy_src (id INT NOT NULL)")
.expect("create");
test.execute_command("INSERT INTO copy_src VALUES (1), (2), (3)")
.expect("insert");
let src_path = test.database_path.to_string_lossy().to_string();
let dst_path = src_path.replace(".hyper", "_backup.hyper");
let _ = std::fs::remove_file(&dst_path);
let result = test.connection.copy_database(&src_path, &dst_path);
if let Ok(()) = result {
assert!(
std::path::Path::new(&dst_path).exists(),
"Backup file should exist"
);
let _ = std::fs::remove_file(&dst_path);
}
}
#[test]
fn test_explain() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE explain_test (id INT NOT NULL, value DOUBLE PRECISION)")
.expect("create");
test.execute_command("INSERT INTO explain_test VALUES (1, 1.0), (2, 2.0)")
.expect("insert");
let plan = test
.connection
.explain("SELECT * FROM explain_test WHERE id > 0")
.expect("explain");
assert!(!plan.is_empty(), "EXPLAIN should return a non-empty plan");
}
#[test]
fn test_connection_builder_application_name() {
let test = TestConnection::new().expect("Failed to create test connection");
let endpoint = test.connection.parameter_status("server_version");
assert!(endpoint.is_some() || endpoint.is_none()); }
#[derive(Debug, PartialEq)]
struct TestUser {
id: i32,
name: String,
score: f64,
}
impl FromRow for TestUser {
fn from_row(row: &Row) -> hyperdb_api::Result<Self> {
Ok(TestUser {
id: row
.get::<i32>(0)
.ok_or_else(|| hyperdb_api::Error::new("NULL id"))?,
name: row.get::<String>(1).unwrap_or_default(),
score: row.get::<f64>(2).unwrap_or(0.0),
})
}
}
#[test]
fn test_fetch_one_as() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command(
"CREATE TABLE from_row_test (id INT NOT NULL, name TEXT, score DOUBLE PRECISION)",
)
.expect("create");
test.execute_command("INSERT INTO from_row_test VALUES (1, 'Alice', 95.5)")
.expect("insert");
let user: TestUser = test
.connection
.fetch_one_as("SELECT id, name, score FROM from_row_test WHERE id = 1")
.expect("fetch_one_as");
assert_eq!(user.id, 1);
assert_eq!(user.name, "Alice");
assert!((user.score - 95.5).abs() < 0.001);
}
#[test]
fn test_fetch_all_as() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command(
"CREATE TABLE from_row_all (id INT NOT NULL, name TEXT, score DOUBLE PRECISION)",
)
.expect("create");
test.execute_command(
"INSERT INTO from_row_all VALUES (1, 'Alice', 95.5), (2, 'Bob', 87.0), (3, 'Carol', 92.3)",
)
.expect("insert");
let users: Vec<TestUser> = test
.connection
.fetch_all_as("SELECT id, name, score FROM from_row_all ORDER BY id")
.expect("fetch_all_as");
assert_eq!(users.len(), 3);
assert_eq!(users[0].id, 1);
assert_eq!(users[0].name, "Alice");
assert_eq!(users[1].id, 2);
assert_eq!(users[1].name, "Bob");
assert_eq!(users[2].id, 3);
assert_eq!(users[2].name, "Carol");
}
#[test]
fn test_from_row_tuple() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE tuple_test (id INT NOT NULL, name TEXT)")
.expect("create");
test.execute_command("INSERT INTO tuple_test VALUES (1, 'Alice')")
.expect("insert");
let row = test
.connection
.fetch_one("SELECT id, name FROM tuple_test")
.expect("fetch");
let tuple: (Option<i32>, Option<String>) =
<(Option<i32>, Option<String>) as FromRow>::from_row(&row).expect("from_row");
assert_eq!(tuple.0, Some(1));
assert_eq!(tuple.1, Some("Alice".to_string()));
}
#[test]
fn test_ping() {
let test = TestConnection::new().expect("Failed to create test connection");
test.connection.ping().expect("ping should succeed");
}
#[test]
fn test_ping_after_operations() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE ping_test (id INT)")
.expect("create");
test.execute_command("INSERT INTO ping_test VALUES (1)")
.expect("insert");
test.connection.ping().expect("ping after operations");
}