From e020e84a3aa37c3f175ab7d1085a6d9232f0cdd8 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 8 May 2026 14:54:13 +0200 Subject: [PATCH] steam_backlog_enforcer: retry install after library hide restart --- steam_backlog_enforcer/_cmd_done.py | 20 +++++++ .../tests/test_main_part2.py | 58 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/steam_backlog_enforcer/_cmd_done.py b/steam_backlog_enforcer/_cmd_done.py index 4631865..75e55e8 100644 --- a/steam_backlog_enforcer/_cmd_done.py +++ b/steam_backlog_enforcer/_cmd_done.py @@ -2,6 +2,8 @@ from __future__ import annotations +import logging + from python_pkg.steam_backlog_enforcer._enforce_loop import get_all_owned_app_ids from python_pkg.steam_backlog_enforcer.config import Config, State, load_snapshot from python_pkg.steam_backlog_enforcer.enforcer import ( @@ -32,6 +34,7 @@ from python_pkg.steam_backlog_enforcer.scanning import ( from python_pkg.steam_backlog_enforcer.steam_api import GameInfo, SteamAPIClient _REASSIGN_REFRESH_LIMIT = 50 +logger = logging.getLogger(__name__) def _backfill_polls_for_finished( @@ -308,6 +311,23 @@ def _finalize_completion( if hidden > 0: _echo(f"\n Library: hid {hidden} games") + if not is_game_installed(state.current_app_id): + logger.info( + "Assigned game still missing after library reconciliation; " + "re-triggering install" + ) + _echo( + "\n Assigned game still missing after library reconciliation; " + "re-triggering install..." + ) + _echo(f"\n Auto-installing {state.current_game_name}...") + install_game( + state.current_app_id, + state.current_game_name, + config.steam_id, + use_steam_protocol=True, + ) + send_notification( "Game Complete!", f"Finished {game_name}! Now playing: {state.current_game_name}", diff --git a/steam_backlog_enforcer/tests/test_main_part2.py b/steam_backlog_enforcer/tests/test_main_part2.py index a91c03c..f6b4b67 100644 --- a/steam_backlog_enforcer/tests/test_main_part2.py +++ b/steam_backlog_enforcer/tests/test_main_part2.py @@ -185,6 +185,64 @@ class TestFinalizeCompletion: assert seen[3] == 18.81 mock_fetch_hltb.assert_called_once_with([(3, "Lacuna")]) + def test_retriggers_install_after_library_hide_if_still_missing(self) -> None: + """Re-trigger install after hide step in case Steam restart drops it.""" + config = Config(steam_id="sid") + state = State(current_app_id=1, current_game_name="DoneGame") + snap = [_snap(2, "Next", 10, 0, 5.0)] + + def set_next( + _games: object, + s: State, + _c: object, + ) -> None: + s.current_app_id = 2 + s.current_game_name = "Next" + + with ( + patch(f"{CMD_DONE_PKG}._echo"), + patch(f"{CMD_DONE_PKG}.load_snapshot", return_value=snap), + patch(f"{CMD_DONE_PKG}.pick_next_game", side_effect=set_next), + patch(f"{CMD_DONE_PKG}.get_all_owned_app_ids", return_value=[1, 2]), + patch(f"{CMD_DONE_PKG}.hide_other_games", return_value=1), + patch(f"{CMD_DONE_PKG}.is_game_installed", return_value=False), + patch(f"{CMD_DONE_PKG}.install_game") as mock_install, + patch(f"{CMD_DONE_PKG}.send_notification"), + patch.object(State, "save"), + ): + _finalize_completion(config, state, "DoneGame", 1) + + mock_install.assert_called_once_with(2, "Next", "sid", use_steam_protocol=True) + + def test_skips_install_retry_when_assigned_game_already_installed(self) -> None: + """Do not re-trigger install when assigned game is already present.""" + config = Config(steam_id="sid") + state = State(current_app_id=1, current_game_name="DoneGame") + snap = [_snap(2, "Next", 10, 0, 5.0)] + + def set_next( + _games: object, + s: State, + _c: object, + ) -> None: + s.current_app_id = 2 + s.current_game_name = "Next" + + with ( + patch(f"{CMD_DONE_PKG}._echo"), + patch(f"{CMD_DONE_PKG}.load_snapshot", return_value=snap), + patch(f"{CMD_DONE_PKG}.pick_next_game", side_effect=set_next), + patch(f"{CMD_DONE_PKG}.get_all_owned_app_ids", return_value=[1, 2]), + patch(f"{CMD_DONE_PKG}.hide_other_games", return_value=1), + patch(f"{CMD_DONE_PKG}.is_game_installed", return_value=True), + patch(f"{CMD_DONE_PKG}.install_game") as mock_install, + patch(f"{CMD_DONE_PKG}.send_notification"), + patch.object(State, "save"), + ): + _finalize_completion(config, state, "DoneGame", 1) + + mock_install.assert_not_called() + class TestEnforceOnDone: """Tests for _enforce_on_done."""