09e9f3545f
Test / Code Quality (push) Has been cancelled
Test / Test (macos-latest, Python 3.10) (push) Has been cancelled
Test / Test (macos-latest, Python 3.11) (push) Has been cancelled
Test / Test (macos-latest, Python 3.12) (push) Has been cancelled
Test / Test (macos-latest, Python 3.13) (push) Has been cancelled
Test / Test (macos-latest, Python 3.14) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.10) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.11) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.12) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.13) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.14) (push) Has been cancelled
Test / Test (windows-latest, Python 3.10) (push) Has been cancelled
Test / Test (windows-latest, Python 3.11) (push) Has been cancelled
Test / Test (windows-latest, Python 3.12) (push) Has been cancelled
Test / Test (windows-latest, Python 3.13) (push) Has been cancelled
Test / Test (windows-latest, Python 3.14) (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
dependency-audit / pip-audit (push) Has been cancelled
89 lines
2.9 KiB
Python
89 lines
2.9 KiB
Python
"""Tests for encoding-safe CLI output helpers."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import notebooklm.cli._encoding as encoding_module
|
|
from notebooklm.cli._encoding import replace_unencodable, safe_echo
|
|
|
|
|
|
class DummyStream:
|
|
def __init__(self, encoding):
|
|
self.encoding = encoding
|
|
|
|
|
|
class TestReplaceUnencodable:
|
|
def test_preserves_text_encodable_by_stream(self):
|
|
assert replace_unencodable("plain text", DummyStream("cp950")) == "plain text"
|
|
|
|
def test_replaces_text_not_encodable_by_stream(self):
|
|
assert replace_unencodable("web 🌐", DummyStream("cp950")) == "web ?"
|
|
|
|
def test_uses_utf8_when_stream_encoding_is_missing(self):
|
|
assert replace_unencodable("web 🌐", DummyStream(None)) == "web 🌐"
|
|
|
|
def test_uses_utf8_when_stream_is_none(self):
|
|
assert replace_unencodable("web 🌐", None) == "web 🌐"
|
|
|
|
def test_unknown_encoding_falls_back_to_ascii_replacement(self):
|
|
assert replace_unencodable("web 🌐", DummyStream("not-a-codec")) == "web ?"
|
|
|
|
|
|
class TestSafeEcho:
|
|
def test_echoes_original_message_when_first_write_succeeds(self):
|
|
with patch.object(encoding_module.click, "echo") as mock_echo:
|
|
safe_echo("web 🌐")
|
|
|
|
mock_echo.assert_called_once_with("web 🌐", err=False)
|
|
|
|
def test_replaces_stdout_text_when_first_write_cannot_encode(self):
|
|
class DummyStdout:
|
|
encoding = "cp950"
|
|
|
|
calls = []
|
|
|
|
def flaky_echo(message=None, **kwargs):
|
|
if not calls:
|
|
calls.append((message, kwargs.get("err", False)))
|
|
raise UnicodeEncodeError(
|
|
"cp950",
|
|
str(message),
|
|
4,
|
|
5,
|
|
"illegal multibyte sequence",
|
|
)
|
|
calls.append((message, kwargs.get("err", False)))
|
|
|
|
with (
|
|
patch.object(encoding_module.click, "echo", side_effect=flaky_echo),
|
|
patch.object(encoding_module.sys, "stdout", DummyStdout()),
|
|
):
|
|
safe_echo("web 🌐")
|
|
|
|
assert calls == [("web 🌐", False), ("web ?", False)]
|
|
|
|
def test_replaces_stderr_text_when_first_write_cannot_encode(self):
|
|
class DummyStderr:
|
|
encoding = "cp950"
|
|
|
|
calls = []
|
|
|
|
def flaky_echo(message=None, **kwargs):
|
|
if not calls:
|
|
calls.append((message, kwargs.get("err", False)))
|
|
raise UnicodeEncodeError(
|
|
"cp950",
|
|
str(message),
|
|
4,
|
|
5,
|
|
"illegal multibyte sequence",
|
|
)
|
|
calls.append((message, kwargs.get("err", False)))
|
|
|
|
with (
|
|
patch.object(encoding_module.click, "echo", side_effect=flaky_echo),
|
|
patch.object(encoding_module.sys, "stderr", DummyStderr()),
|
|
):
|
|
safe_echo("web 🌐", err=True)
|
|
|
|
assert calls == [("web 🌐", True), ("web ?", True)]
|