import pytest from hermes_cli.secret_prompt import _collect_masked_input, masked_secret_prompt def _run_collect(chars: str): output: list[str] = [] iterator = iter(chars) def read_char() -> str: return next(iterator, "") def write(text: str) -> None: output.append(text) value = _collect_masked_input( read_char, write, "API key: ", ) return value, "".join(output) def test_collect_masked_input_shows_feedback_without_echoing_secret(): value, output = _run_collect("secret\n") assert value == "secret" assert output == "API key: ******\n" assert "secret" not in output def test_collect_masked_input_handles_backspace(): value, output = _run_collect("sec\x7fret\r") assert value == "seret" assert output == "API key: ***\b \b***\n" assert "secret" not in output def test_collect_masked_input_raises_keyboard_interrupt(): output: list[str] = [] with pytest.raises(KeyboardInterrupt): _collect_masked_input( lambda: "\x03", output.append, "API key: ", ) assert "".join(output) == "API key: \n" def test_masked_secret_prompt_falls_back_to_getpass_for_non_tty(monkeypatch): class NonTty: def isatty(self): return False monkeypatch.setattr("sys.stdin", NonTty()) monkeypatch.setattr("sys.stdout", NonTty()) monkeypatch.setattr("getpass.getpass", lambda prompt: f"value from {prompt}") assert masked_secret_prompt("API key: ") == "value from API key: "