"""SP9 — macOS Keychain secret accessor tests.""" from __future__ import annotations from unittest.mock import patch import pytest from cyclone import secrets from cyclone.secrets import STUB_SECRET, get_secret, has_keyring, set_secret def test_has_keyring_true_when_lib_present(): # The test env has keyring installed assert has_keyring() is True def test_get_secret_returns_none_when_keyring_missing(monkeypatch): monkeypatch.setattr(secrets, "_HAS_KEYRING", False) monkeypatch.setattr(secrets, "keyring", None) assert get_secret("anything") is None def test_get_secret_returns_keychain_value(): with patch("cyclone.secrets.keyring") as mock_kr: mock_kr.get_password.return_value = "p@ssw0rd" v = get_secret("sftp.gainwell.password") assert v == "p@ssw0rd" mock_kr.get_password.assert_called_once_with("cyclone", "sftp.gainwell.password") def test_get_secret_returns_none_on_keychain_exception(): with patch("cyclone.secrets.keyring") as mock_kr: mock_kr.get_password.side_effect = RuntimeError("keychain locked") v = get_secret("x") assert v is None def test_set_secret_returns_true_on_success(): with patch("cyclone.secrets.keyring") as mock_kr: assert set_secret("a", "b") is True mock_kr.set_password.assert_called_once_with("cyclone", "a", "b") def test_set_secret_returns_false_when_keyring_missing(monkeypatch): monkeypatch.setattr(secrets, "_HAS_KEYRING", False) monkeypatch.setattr(secrets, "keyring", None) assert set_secret("a", "b") is False def test_set_secret_returns_false_on_keychain_exception(): with patch("cyclone.secrets.keyring") as mock_kr: mock_kr.set_password.side_effect = RuntimeError("denied") assert set_secret("a", "b") is False def test_stub_secret_is_distinct_string(): assert STUB_SECRET == "" assert STUB_SECRET != ""