dovecote-sqlx-postgres 0.2.2

PostgreSQL SQLx adapter for Dovecote
Documentation
use super::contracts::{ConstraintContract, IndexContract};
use super::normalization::normalize_sql;
use super::*;
use crate::migration::{MIGRATIONS, SCHEMA_VERSION, current_crate_version, marker_compatibility};

#[test]
fn schema_marker_uses_the_shipped_compatibility_range() {
    let migration = MIGRATIONS
        .iter()
        .find(|migration| migration.version() == SCHEMA_VERSION)
        .expect("the v2 migration is shipped");
    let marker = SchemaMarker {
        schema_version: 2,
        minimum_crate_major: 0,
        minimum_crate_minor: 2,
        minimum_crate_patch: 0,
        rolling_compatible: false,
    };
    assert_eq!(marker_compatibility(&marker), Ok(migration.compatibility()));
    assert!(marker_matches_migration(&marker, *migration).is_ok());
    assert!(migration.compatibility().contains(current_crate_version()));

    let wrong_version = SchemaMarker {
        schema_version: 1,
        ..marker
    };
    assert!(marker_matches_migration(&wrong_version, *migration).is_err());

    let too_new = SchemaMarker {
        minimum_crate_major: 9,
        ..marker
    };
    assert!(marker_matches_migration(&too_new, *migration).is_err());

    let malformed = SchemaMarker {
        minimum_crate_minor: -1,
        ..marker
    };
    assert!(marker_compatibility(&malformed).is_err());
}

#[test]
fn constraint_and_index_contracts_require_their_live_semantics() {
    let constraint = ConstraintInfo {
        table_name: "dovecote_events".to_owned(),
        name: "dovecote_events_pkey".to_owned(),
        kind: "p".to_owned(),
        columns: vec!["row_id".to_owned()],
        referenced_table: None,
        referenced_columns: Vec::new(),
        delete_action: None,
        validated: true,
        deferrable: false,
        deferred: false,
        definition: "PRIMARY KEY (row_id)".to_owned(),
    };
    let expected = ConstraintContract::primary_key(
        "dovecote_events_pkey",
        "dovecote_events",
        &["row_id"],
        &["PRIMARY KEY (row_id)"],
    );
    assert!(constraint.matches(&expected));
    let wrong_relation = ConstraintInfo {
        table_name: "other".to_owned(),
        ..constraint
    };
    assert!(!wrong_relation.matches(&expected));

    let check = ConstraintInfo {
        table_name: "dovecote_schema".to_owned(),
        name: "dovecote_schema_version_supported".to_owned(),
        kind: "c".to_owned(),
        columns: vec!["schema_version".to_owned()],
        referenced_table: None,
        referenced_columns: Vec::new(),
        delete_action: None,
        validated: true,
        deferrable: false,
        deferred: false,
        definition: "CHECK ((schema_version = 1))".to_owned(),
    };
    let expected_check = ConstraintContract::check(
        "dovecote_schema_version_supported",
        "dovecote_schema",
        &["CHECK ((schema_version = 1))"],
    );
    assert!(check.matches(&expected_check));

    let index = IndexInfo {
        table_name: "dovecote_events".to_owned(),
        name: "dovecote_events_tenant_source_event_id".to_owned(),
        access_method: "btree".to_owned(),
        is_unique: true,
        is_valid: true,
        is_ready: true,
        has_predicate: false,
        key_columns: 3,
        total_columns: 3,
        options: vec![0, 0, 0],
        columns: vec![
            "tenant_id".to_owned(),
            "source".to_owned(),
            "event_id".to_owned(),
        ],
        collations: vec!["C".to_owned(), "C".to_owned(), "C".to_owned()],
    };
    let expected_index = IndexContract::new(
        "dovecote_events_tenant_source_event_id",
        "dovecote_events",
        true,
        &["tenant_id", "source", "event_id"],
        Some(&["C", "C", "C"]),
    );
    assert!(index.matches(&expected_index));
    let wrong_order = IndexInfo {
        options: vec![1, 0, 0],
        ..index
    };
    assert!(!wrong_order.matches(&expected_index));
}

#[test]
fn pg17_constraint_renderings_match_the_shipped_contracts() {
    let fixtures = [(
        ConstraintInfo {
            table_name: "dovecote_events".to_owned(),
            name: "dovecote_events_identity_size".to_owned(),
            kind: "c".to_owned(),
            columns: vec!["source".to_owned(), "event_id".to_owned()],
            referenced_table: None,
            referenced_columns: Vec::new(),
            delete_action: None,
            validated: true,
            deferrable: false,
            deferred: false,
            definition:
                "CHECK (((octet_length((source)::text) + octet_length((event_id)::text)) <= 2048))"
                    .to_owned(),
        },
        ConstraintContract::check(
            "dovecote_events_identity_size",
            "dovecote_events",
            &["CHECK (((octet_length((source)) + octet_length((event_id))) <= 2048))"],
        ),
    )];

    for (actual, expected) in fixtures {
        assert!(actual.matches(&expected));
    }
}

#[test]
fn sql_normalization_preserves_boolean_grouping() {
    assert_ne!(
        normalize_sql("CHECK ((left IS NULL OR right IS NULL))"),
        normalize_sql("CHECK (((left IS NULL OR right IS NULL)))")
    );
    assert_ne!(
        normalize_sql("CHECK (((left IS NULL) OR (right IS NULL)))"),
        normalize_sql("CHECK (((left IS NULL) AND (right IS NULL)))")
    );
}

#[test]
fn schema_catalog_name_matching_rejects_unexpected_objects() {
    let expected = [
        "dovecote_events_tenant_source_event_id",
        "dovecote_deliveries_claimable",
    ];
    assert!(is_expected_name(
        "dovecote_events_tenant_source_event_id",
        &expected
    ));
    assert!(!is_expected_name("dovecote_events_probe", &expected));
}