feat(api): ✨ add round-trip serialization tests for city data
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
0d2520a700
commit
ef25e2cf8b
1 changed files with 75 additions and 0 deletions
|
|
@ -1040,3 +1040,78 @@ mod load_items_tests {
|
|||
assert_eq!(cost_of(&defs, "axe"), Some(10));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod save_round_trip_tests {
|
||||
use super::*;
|
||||
use mc_city::{BuildingQueue, CityYields, Queueable, QueueEntry, CITY_CENTER_QUEUE_ID};
|
||||
|
||||
/// Punch-list item 4: a city carrying non-default `building_yields`, a
|
||||
/// non-empty per-building production queue, and both `captured_turn` /
|
||||
/// `last_attacked_turn` set must survive the gdext save path
|
||||
/// (`to_json` → `load_from_json` → `to_json`) byte-identically.
|
||||
///
|
||||
/// Byte-identity is asserted on the *re-serialized* form (json1 == json2)
|
||||
/// rather than on the original input string. `building_yields` is a
|
||||
/// `HashMap`, whose serialization order is only stable when serializing a
|
||||
/// single map instance; a fresh deserialize could reorder a multi-entry
|
||||
/// map. A single `building_yields` entry keeps the comparison deterministic
|
||||
/// while still exercising the non-default-map path. `queues` is a
|
||||
/// `BTreeMap`, so multi-entry queue state is order-stable regardless.
|
||||
#[test]
|
||||
fn city_with_yields_queue_and_capture_turns_round_trips_byte_identical() {
|
||||
let mut city = City::new("city_smithgate");
|
||||
city.city_name = "Smithgate".to_string();
|
||||
city.population = 4;
|
||||
city.food_stored = 12.5;
|
||||
city.production_progress = 7.0;
|
||||
|
||||
// Non-default building_yields (single entry → deterministic serde order).
|
||||
city.register_building_yields(
|
||||
"smithy",
|
||||
CityYields { food: 0.0, production: 3.0, gold: 1.0, culture: 0.0, science: 0.5 },
|
||||
);
|
||||
|
||||
// Non-empty per-building production queue (BTreeMap → order-stable).
|
||||
let mut q = BuildingQueue::new();
|
||||
q.push(QueueEntry {
|
||||
queueable: Queueable::Item { item_id: "bronze_sword".to_string() },
|
||||
production_cost: 40,
|
||||
production_invested: 12,
|
||||
origin: mc_core::ProductionOrigin::default(),
|
||||
});
|
||||
city.queues_mut().insert(CITY_CENTER_QUEUE_ID.to_string(), q);
|
||||
|
||||
// Both occupation/siege timestamps set.
|
||||
city.mark_captured(7);
|
||||
city.mark_attacked(9);
|
||||
|
||||
let slot: Vec<Vec<City>> = vec![vec![city]];
|
||||
|
||||
let json1 = to_json(&slot, 0, 0);
|
||||
assert!(json1.contains("smithy"), "building_yields must serialize");
|
||||
assert!(json1.contains("bronze_sword"), "queue entry must serialize");
|
||||
assert!(json1.contains("\"captured_turn\":7"), "captured_turn must serialize");
|
||||
assert!(json1.contains("\"last_attacked_turn\":9"), "last_attacked_turn must serialize");
|
||||
|
||||
// Restore into a fresh slot via the real load path, then re-serialize.
|
||||
let mut slot2: Vec<Vec<City>> = vec![vec![City::new("placeholder")]];
|
||||
assert!(load_from_json(&mut slot2, 0, 0, &json1), "load_from_json must succeed");
|
||||
let json2 = to_json(&slot2, 0, 0);
|
||||
|
||||
assert_eq!(json1, json2, "city save round-trip must be byte-identical");
|
||||
|
||||
// Field-level proof the restored city kept the four target fields.
|
||||
let restored = at(&slot2, 0, 0).expect("restored city present");
|
||||
assert_eq!(restored.captured_turn, Some(7));
|
||||
assert_eq!(restored.last_attacked_turn, Some(9));
|
||||
assert_eq!(restored.queues().get(CITY_CENTER_QUEUE_ID).map(|q| q.len()), Some(1));
|
||||
assert!(
|
||||
restored.queues().get(CITY_CENTER_QUEUE_ID)
|
||||
.and_then(|q| q.entries().first())
|
||||
.map(|e| e.production_invested == 12)
|
||||
.unwrap_or(false),
|
||||
"queued entry progress must survive"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue