"""El mensaje de revisión del Short. Es la puerta humana: si este mensaje no sale, o sale sin los avisos, se está publicando lo que el modelo recuerde en vez de lo que dicen las fuentes. Por eso tiene test propio aparte del pipeline. """ import json from src.bot.bot import _claims_message from src.generator.grounding import check_grounding from src.generator.short import ShortResult SPEC = { "version": 1, "meta": {"id": "x", "title": "X"}, "shots": [{"template": "scale_bars", "duration": 30.0, "props": { "headline": "REPORTED SCALE", "bars": [{"label": "BOEING 747", "value": 232, "unit": "FT"}]}}], } CHUNKS = [{"content": "A Boeing 747 is 232 ft long.", "url": "https://a.test/1"}] def result_with(**kw): base = dict(topic="Caso X", spec=SPEC, title="X", attempts=1, cost_usd=0.0042, duration_s=30.0, article_url="https://www.theexclusionzone.com/caso-x/", grounding=check_grounding(SPEC, CHUNKS)) base.update(kw) return ShortResult(**base) def test_a_clean_report_still_says_so(): """Un éxito silencioso enseña al lector a dejar de mirar.""" text = _claims_message(result_with()) assert "0 sin encontrar" in text assert "1 chunks de 1 URLs" in text assert "Coste: $0.0042" in text def test_ungrounded_claims_are_listed_one_by_one(): invented = json.loads(json.dumps(SPEC)) invented["shots"][0]["props"]["headline"] = "41,000 FT" text = _claims_message(result_with(spec=invented, grounding=check_grounding(invented, CHUNKS))) assert "1 sin encontrar" in text assert "41,000 FT" in text def test_a_session_without_an_article_url_says_what_to_run(): text = _claims_message(result_with(article_url=None)) assert "/generate blog en" in text def test_render_warnings_reach_the_human(): text = _claims_message(result_with(render_warnings=[ {"template": "data_card", "text": "UNA FILA DEMASIADO LARGA", "requested": 44, "size": 38}])) assert "recortados" in text and "data_card" in text def test_there_is_a_report_even_when_there_was_no_spec(): text = _claims_message(ShortResult(topic="Caso X")) assert "Sin comprobación de fundamento" in text assert "Coste:" in text # --- el parte de la subida a YouTube ---------------------------------------- def _uploaded(**kw): from src.generator.youtube import UploadedVideo base = dict(video_id="abc123", title="X", privacy_status="private") base.update(kw) return UploadedVideo(**base) def test_upload_message_leads_with_the_studio_link(): """El enlace de Studio es la acción; el de watch es sólo comprobación.""" from src.bot.bot import _upload_message text = _upload_message(_uploaded(), {"snippet": {"tags": ["UAP"]}}, "https://theexclusionzone.com/x/") assert "https://studio.youtube.com/video/abc123/edit" in text assert "https://youtube.com/shorts/abc123" in text def test_upload_message_explains_the_private_lock(): """Que esté privado no es un fallo del bot, y hay que decir por qué.""" from src.bot.bot import _upload_message text = _upload_message(_uploaded(), {}, "https://x.test/") assert "PRIVADO" in text assert "auditoría" in text def test_upload_message_flags_a_forced_privacy_change(): from src.bot.bot import _upload_message text = _upload_message(_uploaded(forced_private=True), {}, "https://x.test/") assert "forzó" in text def test_upload_message_warns_when_the_description_has_no_article(): from src.bot.bot import _upload_message text = _upload_message(_uploaded(), {}, None) assert "Sin URL de artículo" in text assert "force" in text def test_upload_message_is_plain_text(): """Va sin parse_mode: lleva el título del modelo, y un Markdown roto haría que Telegram rechazara justo el mensaje que trae el enlace.""" from src.bot.bot import _upload_message text = _upload_message(_uploaded(title="JAL 1628: *three* radars_"), {}, None) assert "*three*" in text and "radars_" in text