Skip to content

test: fix flaky concurrent test β€” mock handler's local import#135

Merged
docdyhr merged 1 commit intomasterfrom
fix/concurrent-test-mock-handler-import
Apr 7, 2026
Merged

test: fix flaky concurrent test β€” mock handler's local import#135
docdyhr merged 1 commit intomasterfrom
fix/concurrent-test-mock-handler-import

Conversation

@docdyhr
Copy link
Copy Markdown
Owner

@docdyhr docdyhr commented Apr 7, 2026

Summary

  • test_concurrent_operations_workflow was failing locally because _get_apps_data() uses a by-value import (from versiontracker.apps import get_applications), so patching versiontracker.apps.get_applications had no effect β€” each thread ran the real system_profiler scan (74 apps) and timed out in < 10s.
  • Fix: patch versiontracker.handlers.app_handlers._get_apps_data directly so threads return immediately with mock data.
  • Raise thread.join timeout 10s β†’ 30s as an additional guard.

Test plan

  • pytest tests/test_end_to_end_integration.py::TestEndToEndIntegration::test_concurrent_operations_workflow passes in ~6s (was timing out)
  • Full test suite passes

πŸ€– Generated with Claude Code

Summary by Sourcery

Stabilize the concurrent operations end-to-end test by ensuring it uses mocked application data instead of hitting real system state and by relaxing thread join timeouts.

Bug Fixes:

  • Prevent the concurrent operations workflow test from performing real system application scans by correctly patching the app handler data source.

Tests:

  • Update the concurrent operations workflow integration test to mock handler-level app data and increase thread join timeout to reduce flakiness.

…odule

_get_apps_data() uses a by-value import so patching versiontracker.apps.get_applications
had no effect in threads; patch _get_apps_data directly instead. Also raise join
timeout 10s β†’ 30s as a belt-and-suspenders guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Apr 7, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts the concurrent operations end-to-end test to mock the handler’s local _get_apps_data import directly and increases the thread join timeout to reduce flakiness under concurrency.

File-Level Changes

Change Details Files
Ensure concurrent workflow test uses a fast, deterministic mock for application data instead of invoking the real handler logic.
  • Define a fixed mock_app_tuples list of sample application name/version pairs used as mock return data.
  • Wrap versiontracker_main invocation in a mock.patch of versiontracker.handlers.app_handlers._get_apps_data to return the mock_app_tuples, ensuring threads skip real system_profiler scans.
  • Retain the existing sys.argv patch while nesting it inside the new _get_apps_data patch context.
tests/test_end_to_end_integration.py
Reduce flakiness of thread completion by extending the join timeout in the concurrent workflow test.
  • Increase thread.join timeout from 10 seconds to 30 seconds when waiting for concurrent versiontracker threads to finish.
tests/test_end_to_end_integration.py

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

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Instead of hardcoding mock_app_tuples in the test, consider reusing or extending the existing mock_applications fixture so that test data stays centralized and consistent.
  • The increased thread.join(timeout=30) is a somewhat arbitrary magic number; consider adding a short comment explaining why 30 seconds is appropriate or extracting it to a named constant to clarify intent.
  • After thread.join, you may want to assert that no threads are still alive (e.g., assert not thread.is_alive()) to ensure timeouts are actually sufficient and detect hidden hangs early.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Instead of hardcoding `mock_app_tuples` in the test, consider reusing or extending the existing `mock_applications` fixture so that test data stays centralized and consistent.
- The increased `thread.join(timeout=30)` is a somewhat arbitrary magic number; consider adding a short comment explaining why 30 seconds is appropriate or extracting it to a named constant to clarify intent.
- After `thread.join`, you may want to assert that no threads are still alive (e.g., `assert not thread.is_alive()`) to ensure timeouts are actually sufficient and detect hidden hangs early.

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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

πŸ”’ Security Analysis Report

Security Analysis Report

Generated: Tue Apr 7 18:07:59 UTC 2026
Repository: docdyhr/versiontracker
Commit: bdacc1f

Bandit Security Scan

