test(sync): add unit tests for StatsSnapshot::win_rate and AchievementRecord::unlock

Both public APIs in solitaire_sync had no test coverage:
- win_rate(): None before any game, 100/50/0% cases
- AchievementRecord::locked(), unlock(), idempotency preserving earliest date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-04-27 03:54:49 +00:00
parent 9e9ce2b752
commit d0b650e08b
2 changed files with 83 additions and 0 deletions
+33
View File
@@ -46,3 +46,36 @@ impl AchievementRecord {
self.unlock_date = Some(at);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn locked_creates_an_unlocked_record() {
let r = AchievementRecord::locked("first_win");
assert_eq!(r.id, "first_win");
assert!(!r.unlocked);
assert!(r.unlock_date.is_none());
assert!(!r.reward_granted);
}
#[test]
fn unlock_sets_unlocked_and_stores_timestamp() {
let mut r = AchievementRecord::locked("first_win");
let ts = Utc::now();
r.unlock(ts);
assert!(r.unlocked);
assert_eq!(r.unlock_date, Some(ts));
}
#[test]
fn unlock_is_idempotent_and_preserves_earliest_date() {
let mut r = AchievementRecord::locked("first_win");
let early = DateTime::UNIX_EPOCH;
let later = Utc::now();
r.unlock(early);
r.unlock(later); // should be a no-op
assert_eq!(r.unlock_date, Some(early), "earliest unlock date must be preserved");
}
}
+50
View File
@@ -74,3 +74,53 @@ impl StatsSnapshot {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn win_rate_is_none_before_any_game() {
let s = StatsSnapshot::default();
assert!(s.win_rate().is_none());
}
#[test]
fn win_rate_100_when_all_games_won() {
let s = StatsSnapshot {
games_played: 5,
games_won: 5,
..StatsSnapshot::default()
};
let rate = s.win_rate().expect("should have a rate");
assert!((rate - 100.0).abs() < 0.01, "expected 100.0, got {rate}");
}
#[test]
fn win_rate_50_when_half_won() {
let s = StatsSnapshot {
games_played: 10,
games_won: 5,
..StatsSnapshot::default()
};
let rate = s.win_rate().expect("should have a rate");
assert!((rate - 50.0).abs() < 0.01, "expected 50.0, got {rate}");
}
#[test]
fn win_rate_0_when_no_wins() {
let s = StatsSnapshot {
games_played: 3,
games_won: 0,
..StatsSnapshot::default()
};
let rate = s.win_rate().expect("should have a rate");
assert!((rate - 0.0).abs() < 0.01, "expected 0.0, got {rate}");
}
#[test]
fn fastest_win_seconds_defaults_to_max() {
let s = StatsSnapshot::default();
assert_eq!(s.fastest_win_seconds, u64::MAX);
}
}