use chrono::{DateTime, Utc};
use serde::Serialize;
use super::debug_evaluation::LoggingSchemaExpectedTableStatus;
pub use super::debug_observed_tables::LoggingSchemaObservedTable;
pub use super::debug_still_needed::LoggingSchemaDebugStillNeeded;
pub use super::debug_summary::LoggingSchemaDebugSummary;
#[derive(Debug, Clone, Serialize)]
pub struct LoggingSchemaDebugReport {
pub logging_client: String,
pub generated_at: DateTime<Utc>,
pub summary: LoggingSchemaDebugSummary,
pub expected_tables: Vec<LoggingSchemaExpectedTableStatus>,
pub still_needed: LoggingSchemaDebugStillNeeded,
pub observed_tables: Vec<LoggingSchemaObservedTable>,
pub missing_required_tables: Vec<String>,
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]
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")
);
}
}