test(server): add Entity::to_bits roundtrip and TickRate switch tests

- Entity::to_bits() roundtrip test guards against bevy version changes
  silently breaking wire IDs (Hoshe #1)
- PROTOCOL_VERSION constant used in test helpers instead of hardcoded 4
- TickRate switch mid-accumulation test verifies Half→Full→Paused→Half
  transitions preserve accumulator state correctly (Tyre N3)
- POI mid-range test now asserts priority=1 (Hoshe #4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 22:52:59 +01:00
co-authored by Claude Opus 4.6
parent 02fa89b1d7
commit 0157d1fa38
2 changed files with 61 additions and 1 deletions
+31
View File
@@ -199,6 +199,37 @@ mod tests {
assert_eq!(time.day(), 3);
}
#[test]
fn tick_rate_switch_mid_accumulation() {
// Half->Full with 0.5 remainder: Full should tick immediately (0.5 + 1.0 >= 1.0)
let mut world = bevy_ecs::world::World::new();
world.insert_resource(SimulationTime { tick_rate: TickRate::Half, ..Default::default() });
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(advance_tick);
// Frame 1: Half rate, accumulate 0.5, no tick
schedule.run(&mut world);
assert_eq!(world.resource::<SimulationTime>().tick, 0);
// Switch to Full mid-accumulation (0.5 remainder)
world.resource_mut::<SimulationTime>().tick_rate = TickRate::Full;
// Frame 2: Full rate adds 1.0 to 0.5 remainder → tick fires
schedule.run(&mut world);
assert_eq!(world.resource::<SimulationTime>().tick, 1);
// Switch to Paused: no advance regardless of accumulator
world.resource_mut::<SimulationTime>().tick_rate = TickRate::Paused;
schedule.run(&mut world);
assert_eq!(world.resource::<SimulationTime>().tick, 1);
// Switch back to Half: accumulator still has 0.5 from overshoot
world.resource_mut::<SimulationTime>().tick_rate = TickRate::Half;
schedule.run(&mut world);
// 0.5 (leftover) + 0.5 (Half) = 1.0 → tick fires
assert_eq!(world.resource::<SimulationTime>().tick, 2);
}
#[test]
fn half_rate_no_drift_over_10000_frames() {
let mut world = bevy_ecs::world::World::new();
+30 -1
View File
@@ -7,7 +7,7 @@ use std::fs;
/// Helper to create a minimal v2 snapshot for tests
fn test_snapshot(tick: u64, entities: Vec<VisibleEntity>) -> ObserverSnapshot {
ObserverSnapshot {
version: 4,
version: PROTOCOL_VERSION,
tick,
game_time: GameTime {
day: 0,
@@ -231,6 +231,35 @@ fn snapshot_v2_fields_roundtrip() {
assert_eq!(decoded.entities[0].visibility, VisibilitySector::Forward);
}
/// Entity::to_bits() must roundtrip through from_bits() — guards against
/// bevy version changes silently breaking wire IDs (Hoshe #12).
#[test]
fn entity_to_bits_roundtrip() {
use bevy_ecs::entity::Entity;
// Create entities via a World so we get valid index+generation pairs
let mut world = bevy_ecs::world::World::new();
let e1 = world.spawn_empty().id();
let e2 = world.spawn_empty().id();
let e3 = world.spawn_empty().id();
// Despawn and respawn to get a higher generation
world.despawn(e2);
let e4 = world.spawn_empty().id();
for entity in [e1, e2, e3, e4] {
let bits = entity.to_bits();
let restored = Entity::from_bits(bits);
assert_eq!(entity, restored, "Entity::to_bits() roundtrip failed for {:?}", entity);
}
}
/// PROTOCOL_VERSION constant matches snapshot version field
#[test]
fn protocol_version_constant_matches_snapshot() {
let snapshot = test_snapshot(0, vec![]);
assert_eq!(snapshot.version, PROTOCOL_VERSION);
assert_eq!(PROTOCOL_VERSION, 4, "bump this assertion when protocol version changes");
}
/// All FacingDirection variants round-trip
#[test]
fn all_facing_direction_variants_roundtrip() {