Files
hate_crack/tests/test_version_check.py
T
Justin Bollinger 4a7f0724d9 feat: add startup version check, fix PassGPT MPS/output issues, hide menu without ML deps
- Add optional startup version check against GitHub releases (check_for_updates config option)
- Add packaging dependency for version comparison
- Fix PassGPT OOM on MPS by capping batch size to 64 and setting memory watermark limits
- Fix PassGPT output having spaces between every character
- Hide PassGPT menu item (17) unless torch/transformers are installed
- Fix mypy errors in passgpt_generate.py with type: ignore comments
- Update README with version check docs, optional ML deps section, and PassGPT CLI options
- Add test_version_check.py with 8 tests covering update check behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:32:40 -05:00

120 lines
4.3 KiB
Python

"""Tests for the startup version check feature."""
import json
from unittest.mock import MagicMock, patch
import pytest
@pytest.fixture
def hc_module():
"""Load hate_crack.main with SKIP_INIT enabled."""
import os
import importlib
os.environ["HATE_CRACK_SKIP_INIT"] = "1"
mod = importlib.import_module("hate_crack.main")
return mod
class TestCheckForUpdates:
"""Tests for check_for_updates()."""
def test_newer_version_prints_update_notice(self, hc_module, capsys):
mock_resp = MagicMock()
mock_resp.json.return_value = {"tag_name": "v99.0.0"}
mock_resp.raise_for_status = MagicMock()
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", True
):
mock_requests.get.return_value = mock_resp
hc_module.check_for_updates()
output = capsys.readouterr().out
assert "Update available: 99.0.0" in output
assert "github.com/trustedsec/hate_crack/releases" in output
def test_same_version_prints_nothing(self, hc_module, capsys):
from hate_crack import __version__
local_base = __version__.split("+")[0]
mock_resp = MagicMock()
mock_resp.json.return_value = {"tag_name": f"v{local_base}"}
mock_resp.raise_for_status = MagicMock()
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", True
):
mock_requests.get.return_value = mock_resp
hc_module.check_for_updates()
output = capsys.readouterr().out
assert "Update available" not in output
def test_older_version_prints_nothing(self, hc_module, capsys):
mock_resp = MagicMock()
mock_resp.json.return_value = {"tag_name": "v0.0.1"}
mock_resp.raise_for_status = MagicMock()
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", True
):
mock_requests.get.return_value = mock_resp
hc_module.check_for_updates()
output = capsys.readouterr().out
assert "Update available" not in output
def test_network_error_silently_handled(self, hc_module, capsys):
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", True
):
mock_requests.get.side_effect = ConnectionError("no network")
hc_module.check_for_updates()
output = capsys.readouterr().out
assert "Update available" not in output
assert "Error" not in output
def test_requests_unavailable_skips_check(self, hc_module, capsys):
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", False
):
hc_module.check_for_updates()
mock_requests.get.assert_not_called()
def test_config_disabled_skips_check(self, hc_module):
"""Verify that check_for_updates_enabled=False prevents the call in main()."""
# The config flag is checked in main() before calling check_for_updates().
# We verify the flag loads correctly from config.
assert hasattr(hc_module, "check_for_updates_enabled")
def test_tag_without_v_prefix(self, hc_module, capsys):
mock_resp = MagicMock()
mock_resp.json.return_value = {"tag_name": "99.0.0"}
mock_resp.raise_for_status = MagicMock()
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", True
):
mock_requests.get.return_value = mock_resp
hc_module.check_for_updates()
output = capsys.readouterr().out
assert "Update available: 99.0.0" in output
def test_empty_tag_name_handled(self, hc_module, capsys):
mock_resp = MagicMock()
mock_resp.json.return_value = {"tag_name": ""}
mock_resp.raise_for_status = MagicMock()
with patch.object(hc_module, "requests") as mock_requests, patch.object(
hc_module, "REQUESTS_AVAILABLE", True
):
mock_requests.get.return_value = mock_resp
hc_module.check_for_updates()
output = capsys.readouterr().out
assert "Update available" not in output