mod common;
use common::TestConnection;
use hyperdb_api::copy::CopyOptions;
#[test]
fn test_export_csv_basic() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command(
"CREATE TABLE csv_export (id INT NOT NULL, name TEXT, value DOUBLE PRECISION)",
)
.expect("create");
test.execute_command("INSERT INTO csv_export VALUES (1, 'Alice', 1.5), (2, 'Bob', 2.5)")
.expect("insert");
let csv = test
.connection
.export_csv_string("SELECT * FROM csv_export ORDER BY id")
.expect("export");
let lines: Vec<&str> = csv.trim().lines().collect();
assert_eq!(
lines.len(),
3,
"Expected header + 2 data rows, got: {lines:?}"
);
assert!(
lines[0].contains("id"),
"Header should contain 'id': {}",
lines[0]
);
assert!(
lines[0].contains("name"),
"Header should contain 'name': {}",
lines[0]
);
assert!(
lines[1].contains("Alice"),
"Row 1 should contain 'Alice': {}",
lines[1]
);
assert!(
lines[2].contains("Bob"),
"Row 2 should contain 'Bob': {}",
lines[2]
);
}
#[test]
fn test_export_csv_to_writer() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_writer (id INT NOT NULL, name TEXT)")
.expect("create");
test.execute_command("INSERT INTO csv_writer VALUES (1, 'test')")
.expect("insert");
let mut buf = Vec::new();
let bytes = test
.connection
.export_csv("SELECT * FROM csv_writer", &mut buf)
.expect("export");
assert!(bytes > 0, "Should write some bytes");
let csv = String::from_utf8(buf).expect("valid utf8");
assert!(csv.contains("test"), "Should contain data: {csv}");
}
#[test]
fn test_export_csv_empty_table() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_empty (id INT)")
.expect("create");
let csv = test
.connection
.export_csv_string("SELECT * FROM csv_empty")
.expect("export");
let lines: Vec<&str> = csv.trim().lines().collect();
assert_eq!(lines.len(), 1, "Should only have header: {lines:?}");
}
#[test]
fn test_export_text_custom_options() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_custom (id INT NOT NULL, name TEXT)")
.expect("create");
test.execute_command("INSERT INTO csv_custom VALUES (1, 'Alice'), (2, 'Bob')")
.expect("insert");
let opts = CopyOptions::csv().with_delimiter(b'|').with_header(true);
let mut buf = Vec::new();
test.connection
.export_text("SELECT * FROM csv_custom ORDER BY id", &opts, &mut buf)
.expect("export pipe-delimited");
let csv = String::from_utf8(buf).expect("valid utf8");
assert!(
csv.contains('|'),
"Pipe-delimited should contain pipes: {csv:?}"
);
assert!(csv.contains("Alice"), "Should contain data: {csv:?}");
}
#[test]
fn test_export_csv_large() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_large (id INT NOT NULL)")
.expect("create");
test.execute_command("INSERT INTO csv_large SELECT * FROM GENERATE_SERIES(1, 10000)")
.expect("insert");
let mut buf = Vec::new();
let bytes = test
.connection
.export_csv("SELECT * FROM csv_large", &mut buf)
.expect("export");
assert!(bytes > 0);
let csv = String::from_utf8(buf).expect("valid utf8");
let lines: Vec<&str> = csv.trim().lines().collect();
assert_eq!(lines.len(), 10001);
}
#[test]
fn test_streaming_copy_out() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE stream_out (id INT NOT NULL, data TEXT)")
.expect("create");
test.execute_command(
"INSERT INTO stream_out SELECT i, 'row_' || i::TEXT FROM GENERATE_SERIES(1, 1000) AS s(i)",
)
.expect("insert");
let mut buf = Vec::new();
let bytes = test
.connection
.export_csv("SELECT * FROM stream_out ORDER BY id", &mut buf)
.expect("streaming export");
assert!(bytes > 0);
let csv = String::from_utf8(buf).expect("valid utf8");
let lines: Vec<&str> = csv.trim().lines().collect();
assert_eq!(lines.len(), 1001, "Header + 1000 rows");
}
#[test]
fn test_import_csv_basic() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_import (id INT NOT NULL, name TEXT)")
.expect("create");
let csv_data = "1,Alice\n2,Bob\n3,Carol\n";
let rows = test
.connection
.import_csv("csv_import", csv_data.as_bytes())
.expect("import");
assert_eq!(rows, 3);
let count = test.count_tuples("csv_import").expect("count");
assert_eq!(count, 3);
let row = test
.connection
.fetch_one("SELECT name FROM csv_import WHERE id = 2")
.expect("fetch");
assert_eq!(row.get::<String>(0), Some("Bob".to_string()));
}
#[test]
fn test_import_csv_with_header() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_header (id INT NOT NULL, name TEXT)")
.expect("create");
let csv_data = "id,name\n1,Alice\n2,Bob\n";
let rows = test
.connection
.import_csv_with_header("csv_header", csv_data.as_bytes())
.expect("import");
assert_eq!(rows, 2);
let count = test.count_tuples("csv_header").expect("count");
assert_eq!(count, 2);
}
#[test]
fn test_import_text_tsv() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE tsv_import (id INT NOT NULL, name TEXT)")
.expect("create");
let opts = CopyOptions::csv().with_delimiter(b'\t');
let tsv_data = "1\tAlice\n2\tBob\n";
let rows = test
.connection
.import_text("tsv_import", &opts, tsv_data.as_bytes())
.expect("import");
assert_eq!(rows, 2);
let count = test.count_tuples("tsv_import").expect("count");
assert_eq!(count, 2);
}
#[test]
fn test_csv_roundtrip() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command(
"CREATE TABLE csv_src (id INT NOT NULL, name TEXT, value DOUBLE PRECISION)",
)
.expect("create src");
test.execute_command(
"INSERT INTO csv_src VALUES (1, 'Alice', 1.5), (2, 'Bob', 2.5), (3, 'Carol', 3.5)",
)
.expect("insert");
let csv = test
.connection
.export_csv_string("SELECT * FROM csv_src ORDER BY id")
.expect("export");
test.execute_command(
"CREATE TABLE csv_dst (id INT NOT NULL, name TEXT, value DOUBLE PRECISION)",
)
.expect("create dst");
let rows = test
.connection
.import_csv_with_header("csv_dst", csv.as_bytes())
.expect("import");
assert_eq!(rows, 3);
let count = test.count_tuples("csv_dst").expect("count");
assert_eq!(count, 3);
let row = test
.connection
.fetch_one("SELECT name, value FROM csv_dst WHERE id = 1")
.expect("fetch");
assert_eq!(row.get::<String>(0), Some("Alice".to_string()));
}
#[test]
fn test_import_csv_empty() {
let test = TestConnection::new().expect("Failed to create test connection");
test.execute_command("CREATE TABLE csv_empty_import (id INT, name TEXT)")
.expect("create");
let csv_data = "";
let rows = test
.connection
.import_csv("csv_empty_import", csv_data.as_bytes())
.expect("import");
assert_eq!(rows, 0);
}