steam-backlog-enforcer/steam_backlog_enforcer/tests
Krzysztof kuhy Rudnicki 8d31e8e9af
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (push) Waiting to run
Cut the daily gaming budget to 6h on a day with no workout
8h is now earned rather than given. A gaming day with a counted workout
logged in screen-locker gets daily_gaming_seconds; a day without gets the
new unearned_gaming_seconds floor. Skipping a workout costs two hours the
same day, instead of nothing until the weekly lock fires.

The fact comes from screen-locker's GET /api/status, which reports only
workout_today -- the hour values live here. Neither repo can drift from the
other about what a workout day is: that flag is derived from
count_day_credits, the same rule the locker enforces with.

Every way of not getting an answer applies the floor, logged at warning with
the URL and the concrete error. Failing open would let the coupling be
defeated by stopping one user service, and a silent 8h would be
indistinguishable from an earned one.

resolve_budget_seconds is called from rules_for rather than at each call
site, because the daemon holds one Config for its whole life while
_budget_view reloads it per HTTP request. Resolved at only one of those and
the web UI would report a budget the daemon was not enforcing -- the exact
silent divergence this codebase is built to prevent.

A budget that rises mid-day now releases the block properly.
_release_after_raise clears blocked_at, which otherwise only roll_over reset
and which would leave the UI reporting "blocked" while gaming worked, and
clears warned_seconds, which is keyed on seconds *remaining* and would
otherwise let the extra two hours arrive with no warnings at all.

Known and accepted (2026-08-29): the budget can DROP mid-day if the resolved
answer changes under a day already in progress, re-pricing time already
spent and cutting off on the next tick with no warning. The module docstring
says so plainly rather than claiming a monotonicity the code does not
implement; the fix, if it ever bites, is a persisted granted_budget_seconds.

