athena_rs 3.26.3

Hyper performant polyglot Database driver
//! Final payload assembly for `/debug/schema`.
//!
//! This module preserves a stable report-output entrypoint while output-shaping
//! policy is split by concern:
//! - `debug_report_output_evaluation_sort_stage`
//! - `debug_report_output_observed_sort_stage`
//! - `debug_report_output_input_contracts`
//! - `debug_report_output_entry_stage_input_assembly`
//! - `debug_report_output_assembly`
//! - `debug_report_output_entry_stage_output_assembly`
//! - `debug_report_output_from_entry_stage_output_assembly`
//! - `debug_report_output_entry_stage_output_handoff_assembly`

use super::debug_contracts::LoggingSchemaDebugReport;
use super::debug_report_output_entry_stage_input_assembly::build_debug_report_output_entry_stage_input_assembly;
use super::debug_report_output_entry_stage_input_contracts::LoggingSchemaDebugReportOutputEntryStageInput;
use super::debug_report_output_entry_stage_output_assembly::build_debug_report_output_entry_stage_output_assembly;
use super::debug_report_output_evaluation_sort_stage::build_debug_report_output_sorted_evaluation;
use super::debug_report_output_from_entry_stage_output_assembly::build_logging_schema_debug_report_output_from_entry_stage_output_assembly;
use super::debug_report_output_input_contracts::LoggingSchemaDebugReportOutputInput;

/// Builds the final `/debug/schema` report payload from one typed input payload.
pub(super) fn build_logging_schema_debug_report_output_from_input(
    input: LoggingSchemaDebugReportOutputInput,
) -> LoggingSchemaDebugReport {
    let LoggingSchemaDebugReportOutputInput {
        logging_client,
        summary,
        evaluation,
        still_needed,
        observed_map,
    } = input;

    let evaluation = build_debug_report_output_sorted_evaluation(evaluation);
    let stage_input: LoggingSchemaDebugReportOutputEntryStageInput =
        build_debug_report_output_entry_stage_input_assembly(
            logging_client,
            summary,
            evaluation,
            still_needed,
            observed_map,
        );
    let stage = build_debug_report_output_entry_stage_output_assembly(stage_input);

    build_logging_schema_debug_report_output_from_entry_stage_output_assembly(stage)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::api::schema::debug_contracts::LoggingSchemaDebugSummary;
    use crate::api::schema::debug_evaluation::{
        ExpectedTablesEvaluation, LoggingSchemaExpectedTableStatus,
    };
    use crate::api::schema::debug_observed_tables::{
        LoggingSchemaObservedTable, ObservedTablesMap,
    };
    use crate::api::schema::debug_still_needed::LoggingSchemaDebugStillNeeded;

    /// Builds a minimal expected-table status row for output-shaping tests.
    fn expected_table_status(schema: &str, table: &str) -> LoggingSchemaExpectedTableStatus {
        LoggingSchemaExpectedTableStatus {
            table_schema: schema.to_string(),
            table_name: table.to_string(),
            expected_relation_type: "BASE TABLE".to_string(),
            required: true,
            purpose: "test".to_string(),
            expected_columns: vec!["id".to_string()],
            found: true,
            found_relation_type: Some("BASE TABLE".to_string()),
            relation_type_matches: true,
            found_columns: vec!["id".to_string()],
            missing_columns: Vec::new(),
            unexpected_columns: Vec::new(),
        }
    }

    #[test]
    /// Sorts expected-table rows by relation key during final payload assembly.
    fn report_output_sorts_expected_tables_by_relation_key() {
        let mut evaluation = ExpectedTablesEvaluation::default();
        evaluation.expected_tables = vec![
            expected_table_status("public", "zeta"),
            expected_table_status("analytics", "alpha"),
        ];
        let observed_map: ObservedTablesMap = [
            (
                "public.zeta".to_string(),
                LoggingSchemaObservedTable {
                    table_schema: "public".to_string(),
                    table_name: "zeta".to_string(),
                    relation_type: "BASE TABLE".to_string(),
                    columns: vec!["id".to_string()],
                },
            ),
            (
                "analytics.alpha".to_string(),
                LoggingSchemaObservedTable {
                    table_schema: "analytics".to_string(),
                    table_name: "alpha".to_string(),
                    relation_type: "BASE TABLE".to_string(),
                    columns: vec!["id".to_string()],
                },
            ),
        ]
        .into_iter()
        .collect();

        let summary = LoggingSchemaDebugSummary {
            health: "healthy".to_string(),
            health_reasons: vec![],
            expected_table_count: 2,
            found_table_count: 2,
            required_missing_table_count: 0,
            optional_missing_table_count: 0,
            required_missing_column_count: 0,
            optional_missing_column_count: 0,
            relation_type_mismatch_count: 0,
        };

        let report = build_logging_schema_debug_report_output_from_input(
            LoggingSchemaDebugReportOutputInput {
                logging_client: "athena_logging".to_string(),
                summary,
                evaluation,
                still_needed: LoggingSchemaDebugStillNeeded {
                    required_missing_tables: Vec::new(),
                    optional_missing_tables: Vec::new(),
                    required_missing_columns: Vec::new(),
                    optional_missing_columns: Vec::new(),
                    relation_type_mismatches: Vec::new(),
                },
                observed_map,
            },
        );

        assert_eq!(
            report
                .expected_tables
                .iter()
                .map(|row| format!("{}.{}", row.table_schema, row.table_name))
                .collect::<Vec<String>>(),
            vec!["analytics.alpha".to_string(), "public.zeta".to_string()]
        );
        assert!(report.still_needed.required_missing_tables.is_empty());
        assert!(report.still_needed.optional_missing_tables.is_empty());
        assert!(report.still_needed.required_missing_columns.is_empty());
        assert!(report.still_needed.optional_missing_columns.is_empty());
        assert!(report.still_needed.relation_type_mismatches.is_empty());
        assert_eq!(
            report
                .observed_tables
                .iter()
                .map(|row| format!("{}.{}", row.table_schema, row.table_name))
                .collect::<Vec<String>>(),
            vec!["analytics.alpha".to_string(), "public.zeta".to_string()]
        );
        assert_eq!(
            report.missing_required_tables,
            report.still_needed.required_missing_tables
        );
        assert_eq!(
            report.missing_optional_tables,
            report.still_needed.optional_missing_tables
        );
    }
}