athena_rs 3.26.1

Hyper performant polyglot Database driver
Documentation
//! Debug-summary assembly for `/debug/schema`.
//!
//! This module converts evaluated expected-table diagnostics into the aggregate
//! summary payload consumed by the logging-schema debug report, while staged
//! summary-builder stage-input payload assembly is delegated to
//! `debug_summary_builder_stage_input_assembly`, staged summary-builder payload
//! assembly is delegated to
//! `debug_summary_builder_stage_output_assembly` and final staged handoff is
//! delegated to the stable facade in
//! `debug_summary_from_builder_stage_output_assembly`, which delegates to
//! `debug_summary_builder_stage_output_handoff_assembly`, which delegates
//! contract projection to
//! `debug_summary_builder_stage_output_contract_handoff_assembly`.

use super::debug_evaluation::ExpectedTablesEvaluation;
use super::debug_health_assessment::build_logging_schema_debug_health_assessment;
#[cfg(test)]
use super::debug_health_assessment_contracts::LoggingSchemaDebugHealthAssessment;
use super::debug_still_needed::LoggingSchemaDebugStillNeeded;
use super::debug_summary::LoggingSchemaDebugSummary;
use super::debug_summary_builder_stage_input_assembly::build_debug_summary_builder_stage_input_assembly;
use super::debug_summary_builder_stage_output_assembly::build_debug_summary_builder_stage_output_assembly;
use super::debug_summary_from_builder_stage_output_assembly::build_logging_schema_debug_summary_from_builder_stage_output_assembly;
#[cfg(test)]
use super::debug_summary_metrics::LoggingSchemaDebugSummaryMetrics;
use super::debug_summary_metrics::build_debug_summary_metrics;

/// Builds one aggregate debug-summary payload from evaluated diagnostics input.
pub(super) fn build_logging_schema_debug_summary(
    evaluation: &ExpectedTablesEvaluation,
    still_needed: &LoggingSchemaDebugStillNeeded,
    expected_table_count: usize,
) -> LoggingSchemaDebugSummary {
    let metrics = build_debug_summary_metrics(evaluation, still_needed);
    let assessment = build_logging_schema_debug_health_assessment(&metrics);
    let stage_input =
        build_debug_summary_builder_stage_input_assembly(expected_table_count, metrics, assessment);
    let summary_builder_stage = build_debug_summary_builder_stage_output_assembly(stage_input);

    build_logging_schema_debug_summary_from_builder_stage_output_assembly(summary_builder_stage)
}

#[cfg(test)]
/// Builds one summary payload from metrics/assessment fixtures for builder tests.
fn summary_from_metrics_and_assessment(
    expected_table_count: usize,
    metrics: LoggingSchemaDebugSummaryMetrics,
    assessment: LoggingSchemaDebugHealthAssessment,
) -> LoggingSchemaDebugSummary {
    let stage_input =
        build_debug_summary_builder_stage_input_assembly(expected_table_count, metrics, assessment);
    let summary_builder_stage = build_debug_summary_builder_stage_output_assembly(stage_input);
    build_logging_schema_debug_summary_from_builder_stage_output_assembly(summary_builder_stage)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::api::schema::debug_summary_test_fixtures::build_expected_table_status_for_summary_tests;

    #[test]
    /// Computes found-table count from expected-table evaluation status rows.
    fn summary_counts_found_tables_from_evaluation() {
        let mut evaluation = ExpectedTablesEvaluation::default();
        evaluation.expected_tables = vec![
            build_expected_table_status_for_summary_tests(true),
            build_expected_table_status_for_summary_tests(false),
        ];
        let still_needed = LoggingSchemaDebugStillNeeded {
            required_missing_tables: vec!["public.gateway_request_log".to_string()],
            optional_missing_tables: Vec::new(),
            required_missing_columns: vec!["public.gateway_request_log.request_id".to_string()],
            optional_missing_columns: Vec::new(),
            relation_type_mismatches: Vec::new(),
        };

        let summary = build_logging_schema_debug_summary(&evaluation, &still_needed, 2);

        assert_eq!(summary.found_table_count, 1);
        assert_eq!(summary.expected_table_count, 2);
        assert_eq!(summary.required_missing_table_count, 1);
        assert_eq!(summary.health, "degraded");
        assert_eq!(summary.health_reasons[0].code, "missing_required_tables");
    }

    #[test]
    /// Uses canonical still-needed counts even when evaluation counters diverge.
    fn summary_uses_still_needed_counts_for_missing_columns_and_mismatches() {
        let mut evaluation = ExpectedTablesEvaluation::default();
        evaluation.expected_tables = vec![build_expected_table_status_for_summary_tests(true)];
        evaluation.required_missing_column_count = 999;
        evaluation.optional_missing_column_count = 999;
        evaluation.relation_type_mismatch_count = 999;

        let still_needed = LoggingSchemaDebugStillNeeded {
            required_missing_tables: Vec::new(),
            optional_missing_tables: vec!["public.route_request_log".to_string()],
            required_missing_columns: vec!["public.gateway_request_log.request_id".to_string()],
            optional_missing_columns: vec!["public.route_request_log.route_path".to_string()],
            relation_type_mismatches: vec!["public.route_request_log".to_string()],
        };

        let summary = build_logging_schema_debug_summary(&evaluation, &still_needed, 1);

        assert_eq!(summary.required_missing_column_count, 1);
        assert_eq!(summary.optional_missing_column_count, 1);
        assert_eq!(summary.relation_type_mismatch_count, 1);
        assert_eq!(summary.optional_missing_table_count, 1);
        assert_eq!(
            summary
                .health_reasons
                .iter()
                .map(|reason| reason.code.as_str())
                .collect::<Vec<&str>>(),
            vec![
                "missing_required_columns",
                "relation_type_mismatches",
                "missing_optional_tables",
                "missing_optional_columns",
            ]
        );
    }

    #[test]
    /// Passes summary output assembly through unchanged from composed fixtures.
    fn summary_builder_delegates_final_output_assembly() {
        let metrics = LoggingSchemaDebugSummaryMetrics {
            found_table_count: 5,
            required_missing_table_count: 1,
            optional_missing_table_count: 2,
            required_missing_column_count: 3,
            optional_missing_column_count: 4,
            relation_type_mismatch_count: 6,
        };
        let assessment = LoggingSchemaDebugHealthAssessment {
            health: "degraded".to_string(),
            health_reasons: vec![],
        };

        let summary = summary_from_metrics_and_assessment(9, metrics, assessment);

        assert_eq!(summary.expected_table_count, 9);
        assert_eq!(summary.found_table_count, 5);
        assert_eq!(summary.required_missing_table_count, 1);
        assert_eq!(summary.optional_missing_table_count, 2);
        assert_eq!(summary.required_missing_column_count, 3);
        assert_eq!(summary.optional_missing_column_count, 4);
        assert_eq!(summary.relation_type_mismatch_count, 6);
        assert_eq!(summary.health, "degraded");
    }
}