athena_rs 3.26.4

Hyper performant polyglot Database driver
Documentation
//! `/debug/schema` response contract types.
//!
//! This module owns public diagnostics payload structs returned by the schema
//! debug endpoint, decoupling response contracts from report-building logic.

use chrono::{DateTime, Utc};
use serde::Serialize;

use super::debug_evaluation::LoggingSchemaExpectedTableStatus;

/// Re-export of observed table diagnostics entries.
pub use super::debug_observed_tables::LoggingSchemaObservedTable;
/// Re-export of normalized "still needed" diagnostics entries.
pub use super::debug_still_needed::LoggingSchemaDebugStillNeeded;
/// Re-export of aggregated schema diagnostics summary fields.
pub use super::debug_summary::LoggingSchemaDebugSummary;

/// Full diagnostics payload for `/debug/schema`.
#[derive(Debug, Clone, Serialize)]
pub struct LoggingSchemaDebugReport {
    /// Logging-client name from runtime config.
    pub logging_client: String,
    /// Report generation timestamp.
    pub generated_at: DateTime<Utc>,
    /// Aggregated health summary.
    pub summary: LoggingSchemaDebugSummary,
    /// Per-expected-table status.
    pub expected_tables: Vec<LoggingSchemaExpectedTableStatus>,
    /// Aggregate list of still-missing schema requirements.
    pub still_needed: LoggingSchemaDebugStillNeeded,
    /// All discovered relations (with columns) from the logging client.
    pub observed_tables: Vec<LoggingSchemaObservedTable>,
    /// Required missing table keys (`schema.table`).
    pub missing_required_tables: Vec<String>,
    /// Optional missing table keys (`schema.table`).
    pub missing_optional_tables: Vec<String>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::api::schema::debug_summary_contracts::LoggingSchemaDebugHealthReason;
    use serde_json::Value;

    #[test]
    /// Preserves the `/debug/schema` runtime payload contract during JSON serialization.
    fn debug_report_serialization_exposes_expected_runtime_contract() {
        let report = LoggingSchemaDebugReport {
            logging_client: "athena_logging".to_string(),
            generated_at: Utc::now(),
            summary: LoggingSchemaDebugSummary {
                health: "degraded".to_string(),
                health_reasons: vec![LoggingSchemaDebugHealthReason {
                    code: "missing_required_tables".to_string(),
                    severity: "error".to_string(),
                    count: 1,
                    message: "Missing required logging tables".to_string(),
                }],
                expected_table_count: 3,
                found_table_count: 2,
                required_missing_table_count: 1,
                optional_missing_table_count: 0,
                required_missing_column_count: 1,
                optional_missing_column_count: 0,
                relation_type_mismatch_count: 0,
            },
            expected_tables: vec![LoggingSchemaExpectedTableStatus {
                table_schema: "public".to_string(),
                table_name: "gateway_request_log".to_string(),
                expected_relation_type: "BASE TABLE".to_string(),
                required: true,
                purpose: "gateway request telemetry".to_string(),
                expected_columns: vec!["request_id".to_string(), "status_code".to_string()],
                found: false,
                found_relation_type: None,
                relation_type_matches: false,
                found_columns: Vec::new(),
                missing_columns: vec!["request_id".to_string(), "status_code".to_string()],
                unexpected_columns: Vec::new(),
            }],
            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(),
            },
            observed_tables: vec![LoggingSchemaObservedTable {
                table_schema: "public".to_string(),
                table_name: "gateway_operation_log".to_string(),
                relation_type: "BASE TABLE".to_string(),
                columns: vec!["id".to_string(), "created_at".to_string()],
            }],
            missing_required_tables: vec!["public.gateway_request_log".to_string()],
            missing_optional_tables: Vec::new(),
        };

        let payload = serde_json::to_value(&report).expect("debug report should serialize to JSON");

        assert_eq!(
            payload.get("logging_client").and_then(Value::as_str),
            Some("athena_logging")
        );
        assert_eq!(
            payload
                .get("summary")
                .and_then(|summary| summary.get("health"))
                .and_then(Value::as_str),
            Some("degraded")
        );
        assert_eq!(
            payload
                .get("expected_tables")
                .and_then(Value::as_array)
                .and_then(|rows| rows.first())
                .and_then(|row| row.get("found"))
                .and_then(Value::as_bool),
            Some(false)
        );
        assert_eq!(
            payload
                .get("still_needed")
                .and_then(|still_needed| still_needed.get("required_missing_tables"))
                .and_then(Value::as_array)
                .and_then(|tables| tables.first())
                .and_then(Value::as_str),
            Some("public.gateway_request_log")
        );
        assert_eq!(
            payload
                .get("observed_tables")
                .and_then(Value::as_array)
                .and_then(|rows| rows.first())
                .and_then(|row| row.get("table_name"))
                .and_then(Value::as_str),
            Some("gateway_operation_log")
        );
    }
}