tellaro-query-language 2.0.0

A flexible, human-friendly query language for searching and filtering structured data
Documentation
//! Replace mutator tests - matching Python test_replace_mutator.py
//!
//! Tests for the replace string mutator functionality.

use serde_json::json;
use tellaro_query_language::Tql;

// =============================================================================
// Basic Replace Mutator Tests
// =============================================================================

#[test]
fn test_replace_mutator_basic() {
    let tql = Tql::new();
    let data = vec![
        json!({"text": "old value"}),
        json!({"text": "old old value"}),
        json!({"text": "no match"}),
    ];

    // Replace should work with basic strings
    let results = tql
        .query(&data, "text | replace('old', 'new') exists")
        .expect("Query should succeed");
    assert_eq!(results.len(), 3);
}

#[test]
fn test_replace_mutator_positional_parameters() {
    let tql = Tql::new();
    let data = vec![
        json!({"text": "Hello World"}),
        json!({"text": "Hello Universe"}),
        json!({"text": "Goodbye World"}),
    ];

    // Test positional parameters
    let results = tql
        .query(&data, "text | replace('Hello', 'Hi') startswith 'Hi'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 2);
}

#[test]
fn test_replace_mutator_named_parameters() {
    let tql = Tql::new();
    let data = vec![json!({"path": "/home/user/documents"})];

    // Test named parameters
    let results = tql
        .query(&data, "path | replace(find='/', replace='_') exists")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_chained_with_lowercase() {
    let tql = Tql::new();
    let data = vec![
        json!({"email": "JOHN.DOE@EXAMPLE.COM"}),
        json!({"email": "jane.smith@test.org"}),
    ];

    // Chain with lowercase
    let results = tql
        .query(
            &data,
            "email | lowercase | replace('@', '_at_') contains '_at_'",
        )
        .expect("Query should succeed");
    assert_eq!(results.len(), 2);
}

#[test]
fn test_replace_mutator_special_characters_hyphen() {
    let tql = Tql::new();
    let data = vec![json!({"text": "Hello-World"})];

    // Replace hyphen
    let results = tql
        .query(&data, "text | replace('-', ' ') contains 'Hello World'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_special_characters_underscore() {
    let tql = Tql::new();
    let data = vec![json!({"text": "Tab_here"})];

    // Replace underscore
    let results = tql
        .query(&data, "text | replace('_', ' ') = 'Tab here'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_remove_characters() {
    let tql = Tql::new();
    let data = vec![json!({"value": "test"})];

    // Replace with empty string (remove character)
    let results = tql
        .query(&data, "value | replace('t', '') = 'es'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_numeric_values() {
    let tql = Tql::new();
    let data = vec![
        json!({"code": 404}),
        json!({"code": 200}),
        json!({"code": 500}),
    ];

    // Replace in numbers (converted to strings)
    let results = tql
        .query(&data, "code | replace('0', 'X') exists")
        .expect("Query should succeed");
    assert_eq!(results.len(), 3);
}

#[test]
fn test_replace_mutator_case_sensitive() {
    let tql = Tql::new();
    let data = vec![json!({"text": "Error ERROR error"})];

    // Replace is case-sensitive - only first 'Error' should be replaced
    let results = tql
        .query(&data, "text | replace('Error', 'Info') contains 'Info'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_multiple_occurrences() {
    let tql = Tql::new();
    let data = vec![json!({"text": "aaa"})];

    // Replace multiple occurrences
    let results = tql
        .query(&data, "text | replace('a', 'b') = 'bbb'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_no_match() {
    let tql = Tql::new();
    let data = vec![json!({"text": "hello world"})];

    // No match should leave string unchanged
    let results = tql
        .query(&data, "text | replace('xyz', 'abc') = 'hello world'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_on_arrays() {
    let tql = Tql::new();
    let data = vec![json!({"tags": ["error", "critical", "production"]})];

    // Replace in array elements
    let results = tql
        .query(&data, "tags | replace('o', '0') exists")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_with_filter() {
    let tql = Tql::new();
    let data = vec![
        json!({"message": "Error: Connection failed"}),
        json!({"message": "Warning: Low memory"}),
        json!({"message": "Info: Process started"}),
        json!({"message": "Error: Timeout occurred"}),
    ];

    // Replace and filter
    let results = tql
        .query(
            &data,
            "message | replace('Error', 'Critical') startswith 'Critical'",
        )
        .expect("Query should succeed");
    assert_eq!(results.len(), 2);
}

#[test]
fn test_replace_mutator_chained_multiple() {
    let tql = Tql::new();
    let data = vec![json!({"date": "2024-01-15"})];

    // Chain multiple replacements
    let results = tql
        .query(&data, "date | replace('-', '/') = '2024/01/15'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_unicode_content() {
    let tql = Tql::new();
    let data = vec![json!({"text": "hello,world,世界"})];

    // Replace in string with unicode
    let results = tql
        .query(&data, "text | replace(',', ' ') contains 'hello'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_empty_string_input() {
    let tql = Tql::new();
    let data = vec![json!({"value": ""})];

    // Replace on empty string
    let results = tql
        .query(&data, "value | replace('x', 'y') exists")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_with_length() {
    let tql = Tql::new();
    let data = vec![
        json!({"version": "1.2.3"}),
        json!({"version": "2.0.0"}),
        json!({"version": "3.0.0-beta"}),
    ];

    // Replace and check length
    let results = tql
        .query(&data, "version | replace('.', '') | length > 3")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1); // "300-beta" has length > 3
}

#[test]
fn test_replace_mutator_slash_character() {
    let tql = Tql::new();
    let data = vec![json!({"path": "/home/user"})];

    // Replace forward slash
    let results = tql
        .query(&data, "path | replace('/', '_') contains '_home_user'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_dot_character() {
    let tql = Tql::new();
    let data = vec![json!({"ip": "192.168.1.100"})];

    // Replace dot
    let results = tql
        .query(&data, "ip | replace('.', '-') contains '192-168-1-100'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_colon_character() {
    let tql = Tql::new();
    let data = vec![json!({"time": "14:30:45"})];

    // Replace colon
    let results = tql
        .query(&data, "time | replace(':', '-') = '14-30-45'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

// =============================================================================
// Complex Queries with Replace
// =============================================================================

#[test]
fn test_replace_mutator_with_and_condition() {
    let tql = Tql::new();
    let data = vec![
        json!({"name": "Alice", "message": "Error: test"}),
        json!({"name": "Bob", "message": "Info: test"}),
    ];

    // Replace with AND condition
    let results = tql
        .query(
            &data,
            "message | replace('Error', 'Warning') startswith 'Warning' AND name = 'Alice'",
        )
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}

#[test]
fn test_replace_mutator_version_comparison() {
    let tql = Tql::new();
    let data = vec![json!({"version": "1.2.3"}), json!({"version": "2.0.0"})];

    // Replace dots and compare
    let results = tql
        .query(&data, "version | replace('.', '_') = '2_0_0'")
        .expect("Query should succeed");
    assert_eq!(results.len(), 1);
}