cqlite-core 0.11.0

Core engine for CQLite — read Apache Cassandra 5.0 SSTables locally without a cluster
Documentation
//! Unit tests proving the parser abstraction layer works correctly

use cqlite_core::cql::*;
use cqlite_core::schema::TableSchema;

#[test]
#[allow(deprecated)] // Testing deprecated API for backward compatibility
fn test_backward_compatibility_api() {
    // The original function must exist and have the correct signature
    let cql = "CREATE TABLE test (id UUID PRIMARY KEY)";
    let result: nom::IResult<&str, TableSchema> = parse_cql_schema_compat(cql);

    // Whether it succeeds or fails, the important thing is the API exists
    match result {
        Ok((remaining, _schema)) => {
            assert_eq!(remaining, ""); // If it parses, should consume all input
        }
        Err(_) => {
            // Parser may not be fully implemented, but API is correct
        }
    }
}

#[test]
fn test_ast_creation() {
    // Test that we can create AST nodes
    let id = CqlIdentifier::new("test_id");
    assert_eq!(id.name(), "test_id");
    assert!(!id.is_quoted());

    let quoted = CqlIdentifier::quoted("test id with spaces");
    assert_eq!(quoted.name(), "test id with spaces");
    assert!(quoted.is_quoted());

    let table = CqlTable::new("users");
    assert_eq!(table.name().name(), "users");
    assert!(table.keyspace().is_none());

    let qualified_table = CqlTable::with_keyspace("myks", "users");
    assert_eq!(qualified_table.name().name(), "users");
    assert_eq!(qualified_table.keyspace().unwrap().name(), "myks");
}

#[test]
fn test_data_types() {
    // Test primitive types
    assert!(matches!(CqlDataType::Text, CqlDataType::Text));
    assert!(matches!(CqlDataType::Int, CqlDataType::Int));
    assert!(matches!(CqlDataType::Uuid, CqlDataType::Uuid));

    // Test collection types
    let _list_type = CqlDataType::List(Box::new(CqlDataType::Text));
    match _list_type {
        CqlDataType::List(inner) => {
            assert!(matches!(*inner, CqlDataType::Text));
        }
        _ => panic!("Expected List type"),
    }

    let _map_type = CqlDataType::Map(Box::new(CqlDataType::Text), Box::new(CqlDataType::Int));
    match _map_type {
        CqlDataType::Map(key, value) => {
            assert!(matches!(*key, CqlDataType::Text));
            assert!(matches!(*value, CqlDataType::Int));
        }
        _ => panic!("Expected Map type"),
    }
}

#[test]
fn test_visitor_pattern() {
    // Create a simple AST
    let create_table = CqlCreateTable {
        if_not_exists: false,
        table: CqlTable::new("test"),
        columns: vec![
            CqlColumnDef {
                name: CqlIdentifier::new("id"),
                data_type: CqlDataType::Uuid,
                is_static: false,
            },
            CqlColumnDef {
                name: CqlIdentifier::new("name"),
                data_type: CqlDataType::Text,
                is_static: false,
            },
        ],
        primary_key: CqlPrimaryKey {
            partition_key: vec![CqlIdentifier::new("id")],
            clustering_key: vec![],
        },
        options: CqlTableOptions::default(),
    };

    let _statement = CqlStatement::CreateTable(create_table);

    // Test IdentifierCollector
    let mut collector = IdentifierCollector::new();
    collector.visit_statement(&_statement).unwrap();
    let identifiers = collector.into_identifiers();

    assert!(identifiers.len() >= 3); // At least: test, id, name
    assert!(identifiers
        .iter()
        .any(|id: &CqlIdentifier| id.name() == "test"));
    assert!(identifiers
        .iter()
        .any(|id: &CqlIdentifier| id.name() == "id"));
    assert!(identifiers
        .iter()
        .any(|id: &CqlIdentifier| id.name() == "name"));
}

#[test]
fn test_parser_factory() {
    // Test that we can get available backends
    let backends = ParserFactory::get_available_backends();
    assert!(!backends.is_empty());
    assert!(backends.iter().any(|b| b.name == "nom"));

    // Test backend availability
    assert!(ParserFactory::is_backend_available(&ParserBackend::Nom));
    assert!(ParserFactory::is_backend_available(&ParserBackend::Auto));

    // Test use case recommendations
    let hp = ParserFactory::recommend_backend(UseCase::HighPerformance);
    assert_eq!(hp, ParserBackend::Nom);

    let prod = ParserFactory::recommend_backend(UseCase::Production);
    assert_eq!(prod, ParserBackend::Auto);
}

#[test]
fn test_configuration_system() {
    // Default configuration
    let config = ParserConfig::default();
    assert!(config.validate().is_ok());

    // Builder pattern
    let custom = ParserConfig::default()
        .with_backend(ParserBackend::Nom)
        .with_feature(ConfigFeature::Streaming);

    assert_eq!(custom.backend, ParserBackend::Nom);
    assert!(custom.has_feature(&ConfigFeature::Streaming));
    assert!(!custom.has_feature(&ConfigFeature::CodeCompletion));

    // Predefined configurations
    let fast = ParserConfig::fast();
    assert!(fast.has_feature(&ConfigFeature::Streaming));

    let strict = ParserConfig::strict();
    assert!(strict.has_feature(&ConfigFeature::SyntaxHighlighting));
}

#[test]
fn test_schema_builder_visitor() {
    // Create an AST for a table
    let create_table = CqlCreateTable {
        if_not_exists: false,
        table: CqlTable::with_keyspace("myks", "users"),
        columns: vec![
            CqlColumnDef {
                name: CqlIdentifier::new("id"),
                data_type: CqlDataType::Uuid,
                is_static: false,
            },
            CqlColumnDef {
                name: CqlIdentifier::new("email"),
                data_type: CqlDataType::Text,
                is_static: false,
            },
            CqlColumnDef {
                name: CqlIdentifier::new("created"),
                data_type: CqlDataType::Timestamp,
                is_static: false,
            },
        ],
        primary_key: CqlPrimaryKey {
            partition_key: vec![CqlIdentifier::new("id")],
            clustering_key: vec![CqlIdentifier::new("created")],
        },
        options: CqlTableOptions::default(),
    };

    let _statement = CqlStatement::CreateTable(create_table);

    // Use SchemaBuilderVisitor to convert to TableSchema
    let mut builder = SchemaBuilderVisitor::new();
    let schema = builder.visit_statement(&_statement).unwrap();

    // Verify the conversion
    assert_eq!(schema.keyspace, "myks");
    assert_eq!(schema.table, "users");
    assert_eq!(schema.columns.len(), 3);
    assert_eq!(schema.partition_keys.len(), 1);
    assert_eq!(schema.clustering_keys.len(), 1);

    // Check partition key
    assert_eq!(schema.partition_keys[0].name, "id");
    assert_eq!(schema.partition_keys[0].data_type, "uuid");

    // Check clustering key
    assert_eq!(schema.clustering_keys[0].name, "created");
    assert_eq!(schema.clustering_keys[0].data_type, "timestamp");
}

#[test]
fn test_error_handling() {
    use cqlite_core::cql::error::*;

    // Test error creation
    let syntax_err = ParserError::syntax("Expected semicolon", SourcePosition::new(1, 10, 10, 0));
    assert_eq!(syntax_err.category(), &ErrorCategory::Syntax);
    assert_eq!(syntax_err.severity(), &ErrorSeverity::Error);
    assert!(syntax_err.position().is_some());

    let semantic_err = ParserError::semantic("Table already exists");
    assert_eq!(semantic_err.category(), &ErrorCategory::Semantic);
    assert!(semantic_err.position().is_none());

    // Test error recovery
    let backend_err = ParserError::backend("nom", "Parse failed");
    assert!(backend_err.is_recoverable());

    let timeout_err = ParserError::timeout(5000);
    assert!(timeout_err.is_recoverable());
    assert!(!timeout_err.recovery_suggestions().is_empty());
}

#[tokio::test]
async fn test_async_parser_interface() {
    // Create a parser
    let _parser = create_default_parser().unwrap();

    // Test async parsing methods (if parser supports it)
    // Note: The actual implementation may vary
    // Just checking that the interface works
}