Files
cyclone/backend/tests/test_secrets.py
Tyler c62826daea feat(sp9): multi-payer, multi-NPI, SFTP stub for dzinesco/TOC
- providers table seeded with 3 NPIs (Montrose, Delta, Salida)
- payers + payer_configs (per-tx config) join table
- clearhouse singleton (dzinesco identity, SFTP block, filename block)
- config/payers.yaml loader with Pydantic schema validation
- cyclone/edi/filenames.py: HCPF X12 File Naming Standards helpers
- cyclone/clearhouse/sftp.py: stub that copies to ./var/sftp/staging/...
- cyclone/secrets.py: macOS Keychain wrapper via keyring
- 11 new validation rules R200-R210 (CO MAP + HCPF naming)
- 6 new API endpoints (/api/clearhouse/*, /api/config/*, /api/admin/reload-config)
- migration 0007
- 72 new tests (646 total passing, was 574)

See spec: docs/superpowers/specs/2026-06-20-cyclone-multi-payer-npi-sftp-design.md
2026-06-20 23:07:31 -06:00

60 lines
1.9 KiB
Python

"""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 == "<stub-secret>"
assert STUB_SECRET != ""