Tests get an autouse stub for the lookup. Without it every test that builds
rules makes a real loopback request, and the suite would pass in the morning
and fail in the evening depending on whether the machine's owner had trained.
test_budget_view builds its RULES at import time, before fixtures apply, so
it stubs the resolver explicitly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fcwu78wk17FWMHbbfbyicc
2026-08-29 23:11:27 +02:00
..
__init__.py test: achieve 100% branch coverage across all python_pkg packages 2026-03-21 17:51:36 +01:00
_fake_proc.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
_fake_session.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
_fake_xlib.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
_isolate_playtime.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
_main_helpers.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
_no_subprocess.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
_no_workout_http.py Cut the daily gaming budget to 6h on a day with no workout 2026-08-29 23:11:27 +02:00
_total_block_paths.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
conftest.py Cut the daily gaming budget to 6h on a day with no workout 2026-08-29 23:11:27 +02:00
test_actions_abandon_manual_pick.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_actions_status_payload.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_actions.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_atomic_write.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_attribution_labels.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_budget_games.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_budget_log_tail.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_budget_view.py Cut the daily gaming budget to 6h on a day with no workout 2026-08-29 23:11:27 +02:00
test_cmd_done_part2.py chore: set up as standalone repo 2026-05-28 07:21:29 +02:00
test_cmd_playtime.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_cmd_serve.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
test_config_interactive_setup.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_config.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_controller_input.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_counted_procs.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_desktop_env.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_desktop_session_gating.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_desktop_user_cmd.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_do_enforce.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_enforce_loop_part2.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_enforce_loop_part3.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_enforce_loop.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_enforce_steps.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_enforcer_part2.py feat: add block-gaming command (Stage 4) + guard-lib migration cleanup 2026-07-04 11:45:54 +02:00
test_enforcer.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_engagement_probes.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_engagement_types.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_ensure_debug_port.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_ensure_steam_running.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_game_install_guard.py Re-arm the no-library warning before install_game's early return 2026-08-24 14:04:54 +02:00
test_game_install_part2.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_game_install_part3.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_game_install.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_game_names.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_hltb_build_search_payload.py Bring the HLTB modules under the 250-line cap 2026-08-24 15:23:13 +02:00
test_hltb_confidence_helpers.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_detail_extract_comp100_avg_and_high.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_detail_fetch_leisure_times_save_interval.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_detail_fetch_leisure_times.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_detail_process_game_detail.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_detail.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_fetch_hltb_detail_missing.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_part2.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_part3.py chore: set up as standalone repo 2026-05-28 07:21:29 +02:00
test_hltb_pick_best_hltb_entry_alias_exact_match_beats_spinoff.py Bring the HLTB modules under the 250-line cap 2026-08-24 15:23:13 +02:00
test_hltb_pick_best_hltb_entry.py Bring the HLTB modules under the 250-line cap 2026-08-24 15:23:13 +02:00
test_hltb_search_extract_leisure_hours.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_search_fetch_batch_hltb.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_search_part2.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_search_zero_comp_100_skipped.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb_search.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_hltb.py Bring the HLTB modules under the 250-line cap 2026-08-24 15:23:13 +02:00
test_library_hider_ops.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_library_hider_part2.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_library_hider.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_main_abandon.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_main_dispatch_extra.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
test_main_dispatch.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_done.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
test_main_entrypoint.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_install.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_misc_gating.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_misc.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_part2.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
test_main_part2b.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
test_main_part2c.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_main_pick_cap.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_main_pick_manual.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_picks.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_shared.py Split main.py (1076 lines) into a main/ package 2026-08-23 19:56:34 +02:00
test_main_status.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_mcp_actions.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_mcp_gaming_time.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_mcp.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_pacman.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_pick_completion.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_playtime_attribution.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_playtime_block_part2.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_playtime_block.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_playtime_cutoff.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_playtime_engagement.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_playtime_history.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_playtime_kill.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_playtime_log.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_playtime_part2.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_playtime_policy.py Cut the daily gaming budget to 6h on a day with no workout 2026-08-29 23:11:27 +02:00
test_playtime_procs.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_playtime_proctree.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_playtime_session.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_playtime_tick.py Show which games make up the gaming budget, and bill osu!lazer 2026-08-29 18:32:03 +02:00
test_playtime.py Show the gaming budget in the web UI 2026-08-29 07:17:42 +02:00
test_polls_tracking_backfill_polls_for_finished.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_polls_tracking_part2.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
test_polls_tracking_report_assigned_confidence.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_polls_tracking_report_poll_confidence_chosen_zero_polls_report_poll_confidence_chosen_equals_min.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
test_polls_tracking_report_poll_confidence_chosen_zero_polls.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
test_polls_tracking.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_protondb_fetch_batch.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_protondb_fetch_one.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_protondb.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_scanning_cached_confidence_overlay_avoids_refetch.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_confidence_helpers_pick_next_shortest_candidate_no_echo_whe.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_confidence_helpers.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_part2.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_scanning_part3.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_part4.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_pick_next_game_sequential.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_pick_next_game_unknown_hours.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_scanning_pick_next_game.py Auto-detect completed manual picks before enforcing the pick cap 2026-08-29 14:53:04 +02:00
test_scanning.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_screen_hold.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_serve_guard_lifecycle.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
test_serve_guard.py Make the fd-scan test independent of iterdir() order 2026-08-29 18:57:01 +02:00
test_serve_startup.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
test_stats_cmd_stats_refilter_called_when_ensure_rush_data_re.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_stats_cmd_stats.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_stats_ensure_rush_data.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_stats_print_player_speed_scenario.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_stats_print_worst_example.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_stats_refresh_recently_played_completions.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_stats_sum_hours.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_stats_unplayable_rating_counts_linux_skipped.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_stats.py Bring _stats.py and scanning.py under the 250-line cap 2026-08-24 16:00:54 +02:00
test_steam_api_steam_a_p_i_client.py Split steam_api and _scanning_confidence under the cap 2026-08-24 14:25:43 +02:00
test_steam_api.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_steam_launch_lifecycle.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_steam_restart_guard.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_steam_restart.py Stop the enforcer launching Steam and notifications as root 2026-08-24 13:55:44 +02:00
test_store_blocker_block_via_hosts_install.py Point the iptables tests at the module that now holds the code 2026-08-24 15:07:58 +02:00
test_store_blocker_part2.py Split store_blocker along its one-way call boundaries 2026-08-24 15:06:15 +02:00
test_store_blocker_unblock_store.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_store_blocker.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_total_block_hosts.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_total_block_iptables.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_total_block_lifecycle.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_total_block_purge.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_total_block_remnants.py Split total-block, playtime and MCP by responsibility 2026-08-23 20:52:08 +02:00
test_total_block.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_update_restart_guard.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_web_build.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
test_web_dataset_build_games.py Bring _web_dataset.py under the 250-line cap — cap campaign complete 2026-08-24 16:08:12 +02:00
test_web_dataset_compute_pace_vs_h_l_t_b_both_rush_and_leisure_interpolation_comp.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_web_dataset_compute_pace_vs_h_l_t_b.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_web_dataset_count_complete_since_start.py Grade against 3.14 too, not just run on it 2026-08-29 11:44:25 +02:00
test_web_dataset_dataset_to_payload.py Bring _web_dataset.py under the 250-line cap — cap campaign complete 2026-08-24 16:08:12 +02:00
test_web_dataset.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_web_server.py Make serve re-runnable, and cover it to 100% 2026-08-29 18:52:11 +02:00
test_whitelist_add_pending_exception.py Bring every test file under the 250-line cap 2026-08-24 14:16:08 +02:00
test_whitelist_immutable_flag_is_append_audit_log.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
test_whitelist_immutable_flag_is.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
test_whitelist.py Split _actions, _whitelist, _cmd_done and hltb under the cap 2026-08-24 14:55:57 +02:00
test_workout_budget.py Cut the daily gaming budget to 6h on a day with no workout 2026-08-29 23:11:27 +02:00
test_x_focus.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00
test_x_probe.py Bill gaming time only while the user is actually playing 2026-08-28 19:24:14 +02:00