Files
soxoj--maigret/tests/test_standalone_wrapper.py
wehub-resource-sync e52ddb7dd4
CodeQL / Analyze (python) (push) Failing after 1s
Linting and testing / build (3.11) (push) Failing after 0s
Package exe with PyInstaller - Windows / build (push) Failing after 2s
Linting and testing / build (3.10) (push) Failing after 1s
Linting and testing / minimal-install (push) Failing after 1s
Update sites rating and statistics / build (push) Failing after 1s
Build docker image and push to DockerHub / docker (push) Failing after 1s
Linting and testing / build (3.12) (push) Failing after 1s
Linting and testing / build (3.14) (push) Failing after 1s
Linting and testing / build (3.13) (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:10:17 +08:00

150 lines
5.9 KiB
Python

"""Unit tests for the PyInstaller Windows wrapper.
The wrapper at ``pyinstaller/maigret_standalone.py`` lives outside the
``maigret`` package, so we load it by path.
"""
import importlib.util
import os
import sys
from unittest import mock
import pytest
WRAPPER_PATH = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
"pyinstaller",
"maigret_standalone.py",
)
@pytest.fixture
def wrapper():
spec = importlib.util.spec_from_file_location(
"maigret_standalone_under_test", WRAPPER_PATH
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_returns_false_on_non_windows(wrapper):
with mock.patch.object(wrapper.platform, "system", return_value="Darwin"):
assert wrapper._launched_by_double_click() is False
def test_returns_false_when_username_argument_present(wrapper):
# Extra argv => user invoked from a shell with a username; never a double-click.
with mock.patch.object(wrapper.platform, "system", return_value="Windows"), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe", "alice"]):
assert wrapper._launched_by_double_click() is False
def test_double_click_detected_when_only_our_process_attached(wrapper):
fake_ctypes = mock.MagicMock()
fake_ctypes.windll.kernel32.GetConsoleProcessList.return_value = 1
with mock.patch.object(wrapper.platform, "system", return_value="Windows"), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe"]), \
mock.patch.dict(sys.modules, {"ctypes": fake_ctypes}):
assert wrapper._launched_by_double_click() is True
def test_cmd_invocation_detected_when_shell_also_attached(wrapper):
fake_ctypes = mock.MagicMock()
fake_ctypes.windll.kernel32.GetConsoleProcessList.return_value = 2
with mock.patch.object(wrapper.platform, "system", return_value="Windows"), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe"]), \
mock.patch.dict(sys.modules, {"ctypes": fake_ctypes}):
assert wrapper._launched_by_double_click() is False
def test_returns_false_when_console_api_raises(wrapper):
fake_ctypes = mock.MagicMock()
fake_ctypes.windll.kernel32.GetConsoleProcessList.side_effect = OSError("boom")
with mock.patch.object(wrapper.platform, "system", return_value="Windows"), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe"]), \
mock.patch.dict(sys.modules, {"ctypes": fake_ctypes}):
assert wrapper._launched_by_double_click() is False
def test_main_runs_cli_when_not_double_click(wrapper):
sentinel = object()
with mock.patch.object(wrapper, "_launched_by_double_click", return_value=False), \
mock.patch.object(wrapper.asyncio, "run") as run_mock, \
mock.patch.object(wrapper.maigret, "cli", new=mock.Mock(return_value=sentinel)) as cli_mock:
wrapper.main()
cli_mock.assert_called_once_with()
run_mock.assert_called_once_with(sentinel)
def test_main_prompts_and_runs_search_on_double_click(wrapper):
sentinel = object()
original_argv = ["maigret_standalone.exe"]
inputs = iter(["alice", ""]) # username prompt, then pause prompt
with mock.patch.object(wrapper, "_launched_by_double_click", return_value=True), \
mock.patch.object(wrapper.sys, "argv", list(original_argv)), \
mock.patch("builtins.input", side_effect=lambda *_: next(inputs)) as input_mock, \
mock.patch.object(wrapper.asyncio, "run") as run_mock, \
mock.patch.object(wrapper.maigret, "cli", new=mock.Mock(return_value=sentinel)):
wrapper.main()
# argv was rewritten to feed argparse the entered username.
assert wrapper.sys.argv == ["maigret_standalone.exe", "alice"]
run_mock.assert_called_once_with(sentinel)
assert input_mock.call_count == 2 # username prompt + final pause
def test_main_skips_search_on_empty_username(wrapper):
inputs = iter(["", ""]) # empty username, then pause prompt
with mock.patch.object(wrapper, "_launched_by_double_click", return_value=True), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe"]), \
mock.patch("builtins.input", side_effect=lambda *_: next(inputs)) as input_mock, \
mock.patch.object(wrapper.asyncio, "run") as run_mock, \
mock.patch.object(wrapper.maigret, "cli") as cli_mock:
wrapper.main()
cli_mock.assert_not_called()
run_mock.assert_not_called()
assert input_mock.call_count == 2 # asked for username, then paused
def test_main_pauses_even_when_cli_raises_system_exit(wrapper):
inputs = iter(["alice", ""])
with mock.patch.object(wrapper, "_launched_by_double_click", return_value=True), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe"]), \
mock.patch("builtins.input", side_effect=lambda *_: next(inputs)) as input_mock, \
mock.patch.object(wrapper.asyncio, "run", side_effect=SystemExit(2)), \
mock.patch.object(wrapper.maigret, "cli", new=mock.Mock()):
with pytest.raises(SystemExit):
wrapper.main()
# Both inputs should have fired: username prompt + final pause via finally.
assert input_mock.call_count == 2
def test_main_treats_keyboard_interrupt_at_prompt_as_empty(wrapper):
def fake_input(*_args, **_kwargs):
if fake_input.calls == 0:
fake_input.calls += 1
raise KeyboardInterrupt
fake_input.calls += 1
return ""
fake_input.calls = 0
with mock.patch.object(wrapper, "_launched_by_double_click", return_value=True), \
mock.patch.object(wrapper.sys, "argv", ["maigret_standalone.exe"]), \
mock.patch("builtins.input", side_effect=fake_input), \
mock.patch.object(wrapper.asyncio, "run") as run_mock, \
mock.patch.object(wrapper.maigret, "cli") as cli_mock:
wrapper.main()
cli_mock.assert_not_called()
run_mock.assert_not_called()