"""T10: chunker si loop de editare per canal.""" from __future__ import annotations import asyncio import pytest import render def test_text_scurt_ramane_intr_o_bucata(): r = render.split_message("salut") assert r.kind == "chunks" and r.parts == ["salut"] def test_sparge_la_2000_de_caractere(): text = "\n".join(f"linia {i} " + "x" * 50 for i in range(60)) r = render.split_message(text) assert r.kind == "chunks" and len(r.parts) > 1 assert all(len(p) <= render.MAX_MSG for p in r.parts) assert "linia 0" in r.parts[0] and "linia 59" in r.parts[-1] def test_fence_nu_se_rupe_peste_granita(): corp = "\n".join(f"cod linia {i}" for i in range(300)) r = render.split_message(f"intro\n```python\n{corp}\n```\ngata") assert len(r.parts) >= 2 for p in r.parts: assert p.count("```") % 2 == 0 # fiecare bucata are fence-uri echilibrate assert r.parts[0].endswith("```") assert r.parts[1].startswith("```python") assert all(len(p) <= render.MAX_MSG for p in r.parts) def test_fence_fara_limbaj(): corp = "\n".join(f"linia {i}" for i in range(400)) r = render.split_message(f"```\n{corp}\n```") assert r.parts[1].startswith("```\n") def test_linie_unica_uriasa_se_taie(): r = render.split_message("y" * 5000) assert r.kind == "chunks" and all(len(p) <= render.MAX_MSG for p in r.parts) def test_peste_6000_devine_atasament(): r = render.split_message("z" * 6001) assert r.kind == "attachment" assert isinstance(r, render.Attachment) assert r.content == "z" * 6001 and r.filename.endswith(".md") assert len(r.preview) <= render.MAX_MSG def test_text_gol(): assert render.split_message("").parts == [""] def test_footer(): s = render.footer("sonnet", 34500, 0.1547, 1.2) assert "sonnet" in s and "34.5s" in s and "0.1547" in s and "1.2000" in s # ------------------------------------------------------------- edit loop async def test_loop_coalesceaza_actualizarile(): edits = [] async def edit(target, text): edits.append((target, text)) loop = render.ChannelEditLoop("c1", edit, min_interval=0.05, max_interval=0.2) loop.start() for i in range(20): loop.queue("msg1", f"text {i}") await asyncio.sleep(0.15) await loop.stop() assert len(edits) < 20 # nu s-a trimis fiecare actualizare assert edits[-1] == ("msg1", "text 19") # ultima versiune ajunge async def test_flush_trimite_tot_ce_a_ramas(): edits = [] loop = render.ChannelEditLoop("c1", lambda t, x: _append(edits, t, x), min_interval=10, max_interval=10) loop.queue("m1", "a") loop.queue("m2", "b") await loop.flush() assert sorted(edits) == [("m1", "a"), ("m2", "b")] async def _append(lst, t, x): lst.append((t, x)) async def test_intervalul_creste_sub_presiune_si_scade_la_liniste(): loop = render.ChannelEditLoop("c1", lambda t, x: _append([], t, x), min_interval=0.01, max_interval=0.2) loop.start() for i in range(10): loop.queue(f"m{i}", "x") await asyncio.sleep(0.1) crescut = loop.interval assert crescut > 0.01 loop.note_rate_limited() assert loop.interval == 0.2 await asyncio.sleep(0.5) await loop.stop() assert loop.interval < 0.2 # linistea coboara intervalul async def test_editarea_esuata_nu_doboara_loop_ul(): async def edit(target, text): raise RuntimeError("429 sau altceva") loop = render.ChannelEditLoop("c1", edit, min_interval=0.01, max_interval=0.05) loop.start() loop.queue("m1", "a") await asyncio.sleep(0.1) assert loop.errors >= 1 loop.queue("m1", "b") await asyncio.sleep(0.1) assert loop.errors >= 2 # loop-ul inca traieste await loop.stop() async def test_un_singur_loop_per_canal(): edits = [] mgr = render.RenderManager(lambda t, x: _append(edits, t, x), min_interval=0.01, max_interval=0.05) l1 = mgr.loop_for("canal-1") assert mgr.loop_for("canal-1") is l1 assert mgr.loop_for("canal-2") is not l1 mgr.queue("canal-1", "m1", "salut") await asyncio.sleep(0.08) assert ("m1", "salut") in edits await mgr.stop_all() assert mgr.loops == {}