//! IPC serialization round-trip tests (D-030 Layer 1: fixture-based). use settled_reach_server::bridge::types::*; use std::fs; #[test] fn observer_snapshot_roundtrip() { let snapshot = ObserverSnapshot { tick: 42, entities: vec![VisibleEntity { entity_id: 1, x: 10.0, y: 20.0, z: 0, kind: EntityKind::Npc, }], }; let bytes = rmp_serde::to_vec_named(&snapshot).expect("serialize"); let decoded: ObserverSnapshot = rmp_serde::from_slice(&bytes).expect("deserialize"); assert_eq!(decoded.tick, 42); assert_eq!(decoded.entities.len(), 1); assert_eq!(decoded.entities[0].entity_id, 1); } #[test] fn player_input_roundtrip() { let input = PlayerInput { tick: 100, action: PlayerAction::MoveNorth, }; let bytes = rmp_serde::to_vec_named(&input).expect("serialize"); let decoded: PlayerInput = rmp_serde::from_slice(&bytes).expect("deserialize"); assert_eq!(decoded.tick, 100); } #[test] fn empty_snapshot_roundtrip() { let snapshot = ObserverSnapshot { tick: 0, entities: vec![], }; let bytes = rmp_serde::to_vec_named(&snapshot).expect("serialize"); let decoded: ObserverSnapshot = rmp_serde::from_slice(&bytes).expect("deserialize"); assert_eq!(decoded.tick, 0); assert!(decoded.entities.is_empty()); } /// All PlayerAction variants must survive MessagePack round-trip (D-030 Layer 1) #[test] fn all_player_action_variants_roundtrip() { let actions = vec![ PlayerAction::MoveNorth, PlayerAction::MoveSouth, PlayerAction::MoveEast, PlayerAction::MoveWest, PlayerAction::MoveNortheast, PlayerAction::MoveNorthwest, PlayerAction::MoveSoutheast, PlayerAction::MoveSouthwest, PlayerAction::Interact, PlayerAction::UsePerceptionMode("thermal".to_string()), PlayerAction::Pause, PlayerAction::Unpause, ]; for action in actions { let input = PlayerInput { tick: 1, action: action.clone(), }; let bytes = rmp_serde::to_vec_named(&input).expect("serialize"); let decoded: PlayerInput = rmp_serde::from_slice(&bytes).expect("deserialize"); assert_eq!(decoded.tick, 1); // Verify the variant survived by re-serializing and comparing bytes let re_bytes = rmp_serde::to_vec_named(&decoded).expect("re-serialize"); assert_eq!(bytes, re_bytes, "round-trip mismatch for action variant"); } } /// All .msgpack fixtures must deserialize without error (guards against corruption in git). /// Snapshot fixtures deserialize as ObserverSnapshot, input_* as PlayerInput, /// input_batch_* as Vec. #[test] fn all_fixtures_deserialize() { let fixture_dir = std::path::Path::new("../client/tests/fixtures/msgpack"); assert!( fixture_dir.exists(), "Fixture directory not found: {}", fixture_dir.display() ); let mut count = 0; for entry in fs::read_dir(fixture_dir).expect("read fixture dir") { let entry = entry.expect("read dir entry"); let path = entry.path(); if path.extension().and_then(|e| e.to_str()) != Some("msgpack") { continue; } let name = path.file_stem().unwrap().to_str().unwrap().to_string(); let bytes = fs::read(&path).unwrap_or_else(|_| panic!("read fixture {}", name)); if name.starts_with("snapshot") { rmp_serde::from_slice::(&bytes) .unwrap_or_else(|e| panic!("deserialize snapshot fixture {}: {}", name, e)); } else if name.starts_with("input_batch") { rmp_serde::from_slice::>(&bytes) .unwrap_or_else(|e| panic!("deserialize batch input fixture {}: {}", name, e)); } else if name.starts_with("input") { rmp_serde::from_slice::(&bytes) .unwrap_or_else(|e| panic!("deserialize input fixture {}: {}", name, e)); } else { panic!("unknown fixture naming convention: {}", name); } count += 1; } assert!(count > 0, "no fixtures found"); eprintln!("Verified {} fixtures", count); } /// All EntityKind variants must survive MessagePack round-trip (D-030 Layer 1) #[test] fn all_entity_kind_variants_roundtrip() { let kinds = vec![ EntityKind::Player, EntityKind::Npc, EntityKind::Object, EntityKind::Terrain, ]; for (i, kind) in kinds.into_iter().enumerate() { let entity = VisibleEntity { entity_id: i as u64, x: 0.0, y: 0.0, z: 0, kind, }; let snapshot = ObserverSnapshot { tick: 0, entities: vec![entity], }; let bytes = rmp_serde::to_vec_named(&snapshot).expect("serialize"); let decoded: ObserverSnapshot = rmp_serde::from_slice(&bytes).expect("deserialize"); let re_bytes = rmp_serde::to_vec_named(&decoded).expect("re-serialize"); assert_eq!( bytes, re_bytes, "round-trip mismatch for EntityKind variant" ); } }