diff --git a/post_parser.py b/post_parser.py index 32ca6f1..c370e7a 100644 --- a/post_parser.py +++ b/post_parser.py @@ -486,7 +486,10 @@ class PostParser: html_content = [] if debug: - html_content.append(f'
Title: {data["html"]["title"]}

') + # title comes from _generate_title (user-controlled post text) and never goes + # through bleach — escape it before embedding, same as raw_message below. + title_escaped = html.escape(str(data["html"]["title"])) + html_content.append(f'
Title: {title_escaped}

') html_content.append(f'
{data["html"]["body"]}
') html_content.append(f'') diff --git a/tests/pytest.ini b/pytest.ini similarity index 86% rename from tests/pytest.ini rename to pytest.ini index 6d6f517..d6e331d 100644 --- a/tests/pytest.ini +++ b/pytest.ini @@ -1,8 +1,9 @@ [pytest] +testpaths = tests addopts = -ra # The stage-1 anti-hang tests are async; run them without per-test event loops # being set up by hand. (The tests also carry @pytest.mark.asyncio explicitly, so # they work in strict mode too — but auto keeps the plugin's requirement obvious.) asyncio_mode = auto filterwarnings = - ignore::DeprecationWarning \ No newline at end of file + ignore::DeprecationWarning diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..850fb53 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,17 @@ +"""Shared test bootstrap (issue #17). + +Ensure the repo root is importable and install the config mock BEFORE any test +module imports application code. Pytest imports conftest.py before collecting +test modules, so doing this here (instead of a per-module preamble) makes the +suite order-independent regardless of collection order or invocation directory. +""" +import os +import sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +if ROOT not in sys.path: + sys.path.insert(0, ROOT) + +import tests.mock_config as _mock_config + +sys.modules['config'] = _mock_config diff --git a/tests/test_postparser_author_info.py b/tests/test_postparser_author_info.py index 84bb48e..2b9c314 100644 --- a/tests/test_postparser_author_info.py +++ b/tests/test_postparser_author_info.py @@ -7,13 +7,6 @@ import unittest from unittest.mock import MagicMock -import sys -import os -# Add project root to sys.path to find post_parser -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -# Mock the config module -sys.modules['config'] = __import__('tests.mock_config', fromlist=['get_settings']) from pyrogram.types import Message from post_parser import PostParser diff --git a/tests/test_postparser_extract_flags.py b/tests/test_postparser_extract_flags.py index a6ab03f..c450e1e 100644 --- a/tests/test_postparser_extract_flags.py +++ b/tests/test_postparser_extract_flags.py @@ -7,14 +7,6 @@ import unittest from unittest.mock import MagicMock, PropertyMock -import sys -import os - -# Add project root to sys.path to find post_parser -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -# Mock the config module before importing PostParser -sys.modules['config'] = __import__('tests.mock_config', fromlist=['get_settings']) from pyrogram.types import Message, Chat, Reaction, MessageReactions from pyrogram.enums import MessageMediaType diff --git a/tests/test_postparser_gen_title.py b/tests/test_postparser_gen_title.py index 6ebda97..08315eb 100644 --- a/tests/test_postparser_gen_title.py +++ b/tests/test_postparser_gen_title.py @@ -7,13 +7,6 @@ # pylance: disable=reportMissingImports, reportMissingModuleSource import unittest from unittest.mock import MagicMock, PropertyMock -import sys -import os -# Add project root to sys.path to find post_parser -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -# Mock the config module -sys.modules['config'] = __import__('tests.mock_config', fromlist=['get_settings']) from pyrogram.types import Message, Document from pyrogram.enums import MessageMediaType diff --git a/tests/test_stage1_hangs.py b/tests/test_stage1_hangs.py index a1b3104..3b9a076 100644 --- a/tests/test_stage1_hangs.py +++ b/tests/test_stage1_hangs.py @@ -10,18 +10,12 @@ Stage 1 (anti-hang) regression tests: worker, and task_done stays balanced so queue.join() completes. - Gate cancellation during the spacing wait does not lose the permit. """ -import os -import sys import time import asyncio from types import SimpleNamespace import pytest -# Add project root to sys.path and mock the config module (same pattern as the other tests). -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -sys.modules['config'] = __import__('tests.mock_config', fromlist=['get_settings']) - import tg_throttle import tg_cache from pyrogram import errors diff --git a/tests/test_stage2_static.py b/tests/test_stage2_static.py index a8a6538..00ce557 100644 --- a/tests/test_stage2_static.py +++ b/tests/test_stage2_static.py @@ -18,17 +18,12 @@ Covers the four scenarios from the plan plus the subtle in-flight-dedup lifecycl fresh partials and non-temp files. """ import os -import sys import time import asyncio from types import SimpleNamespace import pytest -# Add project root to sys.path and mock the config module (same pattern as the other tests). -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -sys.modules['config'] = __import__('tests.mock_config', fromlist=['get_settings']) - import api_server from pyrogram import errors from pyrogram.enums import MessageMediaType diff --git a/tests/test_stage3_fileresponse.py b/tests/test_stage3_fileresponse.py index 9dc054c..9e4f4ef 100644 --- a/tests/test_stage3_fileresponse.py +++ b/tests/test_stage3_fileresponse.py @@ -36,16 +36,11 @@ All 206 responses that carry data return byte-for-byte identical slices old vs n real regression is hidden behind an "accepted difference". """ import os -import sys import time import logging import pytest -# Add project root to sys.path and mock the config module (same pattern as the other tests). -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -sys.modules['config'] = __import__('tests.mock_config', fromlist=['get_settings']) - from fastapi import FastAPI, Request from fastapi.testclient import TestClient diff --git a/tests/test_stage4_eventloop.py b/tests/test_stage4_eventloop.py index c0648ec..0ef5c49 100644 --- a/tests/test_stage4_eventloop.py +++ b/tests/test_stage4_eventloop.py @@ -16,9 +16,7 @@ Covers: - 4.4 sanitize coverage (XSS): a " + # verbatim — it must be html-escaped before being embedded in the debug output. + msg = make_message(25, media=MessageMediaType.POLL) + msg.poll = SimpleNamespace(question="") + parser = PostParser(_make_client_returning(msg)) + html_out = await parser.get_post("testchan", 25, "html", debug=True) + + # No live