fix: use OS trust store for TLS verification via truststore
Regression from #62: verify_ssl true used to resolve to the system CA bundle path, so corporate CAs installed in the OS worked; after the unification true became requests' default (certifi), breaking setups with a corporate CA in the system store. Now verify_ssl true injects truststore, so requests verifies against the OS trust store on any platform. verify_ssl false / custom CA path behavior is unchanged. Tests mock truststore via an autouse fixture to keep the pytest process free of global ssl mutation. Refs #62
This commit is contained in:
@@ -19,6 +19,18 @@ def _configure_current_user(mock_redmine, user_id=1):
|
||||
mock_redmine.user.get.return_value = mock_user
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_truststore():
|
||||
"""Мок truststore для всех тестов клиента.
|
||||
|
||||
При verify=True _create_redmine() вызывает truststore.inject_into_ssl(),
|
||||
который глобально подменяет ssl.SSLContext без восстановления (#62).
|
||||
Не даём реальной инъекции выполниться в pytest-процессе.
|
||||
"""
|
||||
with mock.patch("redmine_reporter.client.truststore") as m:
|
||||
yield m
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, PASSWORD_ENV, clear=True)
|
||||
@mock.patch("redmine_reporter.client.Redmine")
|
||||
def test_fetch_aggregates_hours_per_issue(mock_redmine_class):
|
||||
@@ -172,6 +184,70 @@ def test_fetch_uses_custom_verify_path(mock_redmine_class):
|
||||
assert kwargs["requests"]["verify"] == "/tmp/redmine-ca.pem"
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, {**PASSWORD_ENV, "REDMINE_VERIFY": "true"}, clear=True)
|
||||
@mock.patch("redmine_reporter.client.Redmine")
|
||||
def test_verify_true_injects_system_trust_store(mock_redmine_class, mock_truststore):
|
||||
"""verify=True → truststore.inject_into_ssl() вызван, verify=True в Redmine (#62)."""
|
||||
mock_redmine = mock_redmine_class.return_value
|
||||
_configure_current_user(mock_redmine)
|
||||
mock_redmine.time_entry.filter.return_value = []
|
||||
|
||||
fetch_issues_with_spent_time("2026-01-01", "2026-01-31")
|
||||
|
||||
mock_truststore.inject_into_ssl.assert_called_once_with()
|
||||
_, kwargs = mock_redmine_class.call_args
|
||||
assert kwargs["requests"]["verify"] is True
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, {**PASSWORD_ENV, "REDMINE_VERIFY": "false"}, clear=True)
|
||||
@mock.patch("redmine_reporter.client.Redmine")
|
||||
def test_verify_false_skips_truststore_injection(mock_redmine_class, mock_truststore):
|
||||
"""verify=False → truststore.inject_into_ssl() не вызывается."""
|
||||
mock_redmine = mock_redmine_class.return_value
|
||||
_configure_current_user(mock_redmine)
|
||||
mock_redmine.time_entry.filter.return_value = []
|
||||
|
||||
fetch_issues_with_spent_time("2026-01-01", "2026-01-31")
|
||||
|
||||
mock_truststore.inject_into_ssl.assert_not_called()
|
||||
_, kwargs = mock_redmine_class.call_args
|
||||
assert kwargs["requests"]["verify"] is False
|
||||
|
||||
|
||||
@mock.patch.dict(
|
||||
os.environ, {**PASSWORD_ENV, "REDMINE_VERIFY": "/tmp/redmine-ca.pem"}, clear=True
|
||||
)
|
||||
@mock.patch("redmine_reporter.client.Redmine")
|
||||
def test_verify_custom_path_skips_truststore_injection(
|
||||
mock_redmine_class, mock_truststore
|
||||
):
|
||||
"""verify=<путь к CA-bundle> → truststore.inject_into_ssl() не вызывается."""
|
||||
mock_redmine = mock_redmine_class.return_value
|
||||
_configure_current_user(mock_redmine)
|
||||
mock_redmine.time_entry.filter.return_value = []
|
||||
|
||||
fetch_issues_with_spent_time("2026-01-01", "2026-01-31")
|
||||
|
||||
mock_truststore.inject_into_ssl.assert_not_called()
|
||||
_, kwargs = mock_redmine_class.call_args
|
||||
assert kwargs["requests"]["verify"] == "/tmp/redmine-ca.pem"
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, PASSWORD_ENV, clear=True)
|
||||
@mock.patch("redmine_reporter.client.Redmine")
|
||||
def test_verify_default_injects_system_trust_store(mock_redmine_class, mock_truststore):
|
||||
"""Без REDMINE_VERIFY verify по умолчанию True → inject вызван (#62)."""
|
||||
mock_redmine = mock_redmine_class.return_value
|
||||
_configure_current_user(mock_redmine)
|
||||
mock_redmine.time_entry.filter.return_value = []
|
||||
|
||||
fetch_issues_with_spent_time("2026-01-01", "2026-01-31")
|
||||
|
||||
mock_truststore.inject_into_ssl.assert_called_once_with()
|
||||
_, kwargs = mock_redmine_class.call_args
|
||||
assert kwargs["requests"]["verify"] is True
|
||||
|
||||
|
||||
@mock.patch.dict(os.environ, PASSWORD_ENV, clear=True)
|
||||
@mock.patch("redmine_reporter.client.Redmine")
|
||||
def test_fetch_raises_redmine_api_error_on_auth(mock_redmine_class):
|
||||
|
||||
Reference in New Issue
Block a user