fix(core): save files store the deal via upstream serializers (schema v6)
Test / test (pull_request) Successful in 36m38s
Test / test (pull_request) Successful in 36m38s
GameState's save serde had the same latent flaw PR #170 removed from replays: v5 persisted seed + saved_moves and re-dealt the board from the seed on load, so any RNG or upstream upgrade that shifts the seed->deal mapping would invalidate every in-progress save (graceful error, but the player loses their game). v6 persists the existing SessionRecording payload (upstream card_game session serde: config + dealt board + instructions) instead; seed stays as presentation metadata only. - v4/v5 files still load through the legacy seed path and are rewritten as v6 on the next save; v6 files load from the recording and replay with per-instruction validation, so tampered or corrupt files surface an error rather than a silently wrong board - saved_moves() removed (dead once serialisation reads recording()) - tests: v6 round-trip via storage, seed-corruption immunity, v5 legacy load, missing-recording rejection, invalid-history rejection Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -477,7 +477,7 @@ mod tests {
|
||||
/// again must reproduce byte-identical JSON. `undo_count` deliberately resets
|
||||
/// to 0 on load because only the forward instruction history is persisted.
|
||||
#[test]
|
||||
fn game_state_v5_mid_game_round_trip() {
|
||||
fn game_state_v6_mid_game_round_trip() {
|
||||
use solitaire_core::KlondikeInstruction;
|
||||
use solitaire_core::game_state::GameState;
|
||||
|
||||
@@ -518,11 +518,16 @@ mod tests {
|
||||
|
||||
save_game_state_to(&path, &gs).expect("save");
|
||||
|
||||
// Verify the file carries the v5 schema marker.
|
||||
// Verify the file carries the v6 schema marker and the recording.
|
||||
let json = fs::read_to_string(&path).expect("read json");
|
||||
let parsed: serde_json::Value = serde_json::from_str(&json).expect("parse saved json");
|
||||
assert_eq!(
|
||||
parsed["schema_version"], 6,
|
||||
"saved file must use schema version 6",
|
||||
);
|
||||
assert!(
|
||||
json.contains("\"schema_version\"") && json.contains('5'),
|
||||
"saved file must use schema version 5",
|
||||
parsed["recording"].is_object(),
|
||||
"saved file must embed the session recording",
|
||||
);
|
||||
|
||||
let loaded =
|
||||
@@ -530,7 +535,7 @@ mod tests {
|
||||
|
||||
// The forward instruction history round-trips, so the reconstructed board
|
||||
// re-serialises to byte-identical JSON.
|
||||
let path_reload = gs_path("v5_mid_game_reload");
|
||||
let path_reload = gs_path("v6_mid_game_reload");
|
||||
let _ = fs::remove_file(&path_reload);
|
||||
save_game_state_to(&path_reload, &loaded).expect("re-save loaded");
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user