"""Tests for the textutil module.""" from __future__ import annotations import pytest from vibe_bot.textutil import split_message def test_split_message_empty() -> None: assert split_message("") == [] def test_split_message_short_text_single_chunk() -> None: assert split_message("hello") == ["hello"] def test_split_message_exact_limit() -> None: text = "a" * 1900 chunks = split_message(text) assert chunks == [text] assert len(chunks) == 1 def test_split_message_just_over_limit_no_newline() -> None: text = "a" * 1901 chunks = split_message(text) assert "".join(chunks) == text assert all(len(c) <= 1900 for c in chunks) assert chunks == ["a" * 1900, "a"] def test_split_message_no_newlines_long() -> None: text = "x" * 5000 chunks = split_message(text) assert "".join(chunks) == text assert all(len(c) <= 1900 for c in chunks) def test_split_message_respects_newlines() -> None: # Many short lines spanning several chunks: boundaries fall on newlines. text = "\n".join(f"line {i}" for i in range(1, 501)) chunks = split_message(text) assert "".join(chunks) == text assert all(len(c) <= 1900 for c in chunks) assert len(chunks) > 1 # Every chunk except the last ends on a newline (split at a line boundary). for chunk in chunks[:-1]: assert chunk.endswith("\n") def test_split_message_long_line_hard_split() -> None: # A single line longer than the limit must be hard-split. text = "a" * 5000 + "\n" + "short" chunks = split_message(text) assert "".join(chunks) == text assert all(len(c) <= 1900 for c in chunks) def test_split_message_emoji_round_trip() -> None: # Multi-codepoint emoji at split boundaries must not corrupt on join. text = "hello ๐ŸŒ world ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ end " * 500 for limit in (1, 10, 100, 1900): chunks = split_message(text, limit) assert "".join(chunks) == text assert all(len(c) <= limit for c in chunks) def test_split_message_property_round_trip_corpus() -> None: corpus = [ "", "a", "a" * 1900, "a" * 1901, "line1\nline2\nline3", "para one\n\npara two\n\npara three", "no newline " * 1000, "emoji ๐ŸŽ‰ " * 1000, "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ" * 2000, "mixed ๐ŸŒ text and ๆ—ฅๆœฌ่ชž and emoji ๐ŸŽŠ here", # NFD combining marks (e + U+0301, a + U+0308) straddle split points. "cafe\u0301 " * 1000, "a\u0308\u0301 b\u0327\u0301 c\u0308 " * 1000, # Code spans with backticks and spaces. "`inline code` and `x = 1` spans " * 500, "``double backtick`` `single` " * 500, ] for text in corpus: for limit in (1, 5, 100, 1900): chunks = split_message(text, limit) assert "".join(chunks) == text, f"round-trip failed for limit={limit}" assert all(len(c) <= limit for c in chunks) def test_split_message_exact_multiple_of_limit() -> None: """Input that is an exact multiple of the limit splits into full chunks.""" for limit in (1, 5, 100, 1900): text = "z" * (limit * 4) chunks = split_message(text, limit) assert "".join(chunks) == text assert len(chunks) == 4 assert all(len(c) == limit for c in chunks) def test_split_message_limit_must_be_positive() -> None: with pytest.raises(ValueError): split_message("hello", 0) with pytest.raises(ValueError): split_message("hello", -5)