οΏ½[?25l
οΏ½[2KWorking... οΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m  0%οΏ½[0m οΏ½[36m-:--:--οΏ½[0m
οΏ½[2KWorking... οΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m  0%οΏ½[0m οΏ½[36m-:--:--οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m  6%οΏ½[0m οΏ½[36m-:--:--οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m 13%οΏ½[0m οΏ½[36m0:00:02οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━�[0mοΏ½[91mβ•ΈοΏ½[0mοΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m 22%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m 33%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m 48%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━━━━━━━━�[0m οΏ½[35m 61%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━━━━�[0m οΏ½[35m 70%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━━━�[0m οΏ½[35m 78%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━━━━�[0m οΏ½[35m 83%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0mοΏ½[90mβ•ΊοΏ½[0mοΏ½[90m━━━�[0m οΏ½[35m 91%οΏ½[0m οΏ½[36m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m100%οΏ½[0m οΏ½[33m0:00:01οΏ½[0m
οΏ½[2KWorking... οΏ½[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m οΏ½[35m100%οΏ½[0m οΏ½[33m0:00:01οΏ½[0m
οΏ½[?25hRun started:2026-04-07 18:08:00.930338+00:00

Test results:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Low
   CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b608_hardcoded_sql_expressions.html
   Location: versiontracker/advanced_cache.py:610:24
609	                # Use f-string for better readability
610	                msg = f"Failed to delete from cache {key}: {e}"
611	                raise CacheError(msg) from e

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/config.py:174:16
173	                cmd = f"{path} --version"
174	                import subprocess
175	

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/config.py:177:29
176	                try:
177	                    result = subprocess.run(cmd.split(), capture_output=True, timeout=2, check=False)
178	                    returncode = result.returncode

--------------------------------------------------
>> Issue: [B110:try_except_pass] Try, Except, Pass detected.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b110_try_except_pass.html
   Location: versiontracker/experimental/analytics.py:645:16
644	                    self.peak_cpu = max(self.peak_cpu, cpu_percent)
645	                except Exception:
646	                    pass
647	                time.sleep(0.05)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:493:8
492	        # Type assertion: apps and brews cannot be None here due to exit_code checks above
493	        assert apps is not None
494	        assert brews is not None

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:494:8
493	        assert apps is not None
494	        assert brews is not None
495	        apps = _filter_applications(apps, brews, include_brews)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:511:8
510	        # Type assertion: outdated_info cannot be None here due to exit_code check
511	        assert outdated_info is not None
512	        # Type cast: outdated_info cannot be None here due to exit_code check above

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/homebrew.py:12:0
11	import re
12	import subprocess
13	import time

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/macos_integration.py:11:0
10	import os
11	import subprocess
12	from pathlib import Path

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/macos_integration.py:245:21
244	            # nosec B603 - osascript with controlled arguments
245	            result = subprocess.run(cmd, capture_output=True, text=True)
246	

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/menubar_app.py:8:0
7	import logging
8	import subprocess
9	import sys

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/plugins/example_plugins.py:322:16
321	            try:
322	                import subprocess
323	

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/utils.py:15:0
14	import shutil
15	import subprocess
16	import sys

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:784:17
783	    try:
784	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
785	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:784:17
783	    try:
784	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
785	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:800:17
799	    try:
800	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
801	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:800:17
799	    try:
800	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
801	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:833:15
832	    try:
833	        return subprocess.run(command, capture_output=True, text=True, timeout=timeout, check=check)
834	    except subprocess.TimeoutExpired as e:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/batch.py:11:0
10	import logging
11	import subprocess
12	from concurrent.futures import ThreadPoolExecutor

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/homebrew.py:18:0
17	import logging
18	import subprocess
19	

--------------------------------------------------

Code scanned:
	Total lines of code: 15020
	Total lines skipped (#nosec): 0
	Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 33

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 21
		Medium: 1
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 1
		Medium: 0
		High: 21
Files skipped (0):

Safety Check Results



οΏ½[33mοΏ½[1m+===========================================================================================================================================================================================+οΏ½[0m


οΏ½[31mοΏ½[1mDEPRECATED: οΏ½[0mοΏ½[33mοΏ½[1mthis command (`check`) has been DEPRECATED, and will be unsupported beyond 01 June 2024.οΏ½[0m


οΏ½[32mWe highly encourage switching to the new οΏ½[0mοΏ½[32mοΏ½[1m`scan`οΏ½[0mοΏ½[32m command which is easier to use, more powerful, and can be set up to mimic the deprecated command if required.οΏ½[0m


οΏ½[33mοΏ½[1m+===========================================================================================================================================================================================+οΏ½[0m


+==============================================================================+

                               /$$$$$$            /$$
                              /$$__  $$          | $$
           /$$$$$$$  /$$$$$$ | $$  \__//$$$$$$  /$$$$$$   /$$   /$$
          /$$_____/ |____  $$| $$$$   /$$__  $$|_  $$_/  | $$  | $$
         |  $$$$$$   /$$$$$$$| $$_/  | $$$$$$$$  | $$    | $$  | $$
          \____  $$ /$$__  $$| $$    | $$_____/  | $$ /$$| $$  | $$
          /$$$$$$$/|  $$$$$$$| $$    |  $$$$$$$  |  $$$$/|  $$$$$$$
         |_______/  \_______/|__/     \_______/   \___/   \____  $$
                                                          /$$  | $$
                                                         |  $$$$$$/
  by safetycli.com                                        \______/

+==============================================================================+

 οΏ½[1mREPORTοΏ½[0m 

  Safety οΏ½[1mv3.7.0οΏ½[0m is scanning for οΏ½[1mVulnerabilitiesοΏ½[0mοΏ½[1m...οΏ½[0m
οΏ½[1m  Scanning dependenciesοΏ½[0m in your οΏ½[1menvironment:οΏ½[0m

  -> /opt/hostedtoolcache/Python/3.13.12/x64/lib/python3.13/site-packages

  Using οΏ½[1mopen-source vulnerability databaseοΏ½[0m
οΏ½[1m  Found and scanned 106 packagesοΏ½[0m
  Timestamp οΏ½[1m2026-04-07 18:08:02οΏ½[0m
οΏ½[1m  0οΏ½[0mοΏ½[1m vulnerabilities reportedοΏ½[0m
οΏ½[1m  0οΏ½[0mοΏ½[1m vulnerabilities ignoredοΏ½[0m
+==============================================================================+

 οΏ½[32mοΏ½[1mNo known security vulnerabilities reported.οΏ½[0m 

+==============================================================================+οΏ½[0m


οΏ½[33mοΏ½[1m+===========================================================================================================================================================================================+οΏ½[0m


οΏ½[31mοΏ½[1mDEPRECATED: οΏ½[0mοΏ½[33mοΏ½[1mthis command (`check`) has been DEPRECATED, and will be unsupported beyond 01 June 2024.οΏ½[0m


οΏ½[32mWe highly encourage switching to the new οΏ½[0mοΏ½[32mοΏ½[1m`scan`οΏ½[0mοΏ½[32m command which is easier to use, more powerful, and can be set up to mimic the deprecated command if required.οΏ½[0m


οΏ½[33mοΏ½[1m+===========================================================================================================================================================================================+οΏ½[0m


Pip-Audit Results

οΏ½[?25lοΏ½[32m-οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m-οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m-οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m-οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting inputs
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting aiohappyeyeballs (2.6.1)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing aiohappyeyeballs (2.6.1)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting aiohttp (3.13.5)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing aiohttp (3.13.5)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting aiosignal (1.4.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing aiosignal (1.4.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting annotated-doc (0.0.4)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing annotated-doc (0.0.4)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting annotated-types (0.7.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing annotated-types (0.7.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting anyio (4.13.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting anyio (4.13.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing anyio (4.13.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting attrs (26.1.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing attrs (26.1.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting Authlib (1.6.9)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing Authlib (1.6.9)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting bandit (1.9.4)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing bandit (1.9.4)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting black (26.3.1)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing black (26.3.1)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting boolean.py (5.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing boolean.py (5.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting build (1.4.2)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing build (1.4.2)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting CacheControl (0.14.4)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing CacheControl (0.14.4)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting certifi (2026.2.25)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing certifi (2026.2.25)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting cffi (2.0.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing cffi (2.0.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting charset-normalizer (3.4.7)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing charset-normalizer (3.4.7)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting click (8.3.2)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing click (8.3.2)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting coverage (7.13.5)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing coverage (7.13.5)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting cryptography (46.0.6)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing cryptography (46.0.6)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting cyclonedx-python-lib (11.7.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Auditing cyclonedx-python-lib (11.7.0)
οΏ½[2KοΏ½[32m\οΏ½[0m Collecting defusedxml (0.7.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing defusedxml (0.7.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting docutils (0.22.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing docutils (0.22.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting dparse (0.6.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing dparse (0.6.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting filelock (3.25.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing filelock (3.25.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting frozenlist (1.8.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing frozenlist (1.8.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting h11 (0.16.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing h11 (0.16.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting httpcore (1.0.9)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing httpcore (1.0.9)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting httpx (0.28.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing httpx (0.28.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting id (1.6.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting id (1.6.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing id (1.6.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting idna (3.11)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing idna (3.11)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting iniconfig (2.3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing iniconfig (2.3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting jaraco.classes (3.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing jaraco.classes (3.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting jaraco.context (6.1.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing jaraco.context (6.1.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting jaraco.functools (4.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing jaraco.functools (4.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting jeepney (0.9.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing jeepney (0.9.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting Jinja2 (3.1.6)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing Jinja2 (3.1.6)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting joblib (1.5.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing joblib (1.5.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting keyring (25.7.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing keyring (25.7.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting librt (0.8.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing librt (0.8.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting license-expression (30.4.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing license-expression (30.4.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting macversiontracker (1.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing macversiontracker (1.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting markdown-it-py (4.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing markdown-it-py (4.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting MarkupSafe (3.0.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing MarkupSafe (3.0.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting marshmallow (4.3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing marshmallow (4.3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting mdurl (0.1.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing mdurl (0.1.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting more-itertools (11.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing more-itertools (11.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting msgpack (1.1.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing msgpack (1.1.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting multidict (6.7.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing multidict (6.7.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting mypy (1.20.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing mypy (1.20.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting mypy_extensions (1.1.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing mypy_extensions (1.1.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting nh3 (0.3.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing nh3 (0.3.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting nltk (3.9.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing nltk (3.9.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting packageurl-python (0.17.6)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing packageurl-python (0.17.6)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting packaging (26.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing packaging (26.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pathspec (1.0.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pathspec (1.0.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pip (26.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pip (26.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pip (26.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pip-api (0.0.34)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pip-api (0.0.34)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pip_audit (2.10.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pip_audit (2.10.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pip-requirements-parser (32.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pip-requirements-parser (32.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting platformdirs (4.9.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing platformdirs (4.9.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pluggy (1.6.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pluggy (1.6.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting propcache (0.4.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing propcache (0.4.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting psutil (7.2.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing psutil (7.2.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting py-serializable (2.1.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing py-serializable (2.1.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pycparser (3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pycparser (3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pydantic (2.12.5)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pydantic (2.12.5)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pydantic_core (2.41.5)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pydantic_core (2.41.5)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting Pygments (2.20.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing Pygments (2.20.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pyparsing (3.3.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pyparsing (3.3.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pyproject_hooks (1.2.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pyproject_hooks (1.2.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pytest (9.0.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pytest (9.0.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pytest-asyncio (1.3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pytest-asyncio (1.3.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pytest-cov (7.1.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pytest-cov (7.1.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pytest-mock (3.15.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pytest-mock (3.15.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pytest-timeout (2.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pytest-timeout (2.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting pytokens (0.4.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing pytokens (0.4.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting PyYAML (6.0.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing PyYAML (6.0.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting readme_renderer (44.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing readme_renderer (44.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting regex (2026.4.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing regex (2026.4.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting requests (2.33.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing requests (2.33.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting requests-toolbelt (1.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing requests-toolbelt (1.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting rfc3986 (2.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting rfc3986 (2.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing rfc3986 (2.0.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting rich (14.3.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing rich (14.3.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting ruamel.yaml (0.19.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing ruamel.yaml (0.19.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting ruff (0.15.9)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing ruff (0.15.9)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting safety (3.7.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing safety (3.7.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting safety-schemas (0.0.16)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing safety-schemas (0.0.16)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting SecretStorage (3.5.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing SecretStorage (3.5.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting setuptools (82.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing setuptools (82.0.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting shellingham (1.5.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing shellingham (1.5.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting sortedcontainers (2.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing sortedcontainers (2.4.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting stevedore (5.7.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing stevedore (5.7.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting tabulate (0.10.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing tabulate (0.10.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting tenacity (9.1.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing tenacity (9.1.4)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting tomli (2.4.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing tomli (2.4.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting tomli_w (1.2.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing tomli_w (1.2.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting tomlkit (0.14.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing tomlkit (0.14.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting tqdm (4.67.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing tqdm (4.67.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting twine (6.2.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing twine (6.2.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting typer (0.24.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing typer (0.24.1)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting types-PyYAML (6.0.12.20250915)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing types-PyYAML (6.0.12.20250915)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting typing_extensions (4.15.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing typing_extensions (4.15.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting typing-inspection (0.4.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing typing-inspection (0.4.2)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting urllib3 (2.6.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing urllib3 (2.6.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting wheel (0.46.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing wheel (0.46.3)
οΏ½[2KοΏ½[32m|οΏ½[0m Collecting yarl (1.23.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing yarl (1.23.0)
οΏ½[2KοΏ½[32m|οΏ½[0m Auditing yarl (1.23.0)
οΏ½[?25h
οΏ½[1AοΏ½[2K```

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 790ff9c286

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

result = versiontracker_main()
results.append(result)
# Patch the handler's local import (imported by value, so source-module mock doesn't intercept)
with mock.patch("versiontracker.handlers.app_handlers._get_apps_data", return_value=mock_app_tuples):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply _get_apps_data patch once outside worker threads

Patching versiontracker.handlers.app_handlers._get_apps_data inside each thread is racy: unittest.mock.patch stores/restores the target value per context, so when one thread exits its with block it can restore the real function while other threads are still running. In that window, another thread can call the real _get_apps_data (and trigger system_profiler), reintroducing the same timeout flake this commit is trying to fix. Patch this symbol once around thread startup/join so all workers see a stable mock for the full test duration.

Useful? React with πŸ‘Β / πŸ‘Ž.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

βœ… All modified and coverable lines are covered by tests.

πŸ“’ Thoughts on this report? Let us know!

@docdyhr docdyhr merged commit 9889899 into master Apr 7, 2026
31 checks passed
@docdyhr docdyhr deleted the fix/concurrent-test-mock-handler-import branch April 7, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant