test(cron): regression coverage for Windows encoding cluster

- CJK/emoji round-trip + human-readable jobs.json (PRs #52302/#29754)
- emoji through no_agent script stdout capture (issue #42384)
- truncated/invalid UTF-8 script stdout must not raise (#47393)
This commit is contained in:
Teknium
2026-08-08 11:57:45 -07:00
parent 4af7f05507
commit f1c13377a3
2 changed files with 69 additions and 0 deletions

View File

@@ -200,6 +200,51 @@ class TestRunJobScript:
assert "encoding" not in captured["kwargs"]
assert "errors" not in captured["kwargs"]
def test_emoji_stdout_round_trips_through_script_capture(self, cron_env):
"""Emoji in script stdout must reach the caller intact (#42384).
On Windows the fix is the utf-8 + errors='replace' popen kwargs
(asserted above); on POSIX the UTF-8 locale default must already
carry emoji through. Either way the delivery content is the real
text, never an exception.
"""
from cron.scheduler import _run_job_script
script = cron_env / "scripts" / "emoji.py"
script.write_text(
'import sys\n'
'sys.stdout.buffer.write("backup done \\N{PARTY POPPER} 日次".encode("utf-8"))\n',
encoding="utf-8",
)
success, output = _run_job_script("emoji.py")
assert success is True
assert "backup done 🎉 日次" == output
def test_invalid_utf8_stdout_does_not_raise(self, cron_env):
"""Truncated/invalid UTF-8 in script stdout must never escape as an
exception (#47393) — a raised UnicodeDecodeError higher up would
silently drop the whole delivery (#42384). The run may fail, but it
must fail as a (False, message) result the scheduler can deliver.
"""
from cron.scheduler import _run_job_script
script = cron_env / "scripts" / "bad_bytes.py"
# b'\xe6\x97' is the first two bytes of a three-byte CJK sequence —
# a truncated write, exactly the shape reported in #47393.
script.write_text(
"import sys\n"
"sys.stdout.buffer.write(b'partial \\xe6\\x97')\n",
encoding="utf-8",
)
success, output = _run_job_script("bad_bytes.py") # must not raise
assert isinstance(success, bool)
assert isinstance(output, str)
assert output # a message is always produced, never a silent drop
class TestBuildJobPromptWithScript:
"""Test that script output is injected into the prompt."""

View File

@@ -190,6 +190,30 @@ def tmp_cron_dir(tmp_path, monkeypatch):
class TestJobCRUD:
def test_cjk_and_emoji_round_trip_readable_in_jobs_json(self, tmp_cron_dir):
"""CJK/emoji job text must round-trip AND stay human-readable on disk.
With json.dump's default ensure_ascii=True, every non-ASCII char in
jobs.json is written as \\uXXXX escapes, which users reported as
unreadable garbage when inspecting their job store (#52302, #29754).
ensure_ascii=False + the existing encoding="utf-8" writer keeps the
text literal; the utf-8-sig reader must parse it back identically.
"""
name = "日次レポート 🎉 café"
job = create_job(prompt=f"Summarize {name}", schedule="30m", name=name)
# Round-trip through save/load is lossless.
fetched = get_job(job["id"])
assert fetched["name"] == name
assert name in fetched["prompt"]
# On-disk representation is literal UTF-8, not \uXXXX escapes.
from cron.jobs import JOBS_FILE
raw = JOBS_FILE.read_text(encoding="utf-8")
assert "日次レポート" in raw
assert "🎉" in raw
assert "\\u65e5" not in raw
def test_create_and_get(self, tmp_cron_dir):
job = create_job(prompt="Check server status", schedule="30m")
assert job["id"]