Files
FabledCurator/tests/test_api_settings_downloader.py
T
bvandeusenandClaude Opus 5 2b093958a4
CI and images / extension-version (push) Successful in 3s
CI and images / lint (push) Successful in 3s
CI and images / frontend-build (push) Successful in 19s
CI and images / backend-lint-and-test (push) Successful in 40s
CI and images / integration (push) Failing after 2m17s
CI and images / sign-extension (push) Skipped
CI and images / build-web (push) Skipped
CI and images / smoke-web (push) Skipped
CI and images / promote (push) Skipped
CI and images / build-agent (push) Skipped
feat: a tick keeps looking back 30 days, so an EDITED post is reached (4386)
Operator, 2026-09-23, on a Floppystack post: "this post has been updated as
he implements hot fixes — any chance we have a way to scan for or see updated
posts so we can update ours to match and pull the new attachments and
pictures etc."

The download half already worked: extract_media reads the media list off the
LIVE feed response every walk, so a newly attached hotfix build is a ledger
key we have never seen. Only REACHING the post was missing — a tick stopped
after 20 contiguous already-have-it items, and a post edited three days after
publication sits well below twenty. Not a bug in the early-out; a count
cannot express "recent".

The early-out now needs BOTH conditions: the run of seen items AND a post
published before the horizon. Strictly a widening — window 0 is exactly the
old behaviour, and no window can make a tick stop EARLIER than it used to, so
a source paused for months still walks its whole unseen backlog. The horizon
is a floor on how far to look, never a ceiling.

Inside the window the post-record gate is bypassed too (write_post_record
revisit=True): the body is re-read from the feed response already in hand, so
a revisit costs zero requests, and a body that comes back empty writes
NOTHING rather than blanking one a detail-fetch had filled. Revisits are kept
out of the #862 body-drift canary's sample for the same reason — an empty
revisit is healthy, and counting it would walk the alarm toward firing on
good ticks.

The run summary names what changed ("3 post(s) updated (5 new file(s))") with
a line per post; the ask was to SEE updated posts, not only to end up with
their bytes.

download_revisit_days is a settings row, not a constant (rule 25) — how long
a creator keeps editing is a property of the creator. Default 30, 0 turns it
off. Migration 0108.

Also corrects two stale docstrings: both clients described post_meta as
feeding an Ingester.preview that no longer calls it. It had no consumer at
all until this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
2026-09-23 19:21:06 -04:00

106 lines
3.3 KiB
Python

import pytest
pytestmark = pytest.mark.integration
@pytest.mark.asyncio
async def test_get_includes_downloader_fields(client):
resp = await client.get("/api/settings/import")
assert resp.status_code == 200
body = await resp.get_json()
assert body["download_rate_limit_seconds"] == 3.0
assert body["download_validate_files"] is True
@pytest.mark.asyncio
async def test_patch_download_rate_limit(client):
resp = await client.patch(
"/api/settings/import", json={"download_rate_limit_seconds": 1.5}
)
assert resp.status_code == 200
assert (await resp.get_json())["download_rate_limit_seconds"] == 1.5
@pytest.mark.asyncio
async def test_patch_download_validate_files_false(client):
resp = await client.patch(
"/api/settings/import", json={"download_validate_files": False}
)
assert resp.status_code == 200
assert (await resp.get_json())["download_validate_files"] is False
@pytest.mark.asyncio
async def test_patch_rejects_negative_rate_limit(client):
resp = await client.patch(
"/api/settings/import", json={"download_rate_limit_seconds": -1.0}
)
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_patch_rejects_non_bool_validate(client):
resp = await client.patch(
"/api/settings/import", json={"download_validate_files": "yes"}
)
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_extdl_host_toggles_default_on_and_patch(client):
body = await (await client.get("/api/settings/import")).get_json()
assert body["extdl_mega_enabled"] is True
assert body["extdl_pixeldrain_enabled"] is True
resp = await client.patch(
"/api/settings/import", json={"extdl_mega_enabled": False}
)
assert resp.status_code == 200
assert (await resp.get_json())["extdl_mega_enabled"] is False
@pytest.mark.asyncio
async def test_extdl_toggle_rejects_non_bool(client):
resp = await client.patch(
"/api/settings/import", json={"extdl_gdrive_enabled": "nope"}
)
assert resp.status_code == 400
# -- download_revisit_days: how far back a tick looks for EDITED posts --------
@pytest.mark.asyncio
async def test_revisit_window_defaults_to_thirty_days(client):
body = await (await client.get("/api/settings/import")).get_json()
assert body["download_revisit_days"] == 30
@pytest.mark.asyncio
async def test_revisit_window_is_settable(client):
resp = await client.patch(
"/api/settings/import", json={"download_revisit_days": 7}
)
assert resp.status_code == 200
assert (await resp.get_json())["download_revisit_days"] == 7
@pytest.mark.asyncio
async def test_zero_is_accepted_because_it_is_the_off_switch(client):
"""0 turns the revisit off and restores the pure count early-out. Pinned
because the obvious bounds check for a "days" field is `>= 1`, and that
would take the off switch away without anything failing."""
resp = await client.patch(
"/api/settings/import", json={"download_revisit_days": 0}
)
assert resp.status_code == 200
assert (await resp.get_json())["download_revisit_days"] == 0
@pytest.mark.asyncio
async def test_a_negative_window_is_refused(client):
resp = await client.patch(
"/api/settings/import", json={"download_revisit_days": -1}
)
assert resp.status_code == 400