republisher/tests/test_entrypoint.py
Abel Luck 710ac76192
All checks were successful
buildbot/nix-eval Build done.
buildbot/nix-build Build done.
buildbot/nix-effects Build done.
Prune old job executions
2026-06-02 11:31:39 +02:00

339 lines
9.6 KiB
Python

import io
import logging
from collections.abc import Awaitable, Callable
from types import SimpleNamespace
from typing import Any, cast
from repub.entrypoint import FeedNameFilter, entrypoint, logger, parse_args
def test_feed_name_filter_accepts_matching_item() -> None:
item = SimpleNamespace(feed_name="nasa")
feed_filter = FeedNameFilter({"feed_name": "nasa"})
assert feed_filter.accepts(item) is True
def test_feed_name_filter_rejects_non_matching_item() -> None:
item = SimpleNamespace(feed_name="gp-pod")
feed_filter = FeedNameFilter({"feed_name": "nasa"})
assert feed_filter.accepts(item) is False
def test_parse_args_uses_republisher_host_and_port_env_vars(monkeypatch) -> None:
monkeypatch.setenv("REPUBLISHER_HOST", "0.0.0.0")
monkeypatch.setenv("REPUBLISHER_PORT", "9090")
command, args = parse_args(["serve"])
assert command == "serve"
assert args.host == "0.0.0.0"
assert args.port == "9090"
def test_parse_args_supports_dev_mode_flag() -> None:
command, args = parse_args(["serve", "--dev-mode"])
assert command == "serve"
assert args.dev_mode is True
def test_parse_args_supports_reload_flag() -> None:
command, args = parse_args(["serve", "--reload"])
assert command == "serve"
assert args.reload is True
def test_parse_args_uses_reader_app_url_env_var(monkeypatch) -> None:
monkeypatch.setenv(
"REPUBLISHER_READER_APP_URL",
"https://s3.amazonaws.com/anynews/marti-noticias/index.html",
)
command, args = parse_args(["serve"])
assert command == "serve"
assert (
args.reader_app_url
== "https://s3.amazonaws.com/anynews/marti-noticias/index.html"
)
def test_parse_args_supports_cleanup_media_defaults() -> None:
command, args = parse_args(["cleanup-media"])
assert command == "cleanup-media"
assert args.config is None
assert args.feeds_dir is None
assert args.log_dir is None
assert args.days == 25
assert args.dry_run is False
def test_entrypoint_runs_cleanup_media(monkeypatch, tmp_path) -> None:
recorded: dict[str, object] = {}
class FakeResult:
failures = 0
def fake_cleanup_media(*, feeds_dir, retention_days, dry_run, media_dirs):
recorded["feeds_dir"] = feeds_dir
recorded["retention_days"] = retention_days
recorded["dry_run"] = dry_run
recorded["media_dirs"] = media_dirs
return FakeResult()
def fake_cleanup_job_executions(
*,
log_dir,
successful_days,
unsuccessful_days,
dry_run,
):
recorded["log_dir"] = log_dir
recorded["successful_days"] = successful_days
recorded["unsuccessful_days"] = unsuccessful_days
recorded["job_dry_run"] = dry_run
return FakeResult()
monkeypatch.setattr("repub.entrypoint.cleanup_media", fake_cleanup_media)
monkeypatch.setattr(
"repub.entrypoint.cleanup_job_executions",
fake_cleanup_job_executions,
)
exit_code = entrypoint(
[
"cleanup-media",
"--feeds-dir",
str(tmp_path / "feeds"),
"--days",
"10",
"--dry-run",
]
)
assert exit_code == 0
assert recorded == {
"feeds_dir": tmp_path / "feeds",
"retention_days": 10,
"dry_run": True,
"media_dirs": ("images", "audio", "video", "files"),
"log_dir": tmp_path / "logs",
"successful_days": 7,
"unsuccessful_days": 90,
"job_dry_run": True,
}
def test_entrypoint_cleanup_media_uses_configured_media_dirs(
monkeypatch, tmp_path
) -> None:
config_path = tmp_path / "repub.toml"
config_path.write_text(
"""
out_dir = "mirror"
[[feeds]]
name = "Demo"
slug = "demo"
url = "https://source.example/feed.rss"
[scrapy.settings]
REPUBLISHER_IMAGE_DIR = "images-custom"
REPUBLISHER_AUDIO_DIR = "audio-custom"
REPUBLISHER_VIDEO_DIR = "videos-custom"
REPUBLISHER_FILE_DIR = "files-custom"
""".strip(),
encoding="utf-8",
)
recorded: dict[str, object] = {}
class FakeResult:
failures = 0
def fake_cleanup_media(*, feeds_dir, retention_days, dry_run, media_dirs):
recorded["feeds_dir"] = feeds_dir
recorded["retention_days"] = retention_days
recorded["dry_run"] = dry_run
recorded["media_dirs"] = media_dirs
return FakeResult()
def fake_cleanup_job_executions(
*,
log_dir,
successful_days,
unsuccessful_days,
dry_run,
):
recorded["log_dir"] = log_dir
recorded["successful_days"] = successful_days
recorded["unsuccessful_days"] = unsuccessful_days
recorded["job_dry_run"] = dry_run
return FakeResult()
monkeypatch.setattr("repub.entrypoint.cleanup_media", fake_cleanup_media)
monkeypatch.setattr(
"repub.entrypoint.cleanup_job_executions",
fake_cleanup_job_executions,
)
exit_code = entrypoint(["cleanup-media", "--config", str(config_path)])
assert exit_code == 0
assert recorded == {
"feeds_dir": tmp_path / "mirror" / "feeds",
"retention_days": 25,
"dry_run": False,
"media_dirs": (
"images-custom",
"audio-custom",
"videos-custom",
"files-custom",
),
"log_dir": tmp_path / "mirror" / "logs",
"successful_days": 7,
"unsuccessful_days": 90,
"job_dry_run": False,
}
def test_entrypoint_cleanup_media_accepts_log_dir_override(
monkeypatch, tmp_path
) -> None:
recorded: dict[str, object] = {}
class FakeResult:
failures = 0
def fake_cleanup_media(*, feeds_dir, retention_days, dry_run, media_dirs):
recorded["feeds_dir"] = feeds_dir
return FakeResult()
def fake_cleanup_job_executions(
*,
log_dir,
successful_days,
unsuccessful_days,
dry_run,
):
recorded["log_dir"] = log_dir
recorded["successful_days"] = successful_days
recorded["unsuccessful_days"] = unsuccessful_days
recorded["dry_run"] = dry_run
return FakeResult()
monkeypatch.setattr("repub.entrypoint.cleanup_media", fake_cleanup_media)
monkeypatch.setattr(
"repub.entrypoint.cleanup_job_executions",
fake_cleanup_job_executions,
)
exit_code = entrypoint(
[
"cleanup-media",
"--feeds-dir",
str(tmp_path / "feeds"),
"--log-dir",
str(tmp_path / "custom-logs"),
]
)
assert exit_code == 0
assert recorded == {
"feeds_dir": tmp_path / "feeds",
"log_dir": tmp_path / "custom-logs",
"successful_days": 7,
"unsuccessful_days": 90,
"dry_run": False,
}
def test_parse_args_defaults_to_dev_mode_when_no_args() -> None:
command, args = parse_args([])
assert command == "serve"
assert args.dev_mode is True
def test_entrypoint_rejects_invalid_republisher_port(monkeypatch) -> None:
monkeypatch.setenv("REPUBLISHER_PORT", "not-a-number")
stream = io.StringIO()
handlers = [
cast(logging.StreamHandler[io.StringIO], handler) for handler in logger.handlers
]
original_streams = [handler.stream for handler in handlers]
for handler in handlers:
handler.stream = stream
try:
exit_code = entrypoint(["serve"])
finally:
for handler, original_stream in zip(handlers, original_streams):
handler.stream = original_stream
assert exit_code == 2
assert "Invalid REPUBLISHER_PORT/--port value" in stream.getvalue()
def test_entrypoint_passes_dev_mode_to_create_app(monkeypatch) -> None:
recorded: dict[str, object] = {}
class StubApp:
def __init__(self) -> None:
self.extensions: dict[str, object] = {}
def fake_create_app(*, dev_mode: bool, reader_app_url: str | None) -> StubApp:
recorded["dev_mode"] = dev_mode
recorded["reader_app_url"] = reader_app_url
return StubApp()
def fake_install_signal_handlers(stop_event: object) -> None:
recorded["stop_event"] = stop_event
async def fake_hypercorn_serve(
app: StubApp,
config: Any,
*,
shutdown_trigger: Callable[[], Awaitable[None]],
) -> None:
recorded["app"] = app
recorded["host"] = config.bind[0].split(":")[0]
recorded["port"] = int(config.bind[0].split(":")[1])
recorded["reload"] = config.use_reloader
recorded["shutdown_trigger"] = shutdown_trigger
shutdown_event = cast(Any, app.extensions["repub.shutdown_event"])
recorded["app_shutdown_event"] = shutdown_event
shutdown_event.set()
await shutdown_trigger()
monkeypatch.setattr("repub.entrypoint.create_app", fake_create_app)
monkeypatch.setattr(
"repub.entrypoint._install_signal_handlers", fake_install_signal_handlers
)
monkeypatch.setattr("repub.entrypoint.hypercorn_serve", fake_hypercorn_serve)
exit_code = entrypoint(
[
"serve",
"--dev-mode",
"--reload",
"--host",
"0.0.0.0",
"--port",
"9090",
"--reader-app-url",
"https://reader.example/index.html",
]
)
assert exit_code == 0
assert recorded["dev_mode"] is True
assert recorded["reader_app_url"] == "https://reader.example/index.html"
assert recorded["host"] == "0.0.0.0"
assert recorded["port"] == 9090
assert recorded["reload"] is True
assert recorded["stop_event"] is recorded["app_shutdown_event"]
assert callable(recorded["shutdown_trigger"])