Skip to content

Commit 319674e

Browse files
committed
Added unit test cases
1 parent 9877993 commit 319674e

1 file changed

Lines changed: 175 additions & 0 deletions

File tree

crates/fluss/src/metadata/datatype.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@ impl DecimalType {
497497
self.scale
498498
}
499499

500+
pub fn is_nullable(&self) -> bool {
501+
self.nullable
502+
}
503+
500504
pub fn as_non_nullable(&self) -> Self {
501505
Self::with_nullable(false, self.precision, self.scale)
502506
.expect("Invalid decimal precision or scale")
@@ -594,6 +598,10 @@ impl TimeType {
594598
self.precision
595599
}
596600

601+
pub fn is_nullable(&self) -> bool {
602+
self.nullable
603+
}
604+
597605
pub fn as_non_nullable(&self) -> Self {
598606
Self::with_nullable(false, self.precision).expect("Invalid time precision")
599607
}
@@ -655,6 +663,10 @@ impl TimestampType {
655663
self.precision
656664
}
657665

666+
pub fn is_nullable(&self) -> bool {
667+
self.nullable
668+
}
669+
658670
pub fn as_non_nullable(&self) -> Self {
659671
Self::with_nullable(false, self.precision).expect("Invalid timestamp precision")
660672
}
@@ -717,6 +729,10 @@ impl TimestampLTzType {
717729
self.precision
718730
}
719731

732+
pub fn is_nullable(&self) -> bool {
733+
self.nullable
734+
}
735+
720736
pub fn as_non_nullable(&self) -> Self {
721737
Self::with_nullable(false, self.precision)
722738
.expect("Invalid timestamp with local time zone precision")
@@ -1405,6 +1421,11 @@ fn test_deeply_nested_types() {
14051421
assert_eq!(nested.to_string(), "ARRAY<MAP<STRING, ROW<x INT, y INT>>>");
14061422
}
14071423

1424+
1425+
// ============================================================================
1426+
// DecimalType validation tests
1427+
// ============================================================================
1428+
14081429
#[test]
14091430
fn test_decimal_invalid_precision() {
14101431
// DecimalType::with_nullable should return an error for invalid precision
@@ -1431,6 +1452,65 @@ fn test_decimal_invalid_scale() {
14311452
);
14321453
}
14331454

1455+
// ============================================================================
1456+
// DecimalType validation tests - edge cases
1457+
// ============================================================================
1458+
1459+
#[test]
1460+
fn test_decimal_valid_precision_and_scale() {
1461+
// Valid: precision=10, scale=2
1462+
let result = DecimalType::with_nullable(true, 10, 2);
1463+
assert!(result.is_ok());
1464+
let decimal = result.unwrap();
1465+
assert_eq!(decimal.precision(), 10);
1466+
assert_eq!(decimal.scale(), 2);
1467+
assert!(decimal.is_nullable());
1468+
1469+
// Valid: precision=38, scale=0
1470+
let result = DecimalType::with_nullable(true, 38, 0);
1471+
assert!(result.is_ok());
1472+
let decimal = result.unwrap();
1473+
assert_eq!(decimal.precision(), 38);
1474+
assert_eq!(decimal.scale(), 0);
1475+
1476+
// Valid: precision=1, scale=0
1477+
let result = DecimalType::with_nullable(false, 1, 0);
1478+
assert!(result.is_ok());
1479+
let decimal = result.unwrap();
1480+
assert_eq!(decimal.precision(), 1);
1481+
assert_eq!(decimal.scale(), 0);
1482+
assert!(!decimal.is_nullable());
1483+
}
1484+
1485+
#[test]
1486+
fn test_decimal_invalid_precision_zero() {
1487+
// Invalid: precision=0 (edge case not covered by existing tests)
1488+
let result = DecimalType::with_nullable(true, 0, 0);
1489+
assert!(result.is_err());
1490+
assert!(
1491+
result
1492+
.unwrap_err()
1493+
.to_string()
1494+
.contains("Decimal precision must be between 1 and 38")
1495+
);
1496+
}
1497+
1498+
1499+
// ============================================================================
1500+
// TimeType validation tests
1501+
// ============================================================================
1502+
1503+
#[test]
1504+
fn test_time_valid_precision() {
1505+
// Test all valid precision values 0 through 9
1506+
for precision in 0..=9 {
1507+
let result = TimeType::with_nullable(true, precision);
1508+
assert!(result.is_ok(), "precision {} should be valid", precision);
1509+
let time = result.unwrap();
1510+
assert_eq!(time.precision(), precision);
1511+
}
1512+
}
1513+
14341514
#[test]
14351515
fn test_time_invalid_precision() {
14361516
// TimeType::with_nullable should return an error for invalid precision
@@ -1444,6 +1524,25 @@ fn test_time_invalid_precision() {
14441524
);
14451525
}
14461526

1527+
// ============================================================================
1528+
// TimestampType validation tests
1529+
// ============================================================================
1530+
1531+
#[test]
1532+
fn test_timestamp_valid_precision() {
1533+
// Test all valid precision values 0 through 9
1534+
for precision in 0..=9 {
1535+
let result = TimestampType::with_nullable(true, precision);
1536+
assert!(
1537+
result.is_ok(),
1538+
"precision {} should be valid",
1539+
precision
1540+
);
1541+
let timestamp_type = result.unwrap();
1542+
assert_eq!(timestamp_type.precision(), precision);
1543+
}
1544+
}
1545+
14471546
#[test]
14481547
fn test_timestamp_invalid_precision() {
14491548
// TimestampType::with_nullable should return an error for invalid precision
@@ -1469,3 +1568,79 @@ fn test_timestamp_ltz_invalid_precision() {
14691568
.contains("Timestamp with local time zone precision must be between 0 and 9")
14701569
);
14711570
}
1571+
1572+
// ============================================================================
1573+
// RowType projection tests
1574+
// ============================================================================
1575+
1576+
#[test]
1577+
fn test_row_type_project_valid_indices() {
1578+
// Create a 3-column row type
1579+
let row_type = RowType::with_data_types_and_field_names(
1580+
vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1581+
vec!["id", "name", "age"],
1582+
);
1583+
1584+
// Valid projection by indices: [0, 2]
1585+
let projected = row_type.project(&[0, 2]).unwrap();
1586+
assert_eq!(projected.fields().len(), 2);
1587+
assert_eq!(projected.fields()[0].name, "id");
1588+
assert_eq!(projected.fields()[1].name, "age");
1589+
}
1590+
1591+
#[test]
1592+
fn test_row_type_project_with_field_names_valid() {
1593+
// Create a 3-column row type
1594+
let row_type = RowType::with_data_types_and_field_names(
1595+
vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1596+
vec!["id", "name", "age"],
1597+
);
1598+
1599+
// Valid projection by names: ["id", "name"]
1600+
let projected = row_type
1601+
.project_with_field_names(&["id".to_string(), "name".to_string()])
1602+
.unwrap();
1603+
assert_eq!(projected.fields().len(), 2);
1604+
assert_eq!(projected.fields()[0].name, "id");
1605+
assert_eq!(projected.fields()[1].name, "name");
1606+
}
1607+
1608+
#[test]
1609+
fn test_row_type_project_index_out_of_bounds() {
1610+
// Create a 3-column row type
1611+
let row_type = RowType::with_data_types_and_field_names(
1612+
vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1613+
vec!["id", "name", "age"],
1614+
);
1615+
1616+
// Error: index out of bounds
1617+
let result = row_type.project(&[0, 5]);
1618+
assert!(result.is_err());
1619+
assert!(
1620+
result.unwrap_err().to_string().contains("invalid field position: 5")
1621+
);
1622+
}
1623+
1624+
#[test]
1625+
fn test_row_type_project_with_field_names_nonexistent() {
1626+
// Create a 3-column row type
1627+
let row_type = RowType::with_data_types_and_field_names(
1628+
vec![DataTypes::int(), DataTypes::string(), DataTypes::bigint()],
1629+
vec!["id", "name", "age"],
1630+
);
1631+
1632+
// Error: non-existent field name
1633+
// Note: project_with_field_names filters out non-existent field names using filter_map,
1634+
// so the result will be an empty row if all field names are non-existent
1635+
let projected = row_type
1636+
.project_with_field_names(&["nonexistent".to_string()])
1637+
.unwrap();
1638+
assert_eq!(projected.fields().len(), 0);
1639+
1640+
// Mixed existing and non-existing: only existing fields are projected
1641+
let projected = row_type
1642+
.project_with_field_names(&["id".to_string(), "nonexistent".to_string()])
1643+
.unwrap();
1644+
assert_eq!(projected.fields().len(), 1);
1645+
assert_eq!(projected.fields()[0].name, "id");
1646+
}

0 commit comments

Comments
 (0)