sqrust-rules 0.1.4

Fast SQL linter written in Rust — the Ruff for SQL
Documentation
use sqrust_core::FileContext;
use sqrust_core::Rule;
use sqrust_rules::ambiguous::between_null_boundary::BetweenNullBoundary;

fn check(sql: &str) -> Vec<sqrust_core::Diagnostic> {
    let ctx = FileContext::from_source(sql, "test.sql");
    BetweenNullBoundary.check(&ctx)
}

#[test]
fn rule_name_is_correct() {
    assert_eq!(BetweenNullBoundary.name(), "Ambiguous/BetweenNullBoundary");
}

#[test]
fn between_null_and_value_one_violation() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN NULL AND 10");
    assert_eq!(diags.len(), 1);
    assert_eq!(diags[0].rule, "Ambiguous/BetweenNullBoundary");
}

#[test]
fn between_value_and_value_no_violation() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN 1 AND 10");
    assert!(diags.is_empty());
}

#[test]
fn between_value_and_null_one_violation() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN 1 AND NULL");
    assert_eq!(diags.len(), 1);
}

#[test]
fn between_null_and_null_one_violation() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN NULL AND NULL");
    assert_eq!(diags.len(), 1);
}

#[test]
fn not_between_null_and_value_one_violation() {
    let diags = check("SELECT * FROM t WHERE col NOT BETWEEN NULL AND 10");
    assert_eq!(diags.len(), 1);
}

#[test]
fn not_between_value_and_value_no_violation() {
    let diags = check("SELECT * FROM t WHERE col NOT BETWEEN 1 AND 10");
    assert!(diags.is_empty());
}

#[test]
fn between_zero_and_hundred_no_violation() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN 0 AND 100");
    assert!(diags.is_empty());
}

#[test]
fn between_null_in_case_expression_one_violation() {
    let diags = check(
        "SELECT CASE WHEN col BETWEEN NULL AND 5 THEN 'y' ELSE 'n' END FROM t",
    );
    assert_eq!(diags.len(), 1);
}

#[test]
fn between_null_in_subquery_one_violation() {
    let diags = check(
        "SELECT a FROM (SELECT * FROM t WHERE col BETWEEN NULL AND 10) sub",
    );
    assert_eq!(diags.len(), 1);
}

#[test]
fn between_null_in_cte_one_violation() {
    let diags = check(
        "WITH c AS (SELECT * FROM t WHERE col BETWEEN NULL AND 10) SELECT * FROM c",
    );
    assert_eq!(diags.len(), 1);
}

#[test]
fn parse_error_returns_no_violations() {
    let sql = "SELECTT INVALID GARBAGE @@##";
    let ctx = FileContext::from_source(sql, "test.sql");
    if !ctx.parse_errors.is_empty() {
        let diags = BetweenNullBoundary.check(&ctx);
        assert!(diags.is_empty());
    }
}

#[test]
fn between_column_refs_no_violation() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN a AND b");
    assert!(diags.is_empty());
}

#[test]
fn message_contains_null_or_between() {
    let diags = check("SELECT * FROM t WHERE col BETWEEN NULL AND 10");
    assert_eq!(diags.len(), 1);
    let msg = &diags[0].message;
    assert!(
        msg.contains("NULL") || msg.contains("BETWEEN"),
        "message was: {msg}"
    );
}