mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-11-04 11:57:16 +00:00
The bot's poller and the web viewer's preview each carried the same API timestamp parser, API item field extraction, RSS published-date parse, and the allow_private_urls / max_response_bytes / max_parsed_items config reads. modules/feed_parse.py now holds those; the default size limits move there and feed_manager re-exports them. Where the two sides differ they still do: the bot skips non-dict and id-less API items while the preview turns string items into dicts, and the preview unwraps items/data/results from a bare dict response. Those divergences are a separate decision.
76 lines
3.0 KiB
Python
76 lines
3.0 KiB
Python
"""modules.feed_parse: the parsing and config pieces FeedManager and the viewer share."""
|
|
|
|
import configparser
|
|
from datetime import datetime, timezone
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from modules import feed_parse as fp
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("value", "expected"),
|
|
[
|
|
(0, None),
|
|
("", None),
|
|
(1700000000, datetime(2023, 11, 14, 22, 13, 20, tzinfo=timezone.utc)),
|
|
("2024-05-06T07:08:09Z", datetime(2024, 5, 6, 7, 8, 9, tzinfo=timezone.utc)),
|
|
# fromisoformat (3.11+) accepts these before the UTC-tagging fallback is
|
|
# reached, so they come back naive. Pinned as current behavior.
|
|
("2024-05-06 07:08:09", datetime(2024, 5, 6, 7, 8, 9)),
|
|
("2024-05-06", datetime(2024, 5, 6)),
|
|
("not a date", None),
|
|
(["list"], None),
|
|
],
|
|
)
|
|
def test_parse_item_timestamp(value, expected):
|
|
assert fp.parse_item_timestamp(value) == expected
|
|
|
|
|
|
def test_parse_item_timestamp_microsoft_format():
|
|
assert fp.parse_item_timestamp("/Date(1700000000000)/") == datetime(2023, 11, 14, 22, 13, 20, tzinfo=timezone.utc)
|
|
|
|
|
|
def test_rss_entry_published():
|
|
assert fp.rss_entry_published(SimpleNamespace(published_parsed=(2024, 1, 2, 3, 4, 5, 0, 0, 0))) == datetime(
|
|
2024, 1, 2, 3, 4, 5, tzinfo=timezone.utc
|
|
)
|
|
assert fp.rss_entry_published(SimpleNamespace()) is None
|
|
assert fp.rss_entry_published(SimpleNamespace(published_parsed=None)) is None
|
|
|
|
|
|
def test_api_item_fields_reads_nested_and_custom_fields():
|
|
item = {"t": {"name": "Hello"}, "body": 5, "link": "http://x", "when": "2024-05-06"}
|
|
got = fp.api_item_fields(item, {"title_field": "t.name", "description_field": "body", "timestamp_field": "when"})
|
|
assert got == {
|
|
"title": "Hello", "emoji": "", "link": "http://x", "description": "5",
|
|
"published": datetime(2024, 5, 6), "raw": item,
|
|
}
|
|
assert fp.api_item_fields({}, {})["title"] == "Untitled"
|
|
|
|
|
|
def _config(**sections):
|
|
cfg = configparser.ConfigParser()
|
|
for name, values in sections.items():
|
|
cfg[name] = values
|
|
return cfg
|
|
|
|
|
|
def test_config_helpers_defaults_and_clamps():
|
|
empty = _config()
|
|
assert fp.feed_allow_private_urls(empty) is False
|
|
assert fp.feed_max_response_bytes(empty) == fp.DEFAULT_MAX_FEED_RESPONSE_BYTES
|
|
assert fp.feed_max_parsed_items(empty) == fp.DEFAULT_MAX_PARSED_FEED_ITEMS
|
|
cfg = _config(Feed_Manager={"max_response_bytes": "10", "max_parsed_items": "0"})
|
|
assert fp.feed_max_response_bytes(cfg) == 1024
|
|
assert fp.feed_max_parsed_items(cfg) == 1
|
|
|
|
|
|
def test_allow_private_falls_back_to_feed_command():
|
|
assert fp.feed_allow_private_urls(_config(Feed_Command={"allow_private_urls": "true"})) is True
|
|
assert fp.feed_allow_private_urls(_config(Feed_Command={"allow_private_urls": "maybe"})) is False
|
|
cfg = _config(Feed_Command={"allow_private_urls": "true"}, Feed_Manager={"allow_private_urls": "false"})
|
|
assert fp.feed_allow_private_urls(cfg) is False
|
|
assert fp.feed_allow_private_urls(_config(Feed_Command={"allow_private_urls": "true"}, Feed_Manager={})) is True
|