diff --git a/Cargo.lock b/Cargo.lock index 2c3e3f2..7bbf722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1538,9 +1538,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.4" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", @@ -1684,9 +1684,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.60" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -1998,9 +1998,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -2108,9 +2108,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "der" @@ -3318,9 +3318,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3533,9 +3533,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ "cfg-if", "futures-util", @@ -3642,9 +3642,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -5287,9 +5287,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.39" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "once_cell", "ring", @@ -5301,9 +5301,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -6884,9 +6884,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -6897,9 +6897,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ "js-sys", "wasm-bindgen", @@ -6907,9 +6907,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6917,9 +6917,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -6930,9 +6930,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -6973,9 +6973,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/solitaire_core/src/scoring.rs b/solitaire_core/src/scoring.rs index d90dab6..ba19998 100644 --- a/solitaire_core/src/scoring.rs +++ b/solitaire_core/src/scoring.rs @@ -91,6 +91,6 @@ mod tests { fn time_bonus_is_capped_at_i32_max_for_huge_values() { // Very short elapsed time would overflow without the .min() guard. let bonus = compute_time_bonus(1); - assert!(bonus <= i32::MAX, "time bonus must fit in i32"); + assert!(bonus >= 0, "time bonus must be non-negative after u64→i32 cast"); } } diff --git a/solitaire_data/src/progress.rs b/solitaire_data/src/progress.rs index 5475c8b..a91fc14 100644 --- a/solitaire_data/src/progress.rs +++ b/solitaire_data/src/progress.rs @@ -148,8 +148,7 @@ mod tests { #[test] fn add_xp_saturates_on_overflow() { - let mut p = PlayerProgress::default(); - p.total_xp = u64::MAX - 5; + let mut p = PlayerProgress { total_xp: u64::MAX - 5, ..Default::default() }; p.add_xp(100); assert_eq!(p.total_xp, u64::MAX); } diff --git a/solitaire_data/src/settings.rs b/solitaire_data/src/settings.rs index 6491df7..179a7a9 100644 --- a/solitaire_data/src/settings.rs +++ b/solitaire_data/src/settings.rs @@ -207,8 +207,7 @@ mod tests { #[test] fn adjust_sfx_volume_clamps() { - let mut s = Settings::default(); - s.sfx_volume = 0.5; + let mut s = Settings { sfx_volume: 0.5, ..Default::default() }; assert!((s.adjust_sfx_volume(0.3) - 0.8).abs() < 1e-6); assert!((s.adjust_sfx_volume(0.5) - 1.0).abs() < 1e-6); assert!((s.adjust_sfx_volume(-2.0) - 0.0).abs() < 1e-6); @@ -217,8 +216,7 @@ mod tests { #[test] fn adjust_music_volume_clamps() { - let mut s = Settings::default(); - s.music_volume = 0.5; + let mut s = Settings { music_volume: 0.5, ..Default::default() }; assert!((s.adjust_music_volume(0.3) - 0.8).abs() < 1e-6); assert!((s.adjust_music_volume(0.5) - 1.0).abs() < 1e-6); assert!((s.adjust_music_volume(-2.0) - 0.0).abs() < 1e-6); @@ -241,14 +239,10 @@ mod tests { #[test] fn sanitized_clamps_music_volume() { - let mut s = Settings::default(); - s.music_volume = 2.0; - let s = s.sanitized(); + let s = Settings { music_volume: 2.0, ..Default::default() }.sanitized(); assert_eq!(s.music_volume, 1.0); - let mut s2 = Settings::default(); - s2.music_volume = -0.5; - let s2 = s2.sanitized(); + let s2 = Settings { music_volume: -0.5, ..Default::default() }.sanitized(); assert_eq!(s2.music_volume, 0.0); } diff --git a/solitaire_data/src/stats.rs b/solitaire_data/src/stats.rs index 4f661fd..db40a15 100644 --- a/solitaire_data/src/stats.rs +++ b/solitaire_data/src/stats.rs @@ -173,8 +173,7 @@ mod tests { #[test] fn lifetime_score_saturates_at_u64_max() { - let mut s = StatsSnapshot::default(); - s.lifetime_score = u64::MAX - 100; + let mut s = StatsSnapshot { lifetime_score: u64::MAX - 100, ..Default::default() }; s.update_on_win(200, 60, &DrawMode::DrawOne); assert_eq!(s.lifetime_score, u64::MAX, "lifetime_score must saturate, not overflow"); } diff --git a/solitaire_engine/src/animation_plugin.rs b/solitaire_engine/src/animation_plugin.rs index c2ef5ff..9e5048a 100644 --- a/solitaire_engine/src/animation_plugin.rs +++ b/solitaire_engine/src/animation_plugin.rs @@ -775,8 +775,7 @@ mod tests { let mut app = App::new(); app.add_plugins(MinimalPlugins).add_plugins(AnimationPlugin); - let mut fast_settings = Settings::default(); - fast_settings.animation_speed = AnimSpeed::Fast; + let fast_settings = Settings { animation_speed: AnimSpeed::Fast, ..Default::default() }; app.world_mut().send_event(SettingsChangedEvent(fast_settings)); app.update(); diff --git a/solitaire_engine/src/cursor_plugin.rs b/solitaire_engine/src/cursor_plugin.rs index 796fbd2..84c02bd 100644 --- a/solitaire_engine/src/cursor_plugin.rs +++ b/solitaire_engine/src/cursor_plugin.rs @@ -214,7 +214,6 @@ fn point_in_rect(point: Vec2, center: Vec2, size: Vec2) -> bool { #[cfg(test)] mod tests { use super::*; - use solitaire_core::card::{Card, Rank}; #[test] fn point_in_rect_center_is_inside() { diff --git a/solitaire_engine/src/input_plugin.rs b/solitaire_engine/src/input_plugin.rs index 7932b40..d93a128 100644 --- a/solitaire_engine/src/input_plugin.rs +++ b/solitaire_engine/src/input_plugin.rs @@ -988,6 +988,11 @@ pub fn find_hint(game: &GameState) -> Option<(PileType, PileType, usize)> { all_hints(game).into_iter().next() } +// `Vec3` is referenced only via the `DRAG_Z` constant; keep the import silenced +// when the compiler can't see it used. +#[allow(dead_code)] +const _VEC3_REFERENCED: Option = None; + #[cfg(test)] mod tests { use super::*; @@ -1428,7 +1433,7 @@ mod tests { /// window actually opens on the first G press. #[test] fn forfeit_confirm_window_is_positive() { - assert!(FORFEIT_CONFIRM_WINDOW > 0.0, "FORFEIT_CONFIRM_WINDOW must be > 0"); + const { assert!(FORFEIT_CONFIRM_WINDOW > 0.0, "FORFEIT_CONFIRM_WINDOW must be > 0"); } } /// Simulate the first G press: countdown was 0, so it should become @@ -1616,7 +1621,3 @@ mod tests { } } -// `Vec3` is referenced only via the `DRAG_Z` constant; keep the import silenced -// when the compiler can't see it used. -#[allow(dead_code)] -const _VEC3_REFERENCED: Option = None; diff --git a/solitaire_engine/src/stats_plugin.rs b/solitaire_engine/src/stats_plugin.rs index ea797d3..0fdd013 100644 --- a/solitaire_engine/src/stats_plugin.rs +++ b/solitaire_engine/src/stats_plugin.rs @@ -792,7 +792,7 @@ mod tests { .collect(); assert!( - messages.iter().any(|m| *m == "Streak of 3 broken!"), + messages.contains(&"Streak of 3 broken!"), "expected 'Streak of 3 broken!' in toasts, got: {messages:?}" ); } diff --git a/solitaire_engine/src/sync_plugin.rs b/solitaire_engine/src/sync_plugin.rs index 952c089..c4d5369 100644 --- a/solitaire_engine/src/sync_plugin.rs +++ b/solitaire_engine/src/sync_plugin.rs @@ -403,8 +403,7 @@ mod tests { #[test] fn build_payload_clones_stats() { - let mut stats = StatsSnapshot::default(); - stats.games_played = 42; + let stats = StatsSnapshot { games_played: 42, ..Default::default() }; let payload = build_payload(&stats, &[], &PlayerProgress::default()); assert_eq!(payload.stats.games_played, 42); } diff --git a/solitaire_engine/src/weekly_goals_plugin.rs b/solitaire_engine/src/weekly_goals_plugin.rs index b108aa2..adcd9ba 100644 --- a/solitaire_engine/src/weekly_goals_plugin.rs +++ b/solitaire_engine/src/weekly_goals_plugin.rs @@ -158,7 +158,7 @@ mod tests { assert_eq!(p.weekly_goal_progress.get("weekly_5_wins"), Some(&1)); // No-undo + slow win → no_undo goal also ticked, fast goal NOT ticked. assert_eq!(p.weekly_goal_progress.get("weekly_3_no_undo"), Some(&1)); - assert!(p.weekly_goal_progress.get("weekly_3_fast").is_none()); + assert!(!p.weekly_goal_progress.contains_key("weekly_3_fast")); } #[test] @@ -188,7 +188,7 @@ mod tests { app.update(); let p = &app.world().resource::().0; assert_eq!(p.weekly_goal_progress.get("weekly_5_wins"), Some(&1)); - assert!(p.weekly_goal_progress.get("weekly_3_no_undo").is_none()); + assert!(!p.weekly_goal_progress.contains_key("weekly_3_no_undo")); } #[test] diff --git a/solitaire_server/src/middleware.rs b/solitaire_server/src/middleware.rs index e0b2395..31f6bea 100644 --- a/solitaire_server/src/middleware.rs +++ b/solitaire_server/src/middleware.rs @@ -100,6 +100,22 @@ pub fn validate_refresh_token(token: &str, secret: &str) -> Result FromRequestParts for AuthenticatedUser +where + S: Send + Sync, +{ + type Rejection = AppError; + + async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { + parts + .extensions + .get::() + .cloned() + .ok_or(AppError::Unauthorized) + } +} + // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- @@ -221,19 +237,3 @@ mod tests { assert!(result.is_err(), "expired refresh token must be rejected"); } } - -#[axum::async_trait] -impl FromRequestParts for AuthenticatedUser -where - S: Send + Sync, -{ - type Rejection = AppError; - - async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - parts - .extensions - .get::() - .cloned() - .ok_or(AppError::Unauthorized) - } -} diff --git a/solitaire_sync/src/progress.rs b/solitaire_sync/src/progress.rs index 2a15bb5..ef28a53 100644 --- a/solitaire_sync/src/progress.rs +++ b/solitaire_sync/src/progress.rs @@ -201,8 +201,7 @@ mod tests { #[test] fn add_xp_saturates_on_overflow() { - let mut p = PlayerProgress::default(); - p.total_xp = u64::MAX; + let mut p = PlayerProgress { total_xp: u64::MAX, ..Default::default() }; p.add_xp(1); assert_eq!(p.total_xp, u64::MAX); } @@ -230,8 +229,7 @@ mod tests { #[test] fn roll_weekly_goals_clears_progress_for_new_week() { - let mut p = PlayerProgress::default(); - p.weekly_goal_week_iso = Some("2026-W16".to_string()); + let mut p = PlayerProgress { weekly_goal_week_iso: Some("2026-W16".to_string()), ..Default::default() }; p.weekly_goal_progress.insert("weekly_5_wins".to_string(), 3); let rolled = p.roll_weekly_goals_if_new_week("2026-W17"); @@ -242,8 +240,7 @@ mod tests { #[test] fn roll_weekly_goals_is_noop_for_same_week() { - let mut p = PlayerProgress::default(); - p.weekly_goal_week_iso = Some("2026-W17".to_string()); + let mut p = PlayerProgress { weekly_goal_week_iso: Some("2026-W17".to_string()), ..Default::default() }; p.weekly_goal_progress.insert("weekly_5_wins".to_string(), 2); let rolled = p.roll_weekly_goals_if_new_week("2026-W17"); diff --git a/solitaire_sync/src/stats.rs b/solitaire_sync/src/stats.rs index 7585c46..5190c29 100644 --- a/solitaire_sync/src/stats.rs +++ b/solitaire_sync/src/stats.rs @@ -135,17 +135,14 @@ mod tests { #[test] fn record_abandoned_resets_win_streak() { - let mut s = StatsSnapshot::default(); - s.win_streak_current = 5; + let mut s = StatsSnapshot { win_streak_current: 5, ..Default::default() }; s.record_abandoned(); assert_eq!(s.win_streak_current, 0, "abandoned game must break the win streak"); } #[test] fn record_abandoned_preserves_best_streak() { - let mut s = StatsSnapshot::default(); - s.win_streak_best = 7; - s.win_streak_current = 7; + let mut s = StatsSnapshot { win_streak_best: 7, win_streak_current: 7, ..Default::default() }; s.record_abandoned(); assert_eq!(s.win_streak_best, 7, "best streak must not be reduced on abandon"); assert_eq!(s.win_streak_current, 0);