Skip to content

Replace brittle waits with deterministic polling - #845

Merged
ikostan merged 45 commits into
mainfrom
code-audits-asynchronous-refactoring
Jul 26, 2026
Merged

ikostan merged 45 commits into
mainfrom
code-audits-asynchronous-refactoring

Conversation

@ikostan

@ikostan ikostan commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Refactor test infrastructure for improved reliability:

  • Split fixture setup: session-scoped browser launch + function-scoped page contexts for better isolation and startup overhead reduction
  • Replace all page.wait_for_timeout() calls with deterministic page.wait_for_function() checks or expect() assertions
  • Add wait_for_console_log() helper to poll for matching console logs with predicates instead of fixed waits
  • Improve Godot initialization check: use explicit '=== true' instead of loose truthy check
  • Use expect() API for DOM element visibility assertions (canvas, buttons)
  • Add type hints to function parameters and imports (Any, Callable, Generator)
  • Improve shell script: better signal handling, robust server startup checks, git safety config
  • Remove verbose/redundant comments to improve readability

These changes eliminate timing-dependent flakiness common in browser automation by making assertions wait for actual state changes rather than arbitrary durations.


name: Default Pull Request Template
about: Suggesting changes to SkyLockAssault
title: ''
labels: ''
assignees: ''

PR #845 Summary: Replace brittle waits with deterministic polling

Repository: ikostan/SkyLockAssault
Author: @ikostan
Branch: code-audits-asynchronous-refactoringmain
Linked Issue: #772 – [TASK] Code Audits & Asynchronous Refactoring
Milestone: Milestone 22 – Optimize Test Suite Runtime & Fix Loading Screen
Labels: enhancement, testing, refactoring, python, QA

Purpose

Eliminate timing-dependent flakiness in the Playwright browser E2E test suite by replacing all fixed-duration waits (page.wait_for_timeout(), arbitrary sleeps) with deterministic, state-based synchronization. The changes make tests wait for actual engine readiness, DOM visibility, or console-log events instead of relying on brittle timeouts.

Core Improvements

1. Deterministic Waiting & Assertions

  • Replaced every page.wait_for_timeout() with:
    • page.wait_for_function() for Godot initialization (window.godotInitialized === true) and DOM style/display checks
    • Playwright expect(locator).to_be_visible() for canvas, buttons, and overlays
  • Introduced reusable wait_for_console_log(logs, predicate, start_idx, page) helper that polls captured console messages with a predicate until the condition is met or a timeout occurs
  • Standardized console-log assertions (lower-cased matching, structured waiting) across volume, reset, audio, difficulty, back-navigation, and navigation tests
  • Strict equality check for Godot readiness (=== true instead of truthy)

2. Shared Test Utilities & Fixtures

  • Extracted common helpers and timeout constants into new module tests/test_utils.py
  • Centralized DEFAULT_TIMEOUT / TEST_TIMEOUT (environment-configurable)
  • Restructured tests/conftest.py:
    • Session-scoped browser_instance fixture (single Chromium launch with GPU/WebGL flags)
    • Function-scoped isolated BrowserContext + Page per test
    • Optional HAR recording support via marker
    • Full type annotations (Browser, BrowserContext, Page, Generator, etc.)

3. Test Coverage Stabilization

Affected test files (all converted to deterministic waits):

  • tests/audio_flow_test.py
  • tests/volume_sliders_mutes_test.py
  • tests/reset_audio_flow_test.py
  • tests/difficulty_flow_test.py
  • tests/back_flow_test.py
  • tests/navigation_to_audio_test.py
  • tests/load_main_menu_test.py
  • tests/no_error_logs_test.py
  • tests/validate_clean_load_test.py

Improvements include:

  • Explicit waiting for callback availability (window.xxxPressed)
  • Verification of menu transitions via getComputedStyle(...).display
  • Persistence checks after back-navigation and slider changes
  • Stricter “no unexpected errors” verification with clearer failure diagnostics (screenshots + captured logs)

4. Browser Test Runner Hardening (workspace/run_browser_tests.sh)

  • Added git config --global --add safe.directory to avoid “dubious ownership” errors in containers
  • Simplified and made robust the git restore of export_presets.cfg and globals.gd
  • Signal-aware cleanup (trap on EXIT/INT/TERM) for the background HTTP server
  • Increased server readiness polling frequency and timeout with clearer failure messages
  • Improved pytest invocation formatting and report-generation comments
  • Removed manual kill of server PID in favor of trap-based cleanup

5. Code Quality & Maintainability

  • Added comprehensive type hints (Any, Callable, Generator, Dict, List, Optional)
  • Removed verbose/redundant comments and outdated docstrings
  • Normalized imports and formatting (multiple Black + isort passes)
  • Extracted duplicated helpers (e.g. has_save_log) into the shared utility module

Benefits

  • Dramatically reduced flakiness caused by race conditions and variable load times
  • Faster overall suite runtime (shared browser launch + tighter, condition-driven waits)
  • Better isolation between tests while keeping startup overhead low
  • Clearer failure diagnostics and more maintainable test code
  • Improved CI reliability for browser-based E2E runs

AI / Bot Assistance (for reference)

  • @sourcery-ai – PR summary, Reviewer’s Guide, and code-review feedback
  • @coderabbitai – PR summary, walkthrough, multiple review rounds, and co-authored commits on the runner script
  • @deepsource-io – Automated code review + PR Report Card
  • @deepsource-autofix – Multiple automated Black/isort formatting commits

Status Notes

The PR fully addresses the objectives of issue #772 regarding wait refactoring, fixture lifecycle optimization, and runner robustness.


Related Issue

Closes #ISSUE_NUMBER (if applicable)

Changes

  • List key changes here (e.g., "Updated Jump.gd to use Godot 4.4's new Tween
    system")
  • Any breaking changes? (e.g., "Deprecated old signal; migrate to new one")

Testing

  • Ran the game in Godot v4.5 editor—describe what you tested (e.g., "Jump
    works on Win10 with 60 FPS")
  • Any new unit tests added? (Link to test scene if yes)
  • Screenshots/GIFs if UI-related: (Attach below)

Checklist

  • Code follows Godot style guide (e.g., snake_case for variables)
  • No console errors in editor/output
  • Ready for review!

Additional Notes

Anything else? (e.g., "Tested on Win10 64-bit; needs Linux validation")

Summary by Sourcery

Refactor browser E2E test suite to use deterministic state-based waits instead of fixed timeouts for Godot initialization, UI visibility, and log-driven flows, improving reliability and reducing flakiness.

Enhancements:

  • Introduce a reusable wait_for_console_log helper across tests to poll for specific console output instead of relying on arbitrary delays.
  • Update all Playwright tests to assert DOM visibility and state using expect() and wait_for_function calls with explicit conditions, including strict Godot initialization checks.
  • Restructure pytest fixtures to share a session-scoped Chromium browser and per-test contexts, reducing startup overhead while preserving isolation.
  • Simplify and tighten test assertions by removing redundant manual log scanning and unnecessary comments.
  • Improve the browser test runner script with safer git restore behavior, robust server startup polling, signal-aware cleanup, and clearer reporting output.

Build:

  • Refine run_browser_tests.sh to better manage Godot export, server lifecycle, and Playwright execution under CI-friendly settings.

Tests:

  • Stabilize audio, volume, reset, difficulty, navigation, and back-flow tests by replacing brittle wait_for_timeout usage with deterministic UI and console-log checks.
  • Ensure load and error-free startup tests validate canvas visibility and Godot readiness via explicit expect and wait_for_function conditions.

Summary by CodeRabbit

  • Tests
    • Improved browser-based E2E stability across main-menu, navigation-to-audio, back-navigation, audio reset, volume sliders/mutes, and difficulty flows by replacing fixed waits with deterministic engine/UI readiness checks.
    • Standardized log-driven synchronization using shared helpers, with stricter “no unexpected errors” verification and clearer failure diagnostics (screenshots plus captured console logs/artifacts).
    • Centralized environment-configurable timeouts for consistent waiting behavior.
  • Chores
    • Added shared E2E test utilities and updated fixtures to reuse a single headless browser per run while keeping isolated contexts per test.
    • Hardened the browser test runner’s server lifecycle management and repository cleanup/restore behavior.

AI / Bot Contributors

  • @sourcery-ai
    Generated the PR summary and Reviewer's Guide. Performed code review identifying duplication of wait_for_console_log, suggesting extraction to a shared utility, recommending Playwright locator assertions over raw JS wait_for_function checks, and noting potential tracing cleanup for session-scoped browser fixtures.

  • @coderabbitai
    Generated the PR summary and detailed walkthrough. Conducted multiple rounds of code review with actionable suggestions (timeout centralization, helper extraction, assertion improvements, shell-script hardening). Co-authored commits updating workspace/run_browser_tests.sh.

  • @deepsource-io
    Performed automated code review, published a PR Report Card (Security / Reliability / Complexity / Hygiene), and provided analysis status for Python and JavaScript.

  • @deepsource-autofix
    Authored multiple automated style/format commits (style: format code with Black and isort) to enforce formatting consistency across test files.

Human Contributor

  • @ikostan
    Primary author of the PR. Implemented the core refactor (deterministic polling with wait_for_function / expect(), wait_for_console_log helper, session-scoped browser + function-scoped contexts, type hints, and hardened run_browser_tests.sh). Authored the majority of commits, addressed review feedback, extracted shared utilities to tests/test_utils.py, and created supporting documentation.

Refactor test infrastructure for improved reliability:

- Split fixture setup: session-scoped browser launch + function-scoped page contexts for better isolation and startup overhead reduction
- Replace all page.wait_for_timeout() calls with deterministic page.wait_for_function() checks or expect() assertions
- Add wait_for_console_log() helper to poll for matching console logs with predicates instead of fixed waits
- Improve Godot initialization check: use explicit '=== true' instead of loose truthy check
- Use expect() API for DOM element visibility assertions (canvas, buttons)
- Add type hints to function parameters and imports (Any, Callable, Generator)
- Improve shell script: better signal handling, robust server startup checks, git safety config
- Remove verbose/redundant comments to improve readability

These changes eliminate timing-dependent flakiness common in browser automation by making assertions wait for actual state changes rather than arbitrary durations.
@ikostan ikostan self-assigned this Jul 24, 2026
@ikostan ikostan added enhancement New feature or request testing refactoring labels Jul 24, 2026
@ikostan ikostan linked an issue Jul 24, 2026 that may be closed by this pull request
14 tasks
@ikostan ikostan added python Pull requests that update python code QA labels Jul 24, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors Playwright-based E2E tests and the browser test runner to replace brittle fixed timeouts with deterministic state-based polling, introduce reusable console-log waiting helpers, improve fixtures and typing, and harden the CI shell script and server startup behavior.

File-Level Changes

Change Details Files
Introduce deterministic console-log polling helpers and replace ad-hoc log assertions based on fixed waits.
  • Add a wait_for_console_log(predicate, start_idx, timeout_ms) helper in multiple tests to poll captured console logs until a predicate matches or the timeout elapses.
  • Use wait_for_console_log instead of page.wait_for_timeout() followed by manual log slicing for all log-based assertions in volume, reset, audio, difficulty, back-flow, and navigation tests.
  • Standardize console assertion patterns to rely on lowercased log text and structured waiting, reducing log handling duplication and flakiness.
tests/volume_sliders_mutes_test.py
tests/reset_audio_flow_test.py
tests/audio_flow_test.py
tests/difficulty_flow_test.py
tests/back_flow_test.py
tests/navigation_to_audio_test.py
Replace arbitrary page.wait_for_timeout() calls with deterministic Playwright waits and expect() assertions for DOM and engine state.
  • Remove initial splash-scene waits and instead wait for window.godotInitialized === true using page.wait_for_function in all affected tests.
  • Switch canvas and overlay visibility checks from page.wait_for_selector() and manual style evaluation to expect(locator).to_be_visible() and page.wait_for_function() on specific style/display values.
  • Use page.wait_for_function() to verify slider value changes and menu transitions instead of time-based sleeps, including difficulty, audio back navigation, and reset flows.
tests/volume_sliders_mutes_test.py
tests/reset_audio_flow_test.py
tests/audio_flow_test.py
tests/difficulty_flow_test.py
tests/back_flow_test.py
tests/navigation_to_audio_test.py
tests/load_main_menu_test.py
tests/no_error_logs_test.py
tests/validate_clean_load_test.py
Refine Godot initialization and DOM overlay checks to be explicit and robust.
  • Change initialization checks from truthy window.godotInitialized to strict window.godotInitialized === true for all tests.
  • Ensure main-menu and options DOM overlays are asserted using Playwright expect() and explicit style checks, rather than comments or loose visibility assumptions.
  • Tighten checks around gameplay/audio/options menu transitions by explicitly asserting display style changes (block/none) after back and navigation actions.
tests/volume_sliders_mutes_test.py
tests/reset_audio_flow_test.py
tests/audio_flow_test.py
tests/difficulty_flow_test.py
tests/back_flow_test.py
tests/navigation_to_audio_test.py
tests/load_main_menu_test.py
tests/validate_clean_load_test.py
Restructure pytest fixtures to reuse a session-scoped browser and provide function-scoped, isolated contexts and pages.
  • Introduce a session-scoped browser_instance fixture that launches Chromium once with the required GPU/WebGL flags and yields the Browser.
  • Refactor the page fixture to depend on browser_instance, creating a new BrowserContext and Page per test, with optional HAR recording based on a record_har marker.
  • Add typing to fixtures (Browser, BrowserContext, Page, Generator, pytest.FixtureRequest, pytest.Config) and clean up browser lifecycle to close only contexts per test and the browser at session end.
tests/conftest.py
Improve typing, imports, and comment clarity across tests.
  • Add typing imports such as Any, Callable, Generator, Dict, List, Optional where appropriate and annotate console handlers and helper functions.
  • Import pytest and Playwright expect API in tests that use them, and remove redundant or overly verbose docstrings and comments that no longer reflect behavior.
  • Normalize DEFAULT_TIMEOUT/TEST_TIMEOUT definitions and remove outdated comments about fallback behavior or CLI feature flags that are now enforced elsewhere.
tests/volume_sliders_mutes_test.py
tests/reset_audio_flow_test.py
tests/audio_flow_test.py
tests/difficulty_flow_test.py
tests/back_flow_test.py
tests/navigation_to_audio_test.py
tests/conftest.py
Harden the browser test runner shell script for CI reliability and cleanup.
  • Configure git safe.directory for the project path to avoid dubious ownership errors in containers before running modifications.
  • Simplify git restore to restore both export_presets.cfg and globals.gd in a single command with error checking.
  • Add a trap on EXIT/INT/TERM to reliably kill the background HTTP server, increase server readiness polling frequency and timeout, and restructure the curl-based readiness loop with clear failure messaging.
  • Reformat the pytest invocation with line breaks for readability and explicitly comment the report generation section.
  • Remove manual kill of SERVER_PID at the end in favor of the trap-based cleanup.
workspace/run_browser_tests.sh

Assessment against linked issues

Issue Objective Addressed Explanation
#772 Refactor Playwright browser tests in tests/ to replace arbitrary static waits (e.g., page.wait_for_timeout/time.sleep) with deterministic synchronization using Playwright wait_for_function/expect and console-log/event predicates, while preserving existing assertions and behavior.
#772 Streamline pytest fixtures in tests/conftest.py to use an optimized browser lifecycle (session-scoped browser, function-scoped contexts/pages) and improve maintainability.
#772 Improve workspace/run_browser_tests.sh to reduce unnecessary startup/readiness overhead and make CI/browser test execution more robust (server startup checks, cleanup, Git safety, etc.).

Possibly linked issues

  • #TASK: PR directly addresses the test wait refactors, fixture lifecycle changes, and runner optimizations specified in the issue.
  • #N/A: PR implements audio_flow_test with WARN-01–03 using DOM overlays and log assertions, matching the feature request.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Playwright E2E tests now use explicit Godot, UI, and console-log synchronization. Browser contexts are isolated over shared Chromium, and the browser-test runner improves server readiness, cleanup, restoration checks, and failure handling.

Changes

E2E determinism and test execution

Layer / File(s) Summary
Shared browser and console utilities
tests/conftest.py, tests/test_utils.py
Shared timeouts, console polling, isolated contexts, and a session-scoped Chromium fixture support deterministic tests.
Startup and menu navigation synchronization
tests/load_main_menu_test.py, tests/navigation_to_audio_test.py, tests/no_error_logs_test.py, tests/validate_clean_load_test.py
Startup, menu transitions, visibility checks, exposed callbacks, and console events use explicit readiness assertions.
Audio volume, warning, and reset flows
tests/audio_flow_test.py, tests/reset_audio_flow_test.py, tests/volume_sliders_mutes_test.py
Audio scenarios use console polling and DOM assertions for volume, mute, warning, reset, persistence, and isolation behavior.
Back navigation and gameplay flow
tests/back_flow_test.py, tests/difficulty_flow_test.py
Back-navigation, difficulty, game loading, and firing checks wait for callback availability and sequential console milestones.
Browser test runner lifecycle
workspace/run_browser_tests.sh
The runner improves repository setup, restoration checks, server polling, failure reporting, and background-server cleanup.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

  • Issue 772: Covers the same Playwright tests, fixtures, and runner refactoring objectives.
  • Issue 773: Relates to shared browser/context lifecycle changes.
  • Issue 846: Addresses related replacement of arbitrary waits with deterministic synchronization.

Possibly related PRs

Suggested labels: web, audio, menu, GUI

Poem

I hop through logs where timers slept,
And check each canvas, button, step.
Contexts bloom, old waits flee,
Audio resets happily.
Clean traps guard the server bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 92.59% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: replacing brittle waits with deterministic polling in tests.
Description check ✅ Passed The description is detailed and covers purpose, changes, testing, and checklist, though the template headings are not followed exactly.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch code-audits-asynchronous-refactoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ikostan ikostan moved this to In Progress in Sky Lock Assault Project Jul 24, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • The wait_for_console_log helper is duplicated across multiple test modules with slightly different defaults; consider extracting it into a shared utility or fixture in conftest.py to avoid repetition and keep behavior consistent.
  • In several places you still use string-based page.wait_for_function checks on DOM state (e.g., getComputedStyle(...).display); you might simplify and harden these by wrapping them in reusable helpers or using Playwright locator assertions where possible to reduce reliance on raw JS snippets.
  • The session-scoped browser_instance plus per-test BrowserContext is a good optimization; you may want to add an explicit context.tracing.stop() or similar cleanup if you later enable tracing/recording features to avoid leaks across tests.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `wait_for_console_log` helper is duplicated across multiple test modules with slightly different defaults; consider extracting it into a shared utility or fixture in `conftest.py` to avoid repetition and keep behavior consistent.
- In several places you still use string-based `page.wait_for_function` checks on DOM state (e.g., `getComputedStyle(...).display`); you might simplify and harden these by wrapping them in reusable helpers or using Playwright locator assertions where possible to reduce reliance on raw JS snippets.
- The session-scoped `browser_instance` plus per-test `BrowserContext` is a good optimization; you may want to add an explicit `context.tracing.stop()` or similar cleanup if you later enable tracing/recording features to avoid leaks across tests.

## Individual Comments

### Comment 1
<location path="tests/volume_sliders_mutes_test.py" line_range="66-75" />
<code_context>

     page.on("console", on_console)
+
+    def wait_for_console_log(
+            predicate: Callable[[str], bool], start_idx: int, timeout_ms: int = TEST_TIMEOUT
+    ) -> None:
+        """
+        Helper to poll until a matching console log arrives or timeout expires.
+        """
+        start_time = time.time()
+        while (time.time() - start_time) * 1000 < timeout_ms:
+            if any(predicate(log["text"].lower()) for log in logs[start_idx:]):
+                return
+            page.wait_for_timeout(50)  # Micro-poll for event loop progression
+        pytest.fail(f"Timed out waiting for expected console log matching predicate after {timeout_ms}ms")
+
     try:
</code_context>
<issue_to_address>
**suggestion (testing):** Consider centralizing `wait_for_console_log` as a shared helper to avoid duplication and keep behavior consistent across tests.

The same helper is now duplicated across multiple test modules (volume, reset, audio, difficulty, back, navigation). Extracting it into a shared location (e.g., `conftest.py` or `test_utils.py`) and reusing it as a fixture or helper function would simplify maintenance and keep polling/timeout behavior consistent, while still allowing each test to manage its own `start_idx`.

Suggested implementation:

```python
    page.on("console", on_console)

    from test_utils import wait_for_console_log as shared_wait_for_console_log

    def wait_for_console_log(
            predicate: Callable[[str], bool], start_idx: int, timeout_ms: int = TEST_TIMEOUT
    ) -> None:
        """
        Delegate to shared helper to poll until a matching console log arrives or timeout expires.
        """
        shared_wait_for_console_log(page=page, logs=logs, predicate=predicate, start_idx=start_idx, timeout_ms=timeout_ms)

```

To fully centralize the behavior across tests:
1. Create a shared helper in a common test module, for example `tests/test_utils.py`:

   ```python
   import time
   import pytest

   TEST_TIMEOUT = 5_000  # or import from a shared constants module

   def wait_for_console_log(page, logs, predicate, start_idx: int, timeout_ms: int = TEST_TIMEOUT) -> None:
       """
       Helper to poll until a matching console log arrives or timeout expires.
       """
       start_time = time.time()
       while (time.time() - start_time) * 1000 < timeout_ms:
           if any(predicate(log["text"].lower()) for log in logs[start_idx:]):
               return
           page.wait_for_timeout(50)
       pytest.fail(f"Timed out waiting for expected console log matching predicate after {timeout_ms}ms")
   ```

2. Adjust the import in `tests/volume_sliders_mutes_test.py` if needed based on your package layout, e.g. use `from .test_utils import wait_for_console_log as shared_wait_for_console_log` if `tests` is a package.
3. In the other test modules (volume, reset, audio, difficulty, back, navigation), remove their duplicated `wait_for_console_log` implementations and either:
   - Import and call `test_utils.wait_for_console_log(page, logs, predicate, start_idx, timeout_ms)`, or
   - Use the same thin delegating wrapper pattern if they rely on closure variables.
4. Ensure all modules use the same timeout constant (`TEST_TIMEOUT`) from a shared place to keep behavior consistent.
</issue_to_address>

### Comment 2
<location path="tests/volume_sliders_mutes_test.py" line_range="129-134" />
<code_context>
-        new_logs = logs[pre_change_log_count:]
-        assert any(
-            "log level changed to: debug" in log["text"].lower() for log in new_logs
+        wait_for_console_log(
+            lambda text: "log level changed to: debug" in text,
+            start_idx=pre_change_log_count,
</code_context>
<issue_to_address>
**issue (testing):** The SFX volume change assertions now only check a single log message, dropping verification of related side effects.

The prior test asserted multiple logs for an SFX volume change (e.g., `sfx volume level in audiomanager: 0.8` and `saved volumes to config`), validating propagation through the audio manager and persistence. With `wait_for_console_log`, it now only checks `applied loaded sfx volume to audioserver: 0.8`, so those side-effect verifications are lost. Please either broaden the predicate to include these messages or add additional `wait_for_console_log` calls/assertions to retain equivalent coverage of the persistence path.
</issue_to_address>

### Comment 3
<location path="tests/no_error_logs_test.py" line_range="66-71" />
<code_context>
-        # Wait for Godot engine init (ensures 'godot' object is defined)
-        page.wait_for_function("() => window.godotInitialized", timeout=DEFAULT_TIMEOUT)
+
+        # Wait deterministically for Godot engine initialization
+        page.wait_for_function("() => window.godotInitialized === true", timeout=DEFAULT_TIMEOUT)

         # Verify canvas and title to ensure game is initialized
         canvas = page.locator("canvas")
-        page.wait_for_selector("canvas", state="visible", timeout=DEFAULT_TIMEOUT)
+        expect(canvas).to_be_visible(timeout=DEFAULT_TIMEOUT)
         box: dict[str, float] | None = canvas.bounding_box()
         assert box is not None, "Canvas not found on page"
</code_context>
<issue_to_address>
**suggestion (testing):** Dropping the post-load buffer removes coverage for late-appearing errors; consider a deterministic replacement rather than removing it entirely.

Previously, `BUFFER_TIMEOUT` and `page.wait_for_timeout(BUFFER_TIMEOUT)` gave `no_error_logs_test` a brief window to catch errors occurring just after initialization. Without that buffer, the test now effectively stops observing logs once the engine is initialized and the canvas is visible, so errors triggered shortly after the main menu appears (e.g., deferred signals or late resource loads) may be missed.

To retain the reduced flakiness while preserving coverage for these late errors, consider a more deterministic wait: for example, poll logs until no new entries appear for a short period (N ms), or wait for a specific “main menu ready” console message. This keeps the observation window open long enough without reintroducing arbitrary sleeps.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/volume_sliders_mutes_test.py Outdated
Comment thread tests/volume_sliders_mutes_test.py
Comment thread tests/no_error_logs_test.py
@ikostan

ikostan commented Jul 24, 2026

Copy link
Copy Markdown
Owner Author

⚡ Performance Metrics Summary


📈 Suite Progression Benchmark

Run Benchmark Total Time Time Saved vs. Baseline Overall Speedup Status
Original Baseline 500.82s (8m 20s) Baseline 9/9 PASSED
**After conftest & back_flow** 368.04s (6m 08s) 132.78s ~26.5% 9/9 PASSED
**After difficulty_flow** 333.83s (5m 33s) 166.99s ~33.3% 9/9 PASSED
**After no_error_logs** 307.86s (5m 07s) 192.96s ~38.5% 9/9 PASSED
**After reset_audio_flow** 166.29s (2m 46s) 334.53s ~66.8% 9/9 PASSED
**After validate_clean_load** 166.04s (2m 46s) 334.78s ~66.9% 9/9 PASSED
Current (volume_sliders_mutes) 51.38s 449.44s (~7m 29s) ~89.7% faster (~10x) 9/9 PASSED

⏱️ Individual Test Execution Breakdown

By tracking the HTTP server GET /index.html request deltas from the latest log, we can observe the exact duration of each user flow test:

Test File Start Time End Time Approx. Duration
tests/audio_flow_test.py 03:31:38 03:31:43 ~5.0s
tests/back_flow_test.py (includes 1 page reload) 03:31:43 03:31:52 ~9.0s
tests/difficulty_flow_test.py 03:31:52 03:32:01 ~9.0s
tests/load_main_menu_test.py 03:32:01 03:32:03 ~2.0s
tests/navigation_to_audio_test.py 03:32:03 03:32:08 ~5.0s
tests/no_error_logs_test.py 03:32:08 03:32:10 ~2.0s
tests/reset_audio_flow_test.py (includes 1 page reload) 03:32:10 03:32:20 ~10.0s
tests/validate_clean_load_test.py 03:32:20 03:32:22 ~2.0s
tests/volume_sliders_mutes_test.py 03:32:22 03:32:27 ~5.0s

📋 Issue #772 Progress Checklist

  • Core Fixtures & Runner Scripts:

  • tests/conftest.py

  • workspace/run_browser_tests.sh

  • User Flow Tests:

  • tests/audio_flow_test.py

  • tests/back_flow_test.py

  • tests/difficulty_flow_test.py

  • tests/load_main_menu_test.py

  • tests/navigation_to_audio_test.py

  • tests/no_error_logs_test.py

  • tests/reset_audio_flow_test.py

  • tests/validate_clean_load_test.py

  • tests/volume_sliders_mutes_test.py

  • Integration & CI Tests:

  • tests/refactor/difficulty_integration_test.py

  • tests/refactor/fuel_depletion_test.py

  • tests/refactor/log_level_test.py

  • tests/refactor/weapon_firing_test.py

  • tests/ci/test_ci_flag_injection.py

  • tests/ci/test_salt_injection.py

This commit fixes the style issues introduced in e9212ce according to the output
from Black and isort.

Details: #845
@deepsource-io

deepsource-io Bot commented Jul 24, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 17985cf...2a83e2f on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

Important

Some issues found as part of this review are outside of the diff in this pull request and aren't shown in the inline review comments due to GitHub's API limitations. You can see those issues on the DeepSource dashboard.

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Python Jul 26, 2026 5:02a.m. Review ↗
JavaScript Jul 26, 2026 5:02a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

Comment thread tests/audio_flow_test.py
Comment thread tests/audio_flow_test.py Outdated
Comment thread tests/audio_flow_test.py Outdated
Comment thread tests/audio_flow_test.py Outdated
Comment thread tests/audio_flow_test.py Outdated
Comment thread tests/reset_audio_flow_test.py Outdated
Comment thread tests/volume_sliders_mutes_test.py Outdated
Comment thread tests/volume_sliders_mutes_test.py Outdated
Comment thread tests/volume_sliders_mutes_test.py Outdated
Comment thread tests/volume_sliders_mutes_test.py Outdated
Added the missing docstring to `on_console()` in `tests/audio_flow_test.py` to satisfy the DeepSource documentation requirement.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
tests/audio_flow_test.py (1)

53-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider hoisting wait_for_console_log into a shared fixture.

This helper (and the on_console/logs setup) is duplicated verbatim across audio_flow_test.py, reset_audio_flow_test.py, volume_sliders_mutes_test.py, navigation_to_audio_test.py, back_flow_test.py, and difficulty_flow_test.py. A conftest.py fixture returning (logs, wait_for_console_log) bound to page would remove ~10 lines × 6 files and keep polling behavior consistent. Deferrable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/audio_flow_test.py` around lines 53 - 62, Hoist the duplicated
on_console/logs setup and wait_for_console_log helper from the listed audio-flow
tests into a shared conftest.py fixture bound to page. Have the fixture provide
the logs collection and polling helper with the same predicate, start-index,
timeout, and failure behavior, then update each test to consume the fixture and
remove its local copies.
tests/difficulty_flow_test.py (1)

72-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Helper default timeout deviates from the shared contract.

wait_for_console_log here defaults timeout_ms=DEFAULT_TIMEOUT (30000), whereas the equivalent helper in back_flow_test.py, reset_audio_flow_test.py, and volume_sliders_mutes_test.py defaults to TEST_TIMEOUT. Calls that omit timeout_ms (lines 159, 210, 232, 246, 257) therefore wait 6× longer than the other suites. Align the default with the shared contract for consistency.

♻️ Align default with shared contract
     def wait_for_console_log(
-        predicate: Callable[[str], bool], start_idx: int, timeout_ms: int = DEFAULT_TIMEOUT
+        predicate: Callable[[str], bool], start_idx: int, timeout_ms: int = TEST_TIMEOUT
     ) -> None:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/difficulty_flow_test.py` around lines 72 - 83, Update the
wait_for_console_log helper’s timeout_ms default from DEFAULT_TIMEOUT to the
shared TEST_TIMEOUT constant, matching the equivalent helpers in the other flow
tests. Leave explicit timeout overrides and the polling behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/audio_flow_test.py`:
- Line 232: Replace the bare assert in the warning-check logic with an explicit
exception so the failure remains enforced under python -O. Preserve the existing
message, including log['text'], and update only the assertion at the
unexpected-warning branch.

In `@tests/difficulty_flow_test.py`:
- Around line 42-44: Update the timeout configuration constants so
DEFAULT_TIMEOUT reads from its own environment variable with the 30-second
fallback, while TEST_TIMEOUT continues reading TEST_TIMEOUT with its 5-second
fallback. Keep the existing timeout usages unchanged.

In `@tests/no_error_logs_test.py`:
- Line 28: Update the DEFAULT_TIMEOUT definition so it no longer reads
TEST_TIMEOUT; use a separate environment variable with an independent safe
default of 30000 for page load/init visibility, while preserving TEST_TIMEOUT
for UI timing only.

In `@workspace/run_browser_tests.sh`:
- Around line 67-79: Update the readiness probe loop in run_browser_tests.sh to
require a successful HTTP response, such as by using curl’s fail-on-error
option, and add a per-request timeout so each attempt remains within the
existing retry budget. Preserve the current retry count, delay, server_ready
assignment, and failure exit behavior.
- Line 42: Update the server cleanup trap near the security-isolated web server
startup: perform server termination from an EXIT trap, and have INT and TERM
handlers exit with their corresponding signal status so cancellation cannot
continue into readiness checks or test execution. Preserve cleanup when the
script exits for any reason and keep the existing SERVER_PID behavior.

---

Nitpick comments:
In `@tests/audio_flow_test.py`:
- Around line 53-62: Hoist the duplicated on_console/logs setup and
wait_for_console_log helper from the listed audio-flow tests into a shared
conftest.py fixture bound to page. Have the fixture provide the logs collection
and polling helper with the same predicate, start-index, timeout, and failure
behavior, then update each test to consume the fixture and remove its local
copies.

In `@tests/difficulty_flow_test.py`:
- Around line 72-83: Update the wait_for_console_log helper’s timeout_ms default
from DEFAULT_TIMEOUT to the shared TEST_TIMEOUT constant, matching the
equivalent helpers in the other flow tests. Leave explicit timeout overrides and
the polling behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bd2d0c2-cca7-42f0-8e56-4020e047960b

📥 Commits

Reviewing files that changed from the base of the PR and between 17985cf and e9212ce.

📒 Files selected for processing (11)
  • tests/audio_flow_test.py
  • tests/back_flow_test.py
  • tests/conftest.py
  • tests/difficulty_flow_test.py
  • tests/load_main_menu_test.py
  • tests/navigation_to_audio_test.py
  • tests/no_error_logs_test.py
  • tests/reset_audio_flow_test.py
  • tests/validate_clean_load_test.py
  • tests/volume_sliders_mutes_test.py
  • workspace/run_browser_tests.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: GUT Unit Tests / unit-test
  • GitHub Check: CI/CD Infrastructure Tests / Test Godot Asset Infrastructure and Signature Verification
  • GitHub Check: Sourcery review
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-19T05:07:07.286Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 488
File: tests/difficulty_flow_test.py:194-200
Timestamp: 2026-03-19T05:07:07.286Z
Learning: When writing/adjusting tests that assert SkyLockAssault log output for difficulty (and other float settings), expect the decimal point to be preserved (e.g., logs like "setting 'difficulty' updated to: 1.0"). Do not use regexes that fail on floats due to the decimal point (e.g., patterns with a negative lookahead that assumes digits contain no '.'), since they will not match "1.0". Instead, use a simple substring check for the expected log prefix/value, or use a float-aware regex (e.g., matching `\d+(?:\.\d+)?`) / parse the logged value as a float before asserting.

Applied to files:

  • tests/validate_clean_load_test.py
  • tests/conftest.py
  • tests/load_main_menu_test.py
  • tests/back_flow_test.py
  • tests/no_error_logs_test.py
  • tests/volume_sliders_mutes_test.py
  • tests/navigation_to_audio_test.py
  • tests/reset_audio_flow_test.py
  • tests/audio_flow_test.py
  • tests/difficulty_flow_test.py
🪛 ast-grep (0.44.1)
tests/back_flow_test.py

[warning] 292-294: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(
f"artifacts/test_back_failure_console_logs_{timestamp}.txt", "w"
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

tests/volume_sliders_mutes_test.py

[warning] 402-404: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(
f"artifacts/test_volume_failure_console_logs_{timestamp}.txt", "w"
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

tests/reset_audio_flow_test.py

[warning] 483-485: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(
f"artifacts/test_reset_failure_console_logs_{timestamp}.txt", "w"
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🪛 Ruff (0.15.21)
tests/reset_audio_flow_test.py

[warning] 480-480: Use explicit conversion flag

Replace with conversion flag

(RUF010)

tests/audio_flow_test.py

[warning] 232-232: Do not assert False (python -O removes these calls), raise AssertionError()

Replace assert False

(B011)

🪛 Shellcheck (0.11.0)
workspace/run_browser_tests.sh

[info] 85-85: Not following: /opt/venv/bin/activate was not specified as input (see shellcheck -x).

(SC1091)

🔇 Additional comments (20)
workspace/run_browser_tests.sh (3)

13-20: LGTM!


39-40: LGTM!


82-95: LGTM!

tests/conftest.py (3)

15-30: LGTM!


45-51: LGTM!


40-43: 🩺 Stability & Availability

No import issue found. re and Path are already imported in tests/conftest.py.

tests/load_main_menu_test.py (1)

83-97: LGTM!

tests/no_error_logs_test.py (1)

66-71: LGTM!

tests/validate_clean_load_test.py (1)

56-58: LGTM!

tests/navigation_to_audio_test.py (1)

93-108: LGTM!

Also applies to: 120-148, 169-210

tests/audio_flow_test.py (1)

77-115: LGTM!

Also applies to: 116-232

tests/reset_audio_flow_test.py (1)

92-131: LGTM!

Also applies to: 151-223, 307-363, 374-421, 431-477

tests/volume_sliders_mutes_test.py (1)

92-160: LGTM!

Also applies to: 162-394

tests/back_flow_test.py (4)

294-294: The static-analysis path-traversal warning here is a false positive: timestamp is int(time.time()), not request- or user-derived, so the artifact path is fully controlled by the test.


55-79: LGTM!


93-131: LGTM!


142-282: LGTM!

tests/difficulty_flow_test.py (3)

98-104: LGTM!


127-162: LGTM!

Also applies to: 204-265


300-354: LGTM!

Comment thread tests/audio_flow_test.py Outdated
Comment thread tests/difficulty_flow_test.py Outdated
Comment thread tests/no_error_logs_test.py Outdated
Comment thread workspace/run_browser_tests.sh Outdated
Comment thread workspace/run_browser_tests.sh
ikostan added 4 commits July 23, 2026 20:45
Formatted the single-line docstring for `wait_for_console_log()` onto a single line to comply with PEP 257 / DeepSource recommendations.
Split the `pytest.fail` string literal across multiple lines to satisfy the maximum line length constraint.
Split the JS expression string across multiple lines using implicit string concatenation to reduce line length below the limit.
Formatted JS predicate string across multiple lines using implicit concatenation to adhere to line length limits.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
tests/reset_audio_flow_test.py (1)

37-38: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Both timeout constants read the same env var.

Same duplicated-env-var issue as the sibling flow tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/reset_audio_flow_test.py` around lines 37 - 38, Update DEFAULT_TIMEOUT
in the timeout configuration near TEST_TIMEOUT to read its intended distinct
environment variable, while keeping TEST_TIMEOUT mapped to TEST_TIMEOUT. Ensure
the two constants no longer read the same env var, matching the sibling flow
tests.
tests/back_flow_test.py (1)

38-39: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Both timeout constants read the same env var.

Same issue as the other flow tests: DEFAULT_TIMEOUT and TEST_TIMEOUT both key off os.getenv("TEST_TIMEOUT", ...). Setting TEST_TIMEOUT in CI collapses both to the same value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/back_flow_test.py` around lines 38 - 39, Update the timeout constant
definitions in back_flow_test.py so DEFAULT_TIMEOUT reads its intended distinct
environment variable rather than TEST_TIMEOUT, while leaving TEST_TIMEOUT bound
to TEST_TIMEOUT and preserving their existing fallback values.
tests/volume_sliders_mutes_test.py (1)

37-38: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Both timeout constants read the same env var.

Same duplicated-env-var issue as the sibling flow tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/volume_sliders_mutes_test.py` around lines 37 - 38, Update
DEFAULT_TIMEOUT and TEST_TIMEOUT in the volume slider mute tests to read their
intended, distinct environment variables, matching the sibling flow tests;
retain the existing default values and integer parsing.
tests/audio_flow_test.py (1)

36-37: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Both timeout constants read the same env var.

DEFAULT_TIMEOUT and TEST_TIMEOUT both call os.getenv("TEST_TIMEOUT", ...); only the fallback default differs. If TEST_TIMEOUT is set in the environment, DEFAULT_TIMEOUT silently collapses to the same value, defeating the intent of having a longer timeout for Godot init/networkidle navigation vs. a shorter per-step timeout — undermining the flakiness fix this PR is meant to deliver.

🐛 Proposed fix
-DEFAULT_TIMEOUT = int(os.getenv("TEST_TIMEOUT", "30000"))
+DEFAULT_TIMEOUT = int(os.getenv("DEFAULT_TIMEOUT", "30000"))
 TEST_TIMEOUT = int(os.getenv("TEST_TIMEOUT", "5000"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/audio_flow_test.py` around lines 36 - 37, Update the DEFAULT_TIMEOUT
definition in tests/audio_flow_test.py to read its own environment variable
rather than TEST_TIMEOUT, preserving the separate longer default for Godot
initialization/network-idle navigation while TEST_TIMEOUT remains the shorter
per-step timeout.
🧹 Nitpick comments (4)
tests/reset_audio_flow_test.py (1)

66-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated wait_for_console_log helper.

Same helper duplicated again; candidate for a shared conftest.py utility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/reset_audio_flow_test.py` around lines 66 - 79, Remove the duplicated
wait_for_console_log helper from the test and reuse a shared utility from
conftest.py, preserving its predicate matching, start_idx handling, timeout
behavior, and failure message.
tests/volume_sliders_mutes_test.py (1)

66-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated wait_for_console_log helper.

Same helper duplicated again across the test suite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/volume_sliders_mutes_test.py` around lines 66 - 79, Remove the
duplicated wait_for_console_log helper from this test module and reuse the
existing shared helper already provided by the test suite, updating references
or imports as needed while preserving its polling and timeout behavior.
tests/audio_flow_test.py (1)

54-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

wait_for_console_log is duplicated verbatim across every test file.

Identical helper (and identical on_console/logs-buffer setup) is copy-pasted here and in back_flow_test.py, reset_audio_flow_test.py, volume_sliders_mutes_test.py, and per graph context also difficulty_flow_test.py/navigation_to_audio_test.py. Extracting it into conftest.py (e.g., as a fixture that wraps page and exposes logs/wait_for_console_log) would remove this duplication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/audio_flow_test.py` around lines 54 - 67, Extract the duplicated
console-log buffer, on_console setup, and wait_for_console_log helper into a
shared fixture in conftest.py. Update the affected test files to use that
fixture instead of defining their own logs and polling helper, preserving the
existing predicate, timeout, and failure behavior.
tests/back_flow_test.py (1)

67-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated wait_for_console_log helper.

Same helper as in the sibling flow tests; candidate for extraction into conftest.py.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/back_flow_test.py` around lines 67 - 80, Extract the duplicated
wait_for_console_log helper from the flow tests into the shared conftest.py test
utilities, then update the affected tests to use the centralized helper while
preserving its predicate, timeout, polling, and failure behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/audio_flow_test.py`:
- Around line 36-37: Update the DEFAULT_TIMEOUT definition in
tests/audio_flow_test.py to read its own environment variable rather than
TEST_TIMEOUT, preserving the separate longer default for Godot
initialization/network-idle navigation while TEST_TIMEOUT remains the shorter
per-step timeout.

In `@tests/back_flow_test.py`:
- Around line 38-39: Update the timeout constant definitions in
back_flow_test.py so DEFAULT_TIMEOUT reads its intended distinct environment
variable rather than TEST_TIMEOUT, while leaving TEST_TIMEOUT bound to
TEST_TIMEOUT and preserving their existing fallback values.

In `@tests/reset_audio_flow_test.py`:
- Around line 37-38: Update DEFAULT_TIMEOUT in the timeout configuration near
TEST_TIMEOUT to read its intended distinct environment variable, while keeping
TEST_TIMEOUT mapped to TEST_TIMEOUT. Ensure the two constants no longer read the
same env var, matching the sibling flow tests.

In `@tests/volume_sliders_mutes_test.py`:
- Around line 37-38: Update DEFAULT_TIMEOUT and TEST_TIMEOUT in the volume
slider mute tests to read their intended, distinct environment variables,
matching the sibling flow tests; retain the existing default values and integer
parsing.

---

Nitpick comments:
In `@tests/audio_flow_test.py`:
- Around line 54-67: Extract the duplicated console-log buffer, on_console
setup, and wait_for_console_log helper into a shared fixture in conftest.py.
Update the affected test files to use that fixture instead of defining their own
logs and polling helper, preserving the existing predicate, timeout, and failure
behavior.

In `@tests/back_flow_test.py`:
- Around line 67-80: Extract the duplicated wait_for_console_log helper from the
flow tests into the shared conftest.py test utilities, then update the affected
tests to use the centralized helper while preserving its predicate, timeout,
polling, and failure behavior.

In `@tests/reset_audio_flow_test.py`:
- Around line 66-79: Remove the duplicated wait_for_console_log helper from the
test and reuse a shared utility from conftest.py, preserving its predicate
matching, start_idx handling, timeout behavior, and failure message.

In `@tests/volume_sliders_mutes_test.py`:
- Around line 66-79: Remove the duplicated wait_for_console_log helper from this
test module and reuse the existing shared helper already provided by the test
suite, updating references or imports as needed while preserving its polling and
timeout behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37a523f8-df06-4307-9c86-1643e5b6700d

📥 Commits

Reviewing files that changed from the base of the PR and between e9212ce and c001cc5.

📒 Files selected for processing (10)
  • tests/audio_flow_test.py
  • tests/back_flow_test.py
  • tests/conftest.py
  • tests/difficulty_flow_test.py
  • tests/load_main_menu_test.py
  • tests/navigation_to_audio_test.py
  • tests/no_error_logs_test.py
  • tests/reset_audio_flow_test.py
  • tests/validate_clean_load_test.py
  • tests/volume_sliders_mutes_test.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/conftest.py
  • tests/load_main_menu_test.py
  • tests/validate_clean_load_test.py
  • tests/no_error_logs_test.py
  • tests/navigation_to_audio_test.py
  • tests/difficulty_flow_test.py
📜 Review details
⚠️ CI failures not shown inline (1)

Commit Status: DeepSource: Python: DeepSource: Python

Conclusion: failure

Analysis failed: Blocking issues or failing metrics found
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-19T05:07:07.286Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 488
File: tests/difficulty_flow_test.py:194-200
Timestamp: 2026-03-19T05:07:07.286Z
Learning: When writing/adjusting tests that assert SkyLockAssault log output for difficulty (and other float settings), expect the decimal point to be preserved (e.g., logs like "setting 'difficulty' updated to: 1.0"). Do not use regexes that fail on floats due to the decimal point (e.g., patterns with a negative lookahead that assumes digits contain no '.'), since they will not match "1.0". Instead, use a simple substring check for the expected log prefix/value, or use a float-aware regex (e.g., matching `\d+(?:\.\d+)?`) / parse the logged value as a float before asserting.

Applied to files:

  • tests/audio_flow_test.py
  • tests/back_flow_test.py
  • tests/reset_audio_flow_test.py
  • tests/volume_sliders_mutes_test.py
🪛 ast-grep (0.44.1)
tests/back_flow_test.py

[warning] 300-302: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(
f"artifacts/test_back_failure_console_logs_{timestamp}.txt", "w"
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

tests/reset_audio_flow_test.py

[warning] 502-504: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(
f"artifacts/test_reset_failure_console_logs_{timestamp}.txt", "w"
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

tests/volume_sliders_mutes_test.py

[warning] 411-413: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(
f"artifacts/test_volume_failure_console_logs_{timestamp}.txt", "w"
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🪛 Ruff (0.15.21)
tests/audio_flow_test.py

[warning] 289-289: Do not assert False (python -O removes these calls), raise AssertionError()

Replace assert False

(B011)

🔇 Additional comments (9)
tests/audio_flow_test.py (3)

288-290: 🎯 Functional Correctness | ⚡ Quick win

Bare assert False still unresolved.

Ruff flags this (B011); assert is stripped under python -O. This was already raised in a previous review round with a proposed raise AssertionError(...) fix that hasn't been applied.

Source: Linters/SAST tools


292-303: 🩺 Stability & Availability | ⚡ Quick win

except Exception will not catch pytest.fail() timeouts from wait_for_console_log.

pytest.fail() raises Failed, which subclasses OutcomeException(BaseException), not Exception. When wait_for_console_log (lines 65-67) times out, the exception bypasses this except Exception as e: block entirely, so the screenshot, console-log dump, and HTML capture never run for what is now the most likely failure mode this PR introduces. The finally block still runs (coverage save), but debugging artifacts are silently lost.

🛡️ Proposed fix
-    except Exception as e:
+    except (Exception, pytest.fail.Exception) as e:

82-267: LGTM!

tests/back_flow_test.py (2)

296-306: 🩺 Stability & Availability | ⚡ Quick win

except Exception misses pytest.fail()-raised timeouts.

Same gap as elsewhere: wait_for_console_log's pytest.fail() (lines 78-80) raises Failed(BaseException), so this except Exception as e: never triggers for it, skipping screenshot/log capture on that failure path.


95-294: LGTM!

tests/reset_audio_flow_test.py (2)

498-508: 🩺 Stability & Availability | ⚡ Quick win

except Exception misses pytest.fail()-raised timeouts.

Same gap: wait_for_console_log's pytest.fail() (lines 77-79) raises Failed(BaseException), bypassing this catch and losing the failure screenshot/log dump.


94-497: LGTM!

tests/volume_sliders_mutes_test.py (2)

405-417: 🩺 Stability & Availability | ⚡ Quick win

except Exception misses pytest.fail()-raised timeouts.

Same gap: wait_for_console_log's pytest.fail() (lines 77-79) raises Failed(BaseException), bypassing this catch and losing the failure screenshot/log dump.


94-403: LGTM!

deepsource-autofix Bot and others added 5 commits July 24, 2026 03:52
This commit fixes the style issues introduced in 3249cba according to the output
from Black and isort.

Details: #845
Reflow and clean up tests/back_flow_test.py: wrap long overview and docstring lines, split long JS predicate strings passed to wait_for_function for readability, tidy the wait_for_console_log signature and docstring, and remove an outdated comment about Playwright JS coverage. These are purely formatting and wording changes with no intended behavioral changes.
Wrap long docstrings and configuration text to improve line length compliance and code readability.
Reformat test file to improve code readability and comply with style guidelines:
- Break long lines into multiple lines (especially JavaScript/CSS selectors and error messages)
- Add blank lines in docstrings after summary line (PEP 257)
- Simplify some error message text for clarity
- No functional changes to test logic or behavior
Reformat volume_sliders_mutes_test.py to comply with line length limits:
- Break long lines in function signatures and docstrings
- Split long JavaScript string literals across multiple lines
- Convert multi-line docstring to single-line format
Addressed! Switched `wait_for_console_log` in `tests/test_utils.py` from `time.time()` to `time.monotonic()` to protect polling deadlines against system clock adjustments and drift.
@ikostan

ikostan commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

tests/test_utils.py (1)

26-27: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
Use a monotonic clock for the polling deadline.
time.time() can jump when the system clock is adjusted, causing polling to expire early or continue beyond timeout_ms. Use time.monotonic() for elapsed-time measurement.

🔧 Proposed fix

-    start_time = time.time()
-    while (time.time() - start_time) * 1000 < timeout_ms:
+    start_time = time.monotonic()
+    while (time.monotonic() - start_time) * 1000 < timeout_ms:

Addressed! Switched wait_for_console_log in tests/test_utils.py from time.time() to time.monotonic() to protect polling deadlines against system clock adjustments and drift.

Addressed! Centralized `has_save_log` into `tests/test_utils.py` and updated `tests/volume_sliders_mutes_test.py` (and sibling test files) to import and share the common helper.
@ikostan

ikostan commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

tests/volume_sliders_mutes_test.py (1)

38-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Move _has_save_log into tests/test_utils.py.
This is a verbatim copy of _has_save_log in tests/difficulty_flow_test.py (lines 38-45). Since this PR already centralized wait_for_console_log and the timeout constants, this helper belongs there too.


Addressed! Centralized has_save_log into tests/test_utils.py and updated tests/volume_sliders_mutes_test.py (and sibling test files) to import and share the common helper.

ikostan and others added 3 commits July 25, 2026 21:09
Addressed! Updated `wait_for_console_log` in `tests/test_utils.py` to raise `AssertionError` instead of calling `pytest.fail`. This allows timeout exceptions to be caught by each test's `except Exception as e:` block, ensuring screenshots, console logs, and HTML snapshots are captured on failure before the exception is raised.
Add an additional wait_for_console_log assertion in the test_reset_flow test to verify that console logs contain messages about saving, encryption, or plaintext fallback after a state change.
This commit fixes the style issues introduced in 8118c1d according to the output
from Black and isort.

Details: #845

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/volume_sliders_mutes_test.py (1)

68-74: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Preserve the primary test failure during V8 coverage cleanup.

The coverage block starts cdp_session before the CDP commands finish, and the finally at tests/volume_sliders_mutes_test.py:490-496 unconditionally runs coverage shutdown and disk writes. Any setup, collection, shutdown, or write failure can overwrite or replace the original test failure. Track whether coverage collection has actually started and make shutdown/writing best-effort so the real failure is not lost.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/volume_sliders_mutes_test.py` around lines 68 - 74, Update the coverage
setup and cleanup in the test around cdp_session and its finally block to track
whether coverage collection successfully started, rather than using cdp_session
existence alone. Run coverage shutdown and disk writes only when collection
started, and make all cleanup/write failures non-fatal so they cannot replace
the primary test failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/volume_sliders_mutes_test.py`:
- Around line 68-74: Update the coverage setup and cleanup in the test around
cdp_session and its finally block to track whether coverage collection
successfully started, rather than using cdp_session existence alone. Run
coverage shutdown and disk writes only when collection started, and make all
cleanup/write failures non-fatal so they cannot replace the primary test
failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66a8e8bb-6c83-4889-a2fc-0516b1c3d2dd

📥 Commits

Reviewing files that changed from the base of the PR and between 3ba2027 and 04027dc.

📒 Files selected for processing (4)
  • tests/reset_audio_flow_test.py
  • tests/test_utils.py
  • tests/volume_sliders_mutes_test.py
  • workspace/run_browser_tests.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • workspace/run_browser_tests.sh
  • tests/reset_audio_flow_test.py
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: GUT Unit Tests / unit-test
  • GitHub Check: CI/CD Infrastructure Tests / Test Godot Asset Infrastructure and Signature Verification
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-19T05:07:07.286Z
Learnt from: ikostan
Repo: ikostan/SkyLockAssault PR: 488
File: tests/difficulty_flow_test.py:194-200
Timestamp: 2026-03-19T05:07:07.286Z
Learning: When writing/adjusting tests that assert SkyLockAssault log output for difficulty (and other float settings), expect the decimal point to be preserved (e.g., logs like "setting 'difficulty' updated to: 1.0"). Do not use regexes that fail on floats due to the decimal point (e.g., patterns with a negative lookahead that assumes digits contain no '.'), since they will not match "1.0". Instead, use a simple substring check for the expected log prefix/value, or use a float-aware regex (e.g., matching `\d+(?:\.\d+)?`) / parse the logged value as a float before asserting.

Applied to files:

  • tests/test_utils.py
  • tests/volume_sliders_mutes_test.py
🔇 Additional comments (4)
tests/test_utils.py (2)

17-24: LGTM!


35-40: LGTM!

tests/volume_sliders_mutes_test.py (2)

179-186: The broad save-log fallback still permits false positives.

The master and music waits can succeed on unrelated "saved" or "encrypted" logs, so they do not necessarily verify the corresponding AudioManager volume update. Restrict these predicates to the operation-specific log, or add the same scoped follow-up assertion used for SFX.

Also applies to: 234-241


5-11: LGTM!

Also applies to: 35-47, 56-66, 76-178, 187-233, 242-301, 302-475, 477-489

ikostan added 4 commits July 25, 2026 21:26
Addressed! Added deterministic `wait_for_console_log` synchronization following both `toggleMuteMaster([1])` and `toggleMuteSfx([1])` calls in `tests/audio_flow_test.py` to eliminate timing race conditions before subsequent volume adjustments.
Addressed! Updated `tests/volume_sliders_mutes_test.py` to track `coverage_started = True` only after CDP profiler commands complete successfully, and wrapped the `finally` block teardown and disk writes in a `try...except` block so coverage errors remain non-fatal and cannot mask primary test failures.
@ikostan

ikostan commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

tests/volume_sliders_mutes_test.py (1)

68-74: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Preserve the primary test failure during V8 coverage cleanup.
The coverage block starts cdp_session before the CDP commands finish, and the finally at tests/volume_sliders_mutes_test.py:490-496 unconditionally runs coverage shutdown and disk writes. Any setup, collection, shutdown, or write failure can overwrite or replace the original test failure. Track whether coverage collection has actually started and make shutdown/writing best-effort so the real failure is not lost.


Addressed! Updated tests/volume_sliders_mutes_test.py to track coverage_started = True only after CDP profiler commands complete successfully, and wrapped the finally block teardown and disk writes in a try...except block so coverage errors remain non-fatal and cannot mask primary test failures.

@ikostan

ikostan commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@ikostan

ikostan commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@sourcery-ai

sourcery-ai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Sorry @ikostan, your pull request is larger than the review limit of 150000 diff characters

@ikostan
ikostan merged commit a6b4d4e into main Jul 26, 2026
14 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Sky Lock Assault Project Jul 26, 2026
@ikostan
ikostan deleted the code-audits-asynchronous-refactoring branch July 26, 2026 05:10
@ikostan
ikostan restored the code-audits-asynchronous-refactoring branch July 26, 2026 05:30
@ikostan
ikostan deleted the code-audits-asynchronous-refactoring branch July 26, 2026 05:34
@ikostan ikostan linked an issue Aug 7, 2026 that may be closed by this pull request
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request python Pull requests that update python code QA refactoring testing

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[TASK] Code Audits & Asynchronous Refactoring [EPIC] Optimize Test Suite Runtime and Prevent CI Limit Exhaustion

1 participant