use clicktype_transport::ClientBuilder;
use clicktype_core::ClickTable;
use clicktype_macros::ClickTable;
#[derive(ClickTable, Debug, Clone, serde::Serialize, serde::Deserialize, clickhouse::Row)]
#[click_table(name = "clicktype_test_table_data", primary_key = "id")]
pub struct TestTableData {
pub id: u64,
pub name: String,
pub value: i32,
}
#[derive(ClickTable, Debug, Clone, serde::Serialize, serde::Deserialize, clickhouse::Row)]
#[click_table(name = "clicktype_test_table_ddl", primary_key = "id")]
pub struct TestTableDdl {
pub id: u64,
pub name: String,
pub value: i32,
}
#[derive(ClickTable, Debug, Clone, serde::Serialize, serde::Deserialize, clickhouse::Row)]
#[click_table(name = "clicktype_test_table_create", primary_key = "id")]
pub struct TestTableCreate {
pub id: u64,
pub name: String,
pub value: i32,
}
#[tokio::test]
async fn test_connection() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123) .user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
let result = client.query_check("SELECT 1").await?;
println!("✓ Successfully connected to ClickHouse");
println!(" Database: {}", client.database());
println!(" Test query result rows: {}", result);
assert_eq!(result, 1, "SELECT 1 should return 1 row");
Ok(())
}
#[tokio::test]
async fn test_execute_simple_query() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let result = client.query_check("SELECT 1").await?;
assert_eq!(result, 1, "SELECT 1 should return 1 row");
println!("✓ SELECT 1 executed successfully (rows: {})", result);
Ok(())
}
#[tokio::test]
async fn test_create_and_drop_table() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let _ = client.execute("DROP TABLE IF EXISTS clicktype_test_table_create").await;
let ddl = TestTableCreate::create_table_ddl();
println!("✓ Generated DDL:\n{}", ddl);
client.execute(&ddl).await?;
println!("✓ Table created successfully");
let check_query = format!(
"SELECT name FROM system.tables WHERE database = '{}' AND name = 'clicktype_test_table_create'",
client.database()
);
let count = client.query_check(&check_query).await?;
assert_eq!(count, 1, "Table should exist");
println!("✓ Table verified in system.tables (count: {})", count);
client.execute("DROP TABLE IF EXISTS clicktype_test_table_create").await?;
println!("✓ Table dropped successfully");
Ok(())
}
#[tokio::test]
async fn test_table_with_data() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let _ = client.execute("DROP TABLE IF EXISTS clicktype_test_table_data").await;
let ddl = TestTableData::create_table_ddl();
client.execute(&ddl).await?;
println!("✓ Table created");
let test_data = vec![
TestTableData {
id: 1,
name: "test1".to_string(),
value: 100,
},
TestTableData {
id: 2,
name: "test2".to_string(),
value: 200,
},
TestTableData {
id: 3,
name: "test3".to_string(),
value: 300,
},
];
client.insert("clicktype_test_table_data", &test_data).await?;
println!("✓ Data inserted using real insert()");
let count = client.query_check("SELECT * FROM clicktype_test_table_data").await?;
assert_eq!(count, 3, "Should have 3 rows");
println!("✓ Data verified (rows: {})", count);
let rows: Vec<TestTableData> = client.query("SELECT * FROM clicktype_test_table_data ORDER BY id").await?;
assert_eq!(rows.len(), 3, "Should have 3 rows");
assert_eq!(rows[0].id, 1);
assert_eq!(rows[0].name, "test1");
assert_eq!(rows[0].value, 100);
println!("✓ Typed query<T>() works! Retrieved {} rows", rows.len());
let count_filtered = client.query_check("SELECT * FROM clicktype_test_table_data WHERE value > 150").await?;
assert_eq!(count_filtered, 2, "Should have 2 rows with value > 150");
println!("✓ Filtered query verified (rows: {})", count_filtered);
client.execute("DROP TABLE IF EXISTS clicktype_test_table_data").await?;
println!("✓ Table dropped");
Ok(())
}
#[tokio::test]
async fn test_ddl_generation_compatibility() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let _ = client.execute("DROP TABLE IF EXISTS clicktype_test_table_ddl").await;
let ddl = TestTableDdl::create_table_ddl();
println!("Generated DDL:\n{}\n", ddl);
client.execute(&ddl).await?;
println!("✓ DDL executed successfully - ClickHouse accepted our generated DDL");
let describe_query = "SELECT name FROM system.columns WHERE database = 'default' AND table = 'clicktype_test_table_ddl'";
let row_count = client.query_check(describe_query).await?;
assert!(row_count >= 3, "Should have at least 3 columns");
println!("✓ Table structure verified ({} columns)", row_count);
client.execute("DROP TABLE IF EXISTS clicktype_test_table_ddl").await?;
println!("✓ Cleanup complete");
Ok(())
}
#[derive(ClickTable, Debug, Clone, serde::Serialize, serde::Deserialize, clickhouse::Row)]
#[click_table(name = "clicktype_test_schema_ok", primary_key = "id")]
pub struct TestSchemaValidation {
pub id: u64,
pub name: String,
pub value: i32,
}
#[derive(ClickTable, Debug, Clone, serde::Serialize, serde::Deserialize, clickhouse::Row)]
#[click_table(name = "clicktype_test_schema_mismatch", primary_key = "id")]
pub struct TestSchemaMismatch {
pub id: u64,
pub name: String,
pub value: i32,
}
#[derive(ClickTable, Debug, Clone, serde::Serialize, serde::Deserialize, clickhouse::Row)]
#[click_table(name = "clicktype_test_schema_order", primary_key = "id")]
pub struct TestSchemaOrder {
pub id: u64,
pub name: String,
pub value: i32,
}
#[tokio::test]
async fn test_schema_validation_success() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let _ = client.execute("DROP TABLE IF EXISTS clicktype_test_schema_ok").await;
let ddl = TestSchemaValidation::create_table_ddl();
client.execute(&ddl).await?;
println!("✓ Table created");
let result = client.validate_schema::<TestSchemaValidation>("clicktype_test_schema_ok").await;
assert!(result.is_ok(), "Schema validation should succeed for matching schema");
println!("✓ Schema validation passed");
client.execute("DROP TABLE IF EXISTS clicktype_test_schema_ok").await?;
Ok(())
}
#[tokio::test]
async fn test_schema_validation_mismatch() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let _ = client.execute("DROP TABLE IF EXISTS clicktype_test_schema_mismatch").await;
let wrong_ddl = r#"
CREATE TABLE clicktype_test_schema_mismatch (
id UInt64,
name String,
value String
) ENGINE = MergeTree()
PRIMARY KEY (id)
ORDER BY (id)
"#;
client.execute(wrong_ddl).await?;
println!("✓ Table created with mismatched schema");
let result = client.validate_schema::<TestSchemaMismatch>("clicktype_test_schema_mismatch").await;
assert!(result.is_err(), "Schema validation should fail for mismatched schema");
if let Err(e) = result {
println!("✓ Schema validation correctly failed: {}", e);
assert!(e.to_string().contains("type mismatch"), "Error should mention type mismatch");
}
client.execute("DROP TABLE IF EXISTS clicktype_test_schema_mismatch").await?;
Ok(())
}
#[tokio::test]
async fn test_schema_validation_order_mismatch() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::default()
.host("84.247.133.129")
.port(8123)
.user("default")
.password("tQg6cKagcgF1f0lGfr0wFl0BqGWgrxjyoNw1dhZdjuRRaYofmmk8NPwhWGnmmayh")
.database("default")
.build()
.await?;
println!("✓ Connected to ClickHouse");
let _ = client.execute("DROP TABLE IF EXISTS clicktype_test_schema_order").await;
let wrong_order_ddl = r#"
CREATE TABLE clicktype_test_schema_order (
name String,
value Int32,
id UInt64
) ENGINE = MergeTree()
ORDER BY (id)
"#;
client.execute(wrong_order_ddl).await?;
println!("✓ Table created with different column order");
let result = client.validate_schema::<TestSchemaOrder>("clicktype_test_schema_order").await;
assert!(result.is_err(), "Schema validation should fail for column order mismatch");
if let Err(e) = result {
println!("✓ Schema validation correctly failed: {}", e);
let err_msg = e.to_string();
assert!(err_msg.contains("order mismatch") || err_msg.contains("position"),
"Error should mention column order/position issue, got: {}", err_msg);
}
client.execute("DROP TABLE IF EXISTS clicktype_test_schema_order").await?;
Ok(())
}