diff --git a/src/chrys/service/hooks/run_log.py b/src/chrys/service/hooks/run_log.py index 1a26b108..3f660c27 100644 --- a/src/chrys/service/hooks/run_log.py +++ b/src/chrys/service/hooks/run_log.py @@ -107,14 +107,19 @@ def indented_json(value: object) -> str: def _clip_text(text: str, limit: int = HOOK_RUN_TEXT_MAX_BYTES) -> tuple[str, int]: """*text* cut to at most *limit* UTF-8 bytes, and its full size in them. - A cut drops the character it splits, and every lone surrogate of the text. + A cut drops the character it splits; lone surrogates, which a path that is not + UTF-8 holds, are kept as they are. """ if text.isascii(): return text[:limit], len(text) data = text.encode("utf-8", "surrogatepass") if len(data) <= limit: return text, len(data) - return data[:limit].decode("utf-8", "ignore"), len(data) + cut = limit + # Back to the first byte of the character the limit splits: its others are 10xxxxxx. + while cut and data[cut] & 0xC0 == 0x80: + cut -= 1 + return data[:cut].decode("utf-8", "surrogatepass"), len(data) class HookRunLog: diff --git a/tests/orchestration/engine/test_hook_run_reporting.py b/tests/orchestration/engine/test_hook_run_reporting.py index 848587ae..d41a0ca9 100644 --- a/tests/orchestration/engine/test_hook_run_reporting.py +++ b/tests/orchestration/engine/test_hook_run_reporting.py @@ -24,7 +24,7 @@ from chrys.service.hooks.run_log import HookRunLog from chrys.service.hooks.schema import HookConfig, HookExecution, HookMatch, HookRun, HookSettings, HooksFile from chrys.service.trajectory.hooks import HookOutcome -from tests.support.waiting import ENGINE_TURN_TIMEOUT, wait_for, wait_until +from tests.support.waiting import ENGINE_TURN_TIMEOUT, wait_for _SESSION = "s1" @@ -267,7 +267,6 @@ async def explode(_event: HookRunsChanged) -> None: assert decision.blocked is True (record,) = log.records() assert (record.state, record.effect) == ("ended", "blocked") - assert not await wait_until(lambda: len(log.records()) != 1, timeout=0.2) def test_the_notice_does_not_reach_headless_or_acp_output() -> None: diff --git a/tests/service/hooks/test_run_log_retention.py b/tests/service/hooks/test_run_log_retention.py index 42503b90..40cbc69e 100644 --- a/tests/service/hooks/test_run_log_retention.py +++ b/tests/service/hooks/test_run_log_retention.py @@ -541,10 +541,10 @@ def test_changed_arguments_past_the_cap_are_cut_with_their_size_kept(unit: str) detail = log.detail("rewrite") assert detail is not None and detail.applied is not None kept = detail.applied.args - assert len(kept.encode("utf-8", "surrogatepass")) <= HOOK_RUN_TEXT_MAX_BYTES + # Cut short of the limit by the character it would split, at most: a path that is not UTF-8 included. + assert HOOK_RUN_TEXT_MAX_BYTES - 4 < len(kept.encode("utf-8", "surrogatepass")) <= HOOK_RUN_TEXT_MAX_BYTES assert detail.applied.args_size == len(full.encode("utf-8", "surrogatepass")) - if unit != "\udc80": - assert full.startswith(kept) + assert full.startswith(kept) def test_changed_arguments_within_the_cap_are_kept_whole() -> None: