#![forbid(unsafe_code)]
use ftui_runtime::schema_compat::{
Compatibility, SchemaKind, check_schema_compat, default_compatibility_matrix,
run_compatibility_matrix,
};
#[test]
fn default_matrix_all_entries_pass() {
let matrix = default_compatibility_matrix();
let results = run_compatibility_matrix(&matrix);
let mut failures = Vec::new();
for (entry, result) in &results {
if result.is_compatible() != entry.expected_compatible {
failures.push(format!(
" {}: writer={}, expected={}, got={:?}",
entry.kind, entry.writer_version, entry.expected_compatible, result.compatibility,
));
}
}
assert!(
failures.is_empty(),
"compatibility matrix failures:\n{}",
failures.join("\n")
);
}
#[test]
fn matrix_covers_all_schema_kinds() {
let matrix = default_compatibility_matrix();
for kind in SchemaKind::ALL {
let count = matrix.iter().filter(|e| e.kind == kind).count();
assert!(
count >= 3,
"{kind} has only {count} matrix entries, expected at least 3 (exact, forward, backward/garbage)"
);
}
}
#[test]
fn all_kinds_exact_match_current() {
for kind in SchemaKind::ALL {
let result = check_schema_compat(kind, kind.current_version());
assert_eq!(
result.compatibility,
Compatibility::Exact,
"{kind}: expected exact match for current version '{}'",
kind.current_version(),
);
}
}
#[test]
fn evidence_v1_forward_compat() {
let result = check_schema_compat(SchemaKind::Evidence, "ftui-evidence-v1");
assert!(result.is_compatible(), "v1 should be readable by v2 reader");
assert!(matches!(
result.compatibility,
Compatibility::Forward {
reader_version: 2,
writer_version: 1
}
));
}
#[test]
fn evidence_v0_forward_compat() {
let result = check_schema_compat(SchemaKind::Evidence, "ftui-evidence-v0");
assert!(result.is_compatible(), "v0 should be readable by v2 reader");
}
#[test]
fn evidence_v3_backward_incompat() {
let result = check_schema_compat(SchemaKind::Evidence, "ftui-evidence-v3");
assert!(
!result.is_compatible(),
"v3 should NOT be readable by v2 reader"
);
}
#[test]
fn evidence_garbage_incompat() {
let result = check_schema_compat(SchemaKind::Evidence, "totally-wrong");
assert!(!result.is_compatible());
assert!(matches!(
result.compatibility,
Compatibility::Unknown { .. }
));
}
#[test]
fn render_trace_v0_forward() {
let result = check_schema_compat(SchemaKind::RenderTrace, "render-trace-v0");
assert!(result.is_compatible());
}
#[test]
fn render_trace_v2_backward() {
let result = check_schema_compat(SchemaKind::RenderTrace, "render-trace-v2");
assert!(!result.is_compatible());
}
#[test]
fn event_trace_v0_forward() {
let result = check_schema_compat(SchemaKind::EventTrace, "event-trace-v0");
assert!(result.is_compatible());
}
#[test]
fn event_trace_v2_backward() {
let result = check_schema_compat(SchemaKind::EventTrace, "event-trace-v2");
assert!(!result.is_compatible());
}
#[test]
fn golden_trace_v0_forward() {
let result = check_schema_compat(SchemaKind::GoldenTrace, "golden-trace-v0");
assert!(result.is_compatible());
}
#[test]
fn golden_trace_v2_backward() {
let result = check_schema_compat(SchemaKind::GoldenTrace, "golden-trace-v2");
assert!(!result.is_compatible());
}
#[test]
fn telemetry_older_major_forward() {
let result = check_schema_compat(SchemaKind::Telemetry, "0.9.0");
assert!(
result.is_compatible(),
"older major version should be forward-compatible"
);
}
#[test]
fn telemetry_newer_major_backward() {
let result = check_schema_compat(SchemaKind::Telemetry, "2.0.0");
assert!(
!result.is_compatible(),
"newer major version should be incompatible"
);
}
#[test]
fn telemetry_same_major_different_minor() {
let result = check_schema_compat(SchemaKind::Telemetry, "1.1.0");
assert_eq!(
result.compatibility,
Compatibility::Exact,
"same major version should be exact match regardless of minor/patch"
);
}
#[test]
fn migration_ir_v0_forward() {
let result = check_schema_compat(SchemaKind::MigrationIr, "migration-ir-v0");
assert!(result.is_compatible());
}
#[test]
fn migration_ir_v2_backward() {
let result = check_schema_compat(SchemaKind::MigrationIr, "migration-ir-v2");
assert!(!result.is_compatible());
}
#[test]
fn no_two_kinds_share_current_version() {
let mut seen = std::collections::HashSet::new();
for kind in SchemaKind::ALL {
let v = kind.current_version();
assert!(
seen.insert(v),
"duplicate current version '{v}' across schema kinds"
);
}
}
#[test]
fn all_kinds_have_nonempty_display() {
for kind in SchemaKind::ALL {
let s = kind.to_string();
assert!(!s.is_empty(), "{kind:?} has empty display");
}
}
#[test]
fn compat_check_tracing_contract() {
let result = check_schema_compat(SchemaKind::Evidence, "ftui-evidence-v99");
assert!(!result.is_compatible());
assert!(matches!(
result.compatibility,
Compatibility::Backward {
reader_version: 2,
writer_version: 99
}
));
}
#[test]
fn incompat_check_increments_counter() {
use ftui_runtime::metrics_registry::{BuiltinCounter, METRICS};
let before = METRICS
.counter(BuiltinCounter::TraceCompatFailuresTotal)
.get();
let _ = check_schema_compat(SchemaKind::Evidence, "ftui-evidence-v99");
let after = METRICS
.counter(BuiltinCounter::TraceCompatFailuresTotal)
.get();
assert!(
after > before,
"trace_compat_failures_total should increment on incompatibility"
);
}
#[test]
fn compat_check_does_not_increment_counter() {
use ftui_runtime::metrics_registry::{BuiltinCounter, METRICS};
let before = METRICS
.counter(BuiltinCounter::TraceCompatFailuresTotal)
.get();
let _ = check_schema_compat(SchemaKind::Evidence, "ftui-evidence-v2");
let after = METRICS
.counter(BuiltinCounter::TraceCompatFailuresTotal)
.get();
assert_eq!(
after, before,
"counter should not increment on compatible check"
);
}
#[test]
fn version_n_replays_on_n_plus_1() {
for kind in SchemaKind::ALL {
if kind == SchemaKind::Telemetry {
let result = check_schema_compat(kind, "0.0.1");
assert!(
result.is_compatible(),
"telemetry: v0 data should replay on v1 reader"
);
} else {
let prefix = match kind {
SchemaKind::Evidence => "ftui-evidence-v",
SchemaKind::RenderTrace => "render-trace-v",
SchemaKind::EventTrace => "event-trace-v",
SchemaKind::GoldenTrace => "golden-trace-v",
SchemaKind::MigrationIr => "migration-ir-v",
SchemaKind::Telemetry => unreachable!(),
};
let current = kind.current_version();
let current_num: u32 = current.strip_prefix(prefix).unwrap().parse().unwrap();
if current_num > 0 {
let older = format!("{prefix}{}", current_num - 1);
let result = check_schema_compat(kind, &older);
assert!(
result.is_compatible(),
"{kind}: v{} data should replay on v{current_num} reader",
current_num - 1,
);
}
}
}
}