#[cfg(test)]
mod tests {
use crate::datatypes::bulk_copy_metadata::{BulkCopyColumnMetadata, SqlDbType, TypeLength};
use crate::token::tokens::SqlCollation;
fn create_int_column(name: &str) -> BulkCopyColumnMetadata {
BulkCopyColumnMetadata::new(name, SqlDbType::Int, 0x26)
.with_nullable(false)
.with_length(4, TypeLength::Fixed(4))
}
fn create_varchar_column(name: &str, length: i32) -> BulkCopyColumnMetadata {
BulkCopyColumnMetadata::new(name, SqlDbType::VarChar, 0xA7)
.with_nullable(true)
.with_length(length, TypeLength::Variable(length))
.with_collation(SqlCollation::default())
}
fn create_nvarchar_column(name: &str, char_length: i32) -> BulkCopyColumnMetadata {
let byte_length = char_length * 2; BulkCopyColumnMetadata::new(name, SqlDbType::NVarChar, 0xE7)
.with_nullable(true)
.with_length(byte_length, TypeLength::Variable(byte_length))
.with_collation(SqlCollation::default())
}
#[test]
fn test_tds_type_mapping_comprehensive() {
assert_eq!(SqlDbType::TinyInt.to_tds_type(), 0x26);
assert_eq!(SqlDbType::SmallInt.to_tds_type(), 0x26);
assert_eq!(SqlDbType::Int.to_tds_type(), 0x26);
assert_eq!(SqlDbType::BigInt.to_tds_type(), 0x26);
assert_eq!(SqlDbType::Bit.to_tds_type(), 0x68);
assert_eq!(SqlDbType::Real.to_tds_type(), 0x6D);
assert_eq!(SqlDbType::Float.to_tds_type(), 0x6D);
assert_eq!(SqlDbType::Decimal.to_tds_type(), 0x6A);
assert_eq!(SqlDbType::Numeric.to_tds_type(), 0x6C);
assert_eq!(SqlDbType::Money.to_tds_type(), 0x6E);
assert_eq!(SqlDbType::SmallMoney.to_tds_type(), 0x6E);
assert_eq!(SqlDbType::Date.to_tds_type(), 0x28);
assert_eq!(SqlDbType::Time.to_tds_type(), 0x29);
assert_eq!(SqlDbType::DateTime.to_tds_type(), 0x6F);
assert_eq!(SqlDbType::DateTime2.to_tds_type(), 0x2A);
assert_eq!(SqlDbType::DateTimeOffset.to_tds_type(), 0x2B);
assert_eq!(SqlDbType::SmallDateTime.to_tds_type(), 0x6F);
assert_eq!(SqlDbType::Char.to_tds_type(), 0xAF);
assert_eq!(SqlDbType::VarChar.to_tds_type(), 0xA7);
assert_eq!(SqlDbType::Text.to_tds_type(), 0x23);
assert_eq!(SqlDbType::NChar.to_tds_type(), 0xEF);
assert_eq!(SqlDbType::NVarChar.to_tds_type(), 0xE7);
assert_eq!(SqlDbType::NText.to_tds_type(), 0x63);
assert_eq!(SqlDbType::Binary.to_tds_type(), 0xAD);
assert_eq!(SqlDbType::VarBinary.to_tds_type(), 0xA5);
assert_eq!(SqlDbType::Image.to_tds_type(), 0x22);
assert_eq!(SqlDbType::UniqueIdentifier.to_tds_type(), 0x24);
assert_eq!(SqlDbType::Xml.to_tds_type(), 0xF1);
assert_eq!(SqlDbType::Json.to_tds_type(), 0xF4);
assert_eq!(SqlDbType::Variant.to_tds_type(), 0x62);
assert_eq!(SqlDbType::Udt.to_tds_type(), 0xF0);
assert_eq!(SqlDbType::Vector.to_tds_type(), 0xF5);
}
#[test]
fn test_tds_type_fixed_mapping() {
assert_eq!(SqlDbType::TinyInt.to_tds_type_fixed(), 0x30);
assert_eq!(SqlDbType::SmallInt.to_tds_type_fixed(), 0x34);
assert_eq!(SqlDbType::Int.to_tds_type_fixed(), 0x38);
assert_eq!(SqlDbType::BigInt.to_tds_type_fixed(), 0x7F);
assert_eq!(SqlDbType::Bit.to_tds_type_fixed(), 0x32);
assert_eq!(SqlDbType::Real.to_tds_type_fixed(), 0x3B);
assert_eq!(SqlDbType::Float.to_tds_type_fixed(), 0x3E);
assert_eq!(
SqlDbType::Decimal.to_tds_type_fixed(),
SqlDbType::Decimal.to_tds_type()
);
assert_eq!(
SqlDbType::NVarChar.to_tds_type_fixed(),
SqlDbType::NVarChar.to_tds_type()
);
}
#[test]
fn test_bulk_copy_tds_type_mapping() {
assert_eq!(
SqlDbType::Int.to_bulk_copy_tds_type(),
SqlDbType::Int.to_tds_type()
);
assert_eq!(
SqlDbType::VarChar.to_bulk_copy_tds_type(),
SqlDbType::VarChar.to_tds_type()
);
assert_eq!(
SqlDbType::NVarChar.to_bulk_copy_tds_type(),
SqlDbType::NVarChar.to_tds_type()
);
assert_eq!(
SqlDbType::DateTime.to_bulk_copy_tds_type(),
SqlDbType::DateTime.to_tds_type()
);
assert_eq!(
SqlDbType::Vector.to_bulk_copy_tds_type(),
SqlDbType::Vector.to_tds_type()
);
assert_eq!(
SqlDbType::Json.to_tds_type(),
0xF4,
"JSON should return 0xF4 (TdsDataType::Json) from to_tds_type()"
);
assert_eq!(
SqlDbType::Json.to_bulk_copy_tds_type(),
0xE7,
"JSON should return 0xE7 (TdsDataType::NVarChar) from to_bulk_copy_tds_type() for bulk copy operations"
);
assert_ne!(
SqlDbType::Json.to_tds_type(),
SqlDbType::NVarChar.to_tds_type(),
"JSON type identifier (0xF4) should differ from NVARCHAR (0xE7)"
);
assert_eq!(
SqlDbType::Json.to_bulk_copy_tds_type(),
SqlDbType::NVarChar.to_tds_type(),
"JSON should use NVARCHAR encoding (0xE7) for bulk copy"
);
assert_eq!(
SqlDbType::Xml.to_tds_type(),
0xF1,
"XML should return 0xF1 (TdsDataType::Xml) from to_tds_type()"
);
assert_eq!(
SqlDbType::Xml.to_bulk_copy_tds_type(),
0xE7,
"XML should return 0xE7 (TdsDataType::NVarChar) from to_bulk_copy_tds_type() for bulk copy operations"
);
assert_ne!(
SqlDbType::Xml.to_tds_type(),
SqlDbType::NVarChar.to_tds_type(),
"XML type identifier (0xF1) should differ from NVARCHAR (0xE7)"
);
assert_eq!(
SqlDbType::Xml.to_bulk_copy_tds_type(),
SqlDbType::NVarChar.to_tds_type(),
"XML should use NVARCHAR encoding (0xE7) for bulk copy"
);
}
#[test]
fn test_insert_bulk_with_collation() {
use crate::connection::bulk_copy::BulkCopyOptions;
use crate::message::bulk_load::build_insert_bulk_command;
let col1 = create_int_column("Id");
let mut col2 = create_nvarchar_column("Name", 100);
col2.collation_name = Some("SQL_Latin1_General_CP1_CI_AS".to_string());
let mut col3 = create_varchar_column("Description", 255);
col3.collation_name = Some("Latin1_General_BIN".to_string());
let metadata = vec![col1, col2, col3];
let options = BulkCopyOptions::default();
let command = build_insert_bulk_command("dbo.TestTable", &metadata, &options).unwrap();
assert!(
command.contains("COLLATE SQL_Latin1_General_CP1_CI_AS"),
"Expected COLLATE clause for nvarchar column, got: {}",
command
);
assert!(
command.contains("COLLATE Latin1_General_BIN"),
"Expected COLLATE clause for varchar column, got: {}",
command
);
let int_section = &command[..command.find("Name").unwrap()];
assert!(
!int_section.contains("COLLATE"),
"Int column should not have COLLATE clause, got: {}",
command
);
}
#[test]
fn test_insert_bulk_without_collation() {
use crate::connection::bulk_copy::BulkCopyOptions;
use crate::message::bulk_load::build_insert_bulk_command;
let col1 = create_int_column("Id");
let col2 = create_nvarchar_column("Name", 100);
let col3 = create_varchar_column("Description", 255);
let metadata = vec![col1, col2, col3];
let options = BulkCopyOptions::default();
let command = build_insert_bulk_command("dbo.TestTable", &metadata, &options).unwrap();
assert!(
!command.contains("COLLATE"),
"Should not have COLLATE clause when collation_name is None, got: {}",
command
);
assert!(command.starts_with("INSERT BULK dbo.TestTable ("));
assert!(command.contains("[Id] int"));
assert!(command.contains("[Name] nvarchar(100)"));
assert!(command.contains("[Description] varchar(255)"));
}
#[test]
fn test_insert_bulk_mixed_collation() {
use crate::connection::bulk_copy::BulkCopyOptions;
use crate::message::bulk_load::build_insert_bulk_command;
let mut col1 = create_nvarchar_column("Name", 50);
col1.collation_name = Some("SQL_Latin1_General_CP1_CI_AS".to_string());
let col2 = create_nvarchar_column("Description", 200);
let mut col3 = create_varchar_column("Code", 10);
col3.collation_name = Some("Latin1_General_BIN".to_string());
let metadata = vec![col1, col2, col3];
let options = BulkCopyOptions::default();
let command = build_insert_bulk_command("dbo.MixedTable", &metadata, &options).unwrap();
assert!(
command.contains("[Name] nvarchar(50) COLLATE SQL_Latin1_General_CP1_CI_AS"),
"First column should have COLLATE, got: {}",
command
);
let desc_pos = command.find("Description").unwrap();
let next_comma_pos = command[desc_pos..]
.find(',')
.unwrap_or(command.len() - desc_pos);
let desc_section = &command[desc_pos..desc_pos + next_comma_pos];
assert!(
!desc_section.contains("COLLATE"),
"Description column should not have COLLATE, got: {}",
desc_section
);
assert!(
command.contains("[Code] varchar(10) COLLATE Latin1_General_BIN"),
"Third column should have COLLATE, got: {}",
command
);
}
#[test]
fn test_insert_bulk_collation_with_options() {
use crate::connection::bulk_copy::BulkCopyOptions;
use crate::message::bulk_load::build_insert_bulk_command;
let mut col1 = create_nvarchar_column("Name", 100);
col1.collation_name = Some("SQL_Latin1_General_CP1_CI_AS".to_string());
let metadata = vec![col1];
let options = BulkCopyOptions {
keep_nulls: true,
table_lock: true,
..BulkCopyOptions::default()
};
let command = build_insert_bulk_command("dbo.TestTable", &metadata, &options).unwrap();
assert!(
command.contains("COLLATE SQL_Latin1_General_CP1_CI_AS"),
"Expected COLLATE clause, got: {}",
command
);
assert!(
command.contains("WITH (KEEP_NULLS, TABLOCK)"),
"Expected WITH clause with options, got: {}",
command
);
}
}