#![cfg(feature = "std")]
use oxirouter::RouterState;
use oxirouter::prelude::*;
fn make_two_source_router() -> Router {
let mut r = Router::new();
r.add_source(
DataSource::new("src-a", "https://a.example.org/sparql")
.with_vocabulary("http://example.org/a/"),
);
r.add_source(
DataSource::new("src-b", "https://b.example.org/sparql")
.with_vocabulary("http://example.org/b/"),
);
r
}
#[test]
fn test_state_roundtrip_sources() {
let router = make_two_source_router();
let bytes = router.save_state().expect("save_state failed");
let mut restored = Router::new();
restored.load_state(&bytes).expect("load_state failed");
assert_eq!(restored.source_count(), 2, "expected 2 sources after load");
assert!(
restored.get_source("src-a").is_some(),
"src-a should be present after load"
);
assert!(
restored.get_source("src-b").is_some(),
"src-b should be present after load"
);
}
#[cfg(feature = "ml")]
#[test]
fn test_state_roundtrip_with_ml() {
let router = Router::new();
let bytes = router
.save_state()
.expect("save_state with no model failed");
let mut restored = Router::new();
restored
.load_state(&bytes)
.expect("load_state with no model failed");
}
#[test]
fn test_state_roundtrip_query_log() {
let mut router = make_two_source_router();
let query = Query::parse("SELECT ?s WHERE { ?s ?p ?o }").expect("parse failed");
let ranking = router.route_and_log(&query).expect("route_and_log failed");
if let Some(best) = ranking.best() {
let source_id = best.source_id.clone();
router
.learn_from_outcome(query.predicate_hash(), &source_id, true, 100, 10)
.expect("learn_from_outcome failed");
let bytes = router.save_state().expect("save_state failed");
let mut restored = Router::new();
restored.load_state(&bytes).expect("load_state failed");
let stats = restored.query_log().source_stats(&source_id);
assert!(
stats.is_some(),
"expected source_stats for {source_id} after load"
);
} else {
let bytes = router.save_state().expect("save_state failed");
let mut restored = Router::new();
restored.load_state(&bytes).expect("load_state failed");
}
}
#[test]
fn test_state_magic_mismatch() {
let mut bad_bytes = [0u8; 16];
bad_bytes[0..4].copy_from_slice(b"BAAD");
let err = RouterState::from_bytes(&bad_bytes).expect_err("expected error on bad magic");
assert!(
matches!(err, oxirouter::OxiRouterError::IncompatibleModel { .. }),
"expected IncompatibleModel, got {err:?}"
);
}
#[test]
fn test_state_version_mismatch() {
let router = Router::new();
let mut bytes = router.save_state().expect("save_state failed");
bytes[4..8].copy_from_slice(&99u32.to_le_bytes());
let err = RouterState::from_bytes(&bytes).expect_err("expected error on version mismatch");
assert!(
matches!(err, oxirouter::OxiRouterError::IncompatibleModel { .. }),
"expected IncompatibleModel, got {err:?}"
);
}
#[test]
fn test_state_roundtrip_no_context_change() {
let router = make_two_source_router();
let bytes = router.save_state().expect("save_state failed");
let mut restored = Router::new();
restored.load_state(&bytes).expect("load_state failed");
let query = Query::parse("SELECT ?s WHERE { ?s ?p ?o }").expect("parse failed");
let ranking = restored
.route(&query)
.expect("route failed after load_state");
assert!(
!ranking.is_empty(),
"expected non-empty routing result after restore"
);
}