athena_rs 3.26.3

Hyper performant polyglot Database driver
//! Column-delta comparison policy for `/debug/schema`.
//!
//! This module owns column-delta orchestration while missing/unexpected
//! policies are delegated to dedicated helper modules.

use std::collections::BTreeSet;

use super::debug_table_comparison_column_delta_contracts::LoggingSchemaColumnDelta;
use super::debug_table_comparison_missing_columns_builder::collect_missing_columns;
use super::debug_table_comparison_unexpected_columns_builder::collect_unexpected_columns;

/// Computes missing and unexpected column deltas.
pub(super) fn compare_column_delta(
    observed_columns: &[String],
    expected_columns: &[String],
) -> LoggingSchemaColumnDelta {
    let observed_set: BTreeSet<String> = observed_columns.iter().cloned().collect();
    let expected_set: BTreeSet<String> = expected_columns.iter().cloned().collect();

    let missing_columns = collect_missing_columns(expected_columns, &observed_set);
    let unexpected_columns = collect_unexpected_columns(observed_columns, &expected_set);

    LoggingSchemaColumnDelta {
        missing_columns,
        unexpected_columns,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Computes both missing and unexpected deltas in deterministic input order.
    #[test]
    fn compare_column_delta_reports_missing_and_unexpected_columns() {
        let observed = vec![
            "request_id".to_string(),
            "status_code".to_string(),
            "extra_col".to_string(),
        ];
        let expected = vec![
            "request_id".to_string(),
            "route_path".to_string(),
            "status_code".to_string(),
        ];

        let delta = compare_column_delta(&observed, &expected);

        assert_eq!(delta.missing_columns, vec!["route_path".to_string()]);
        assert_eq!(delta.unexpected_columns, vec!["extra_col".to_string()]);
    }

    /// Ignores duplicate values while preserving stable first-pass ordering.
    #[test]
    fn compare_column_delta_treats_duplicates_as_present() {
        let observed = vec![
            "request_id".to_string(),
            "request_id".to_string(),
            "status_code".to_string(),
        ];
        let expected = vec![
            "request_id".to_string(),
            "status_code".to_string(),
            "status_code".to_string(),
        ];

        let delta = compare_column_delta(&observed, &expected);

        assert!(delta.missing_columns.is_empty());
        assert!(delta.unexpected_columns.is_empty());
    }
}