radixdb-core 1.1.0

Canonical value, identity, and shared contracts for RadixDB
Documentation
use chrono::Datelike;
use radixdb_core::*;

/// Integration test: Create a schema, build rows, validate
#[test]
fn test_schema_row_integration() {
    // Create a schema
    let schema = SchemaBuilder::new("users")
        .add_primary_key("id", DataType::Integer)
        .add("name", DataType::Text)
        .add_nullable("email", DataType::Text)
        .add("active", DataType::Boolean)
        .build();

    // Create a valid row
    let row = Row::from_values(vec![
        Value::integer(1),
        Value::text("Alice"),
        Value::text("alice@example.com"),
        Value::boolean(true),
    ]);

    // Validate the row
    assert!(row.validate(&schema).is_ok());

    // Create a row with NULL in nullable column
    let row = Row::from_values(vec![
        Value::integer(2),
        Value::text("Bob"),
        Value::null(DataType::Text),
        Value::boolean(false),
    ]);
    assert!(row.validate(&schema).is_ok());

    // Create a row with NULL in non-nullable column (should fail)
    let row = Row::from_values(vec![
        Value::integer(3),
        Value::null(DataType::Text), // name is not nullable
        Value::null(DataType::Text),
        Value::boolean(true),
    ]);
    assert!(row.validate(&schema).is_err());
}

/// Integration test: Value comparisons across types
#[test]
fn test_value_comparison_integration() {
    // Same type comparisons
    assert!(Value::integer(1).compare(&Value::integer(2)).unwrap() == std::cmp::Ordering::Less);
    assert!(Value::text("a").compare(&Value::text("b")).unwrap() == std::cmp::Ordering::Less);

    // Cross-type numeric comparisons
    assert!(Value::integer(1).compare(&Value::float(1.5)).unwrap() == std::cmp::Ordering::Less);
    assert!(Value::float(2.5).compare(&Value::integer(2)).unwrap() == std::cmp::Ordering::Greater);

    // NULL comparisons
    assert!(Value::null(DataType::Integer)
        .compare(&Value::integer(0))
        .is_err());
}

/// Integration test: Type conversions
#[test]
fn test_value_conversion_integration() {
    // Integer conversions
    let v = Value::integer(42);
    assert_eq!(v.as_int64(), Some(42));
    assert_eq!(v.as_float64(), Some(42.0));
    assert_eq!(v.as_string(), Some("42".to_string()));
    assert_eq!(v.as_boolean(), Some(true));

    // Float conversions
    let v = Value::float(3.5);
    assert_eq!(v.as_int64(), Some(3)); // truncates
    assert_eq!(v.as_float64(), Some(3.5));

    // Boolean conversions
    let v = Value::boolean(true);
    assert_eq!(v.as_int64(), Some(1));
    assert_eq!(v.as_float64(), Some(1.0));
    assert_eq!(v.as_string(), Some("true".to_string()));

    // String parsing
    let v = Value::text("42");
    assert_eq!(v.as_int64(), Some(42));
    assert_eq!(v.as_float64(), Some(42.0));

    // Timestamp parsing
    let v = Value::text("2024-01-15T10:30:00Z");
    assert!(v.as_timestamp().is_some());
}

/// Integration test: Timestamp handling
#[test]
fn test_timestamp_integration() {
    use chrono::TimeZone;

    // Parse various formats
    let ts1 = parse_timestamp("2024-01-15T10:30:00Z").unwrap();
    let ts2 = parse_timestamp("2024-01-15 10:30:00").unwrap();
    let ts3 = parse_timestamp("2024-01-15").unwrap();

    assert_eq!(ts1.year(), 2024);
    assert_eq!(ts2.year(), 2024);
    assert_eq!(ts3.year(), 2024);

    // Create timestamp value
    let v = Value::timestamp(ts1);
    assert_eq!(v.data_type(), DataType::Timestamp);
    assert!(v.as_timestamp().is_some());

    // Timestamp comparison
    let v1 = Value::timestamp(ts1);
    let v2 = Value::timestamp(chrono::Utc.with_ymd_and_hms(2024, 1, 15, 11, 0, 0).unwrap());
    assert!(v1.compare(&v2).unwrap() == std::cmp::Ordering::Less);
}

/// Integration test: Error handling
#[test]
fn test_error_integration() {
    // Constraint errors
    let err = Error::not_null_constraint("email");
    assert!(err.is_constraint_violation());
    assert!(!err.is_not_found());

    // Not found errors
    let err = Error::TableNotFound(String::new());
    assert!(err.is_not_found());
    assert!(!err.is_constraint_violation());

    // Transaction errors
    let err = Error::TransactionAborted;
    assert!(err.is_transaction_error());
}

/// Integration test: Schema operations
#[test]
fn test_schema_operations_integration() {
    let mut schema = SchemaBuilder::new("products")
        .add_primary_key("id", DataType::Integer)
        .add("name", DataType::Text)
        .build();

    // Add column
    schema
        .add_column(SchemaColumn::nullable(2, "description", DataType::Text))
        .unwrap();
    assert_eq!(schema.column_count(), 3);

    // Rename column
    schema.rename_column("name", "product_name").unwrap();
    assert!(schema.has_column("product_name"));
    assert!(!schema.has_column("name"));

    // Modify column
    schema
        .modify_column("description", None, Some(false))
        .unwrap();
    assert!(!schema.get_column_by_name("description").unwrap().nullable);

    // Remove column
    schema.remove_column("description").unwrap();
    assert_eq!(schema.column_count(), 2);
}

/// Integration test: Row creation
#[test]
fn test_row_creation_integration() {
    // Empty row
    let empty = Row::new();
    assert!(empty.is_empty());

    // Row with values using from_values
    let row = Row::from_values(vec![
        Value::integer(1),
        Value::text("hello"),
        Value::boolean(true),
        Value::float(3.5),
    ]);
    assert_eq!(row.len(), 4);
    assert_eq!(row[0], Value::integer(1));
    assert_eq!(row[1], Value::text("hello"));
    assert_eq!(row[2], Value::boolean(true));
    assert_eq!(row[3], Value::float(3.5));
}