mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 22:27:01 +01:00
The last six over-cap files split into eleven new modules, in two passes since the first cut left five still over. python_pkg has no violations left at all, down from 50 when this effort started. Each split carries the whole preamble including the module's pytest fixtures - started, toolkit and session are taken as parameters and resolve lexically, so a hand-written import block would have missed them. ruff then pruned 82 unused imports. Pass count is 277 before and after, checked after both passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
224 lines
5.9 KiB
Python
224 lines
5.9 KiB
Python
"""Tests for the background worker.
|
|
|
|
Every case drives ``run_once`` directly. No real thread runs and no socket is
|
|
opened, so the suite stays deterministic and cannot leak either.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import queue
|
|
import threading
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from python_pkg.wsg_grabber import db, downloader, net, store, store_threads
|
|
from python_pkg.wsg_grabber.models import (
|
|
DownloadEvent,
|
|
Indexed,
|
|
JsonResponse,
|
|
RemoteFile,
|
|
TaskKind,
|
|
ThreadRef,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
from pathlib import Path
|
|
|
|
_MD5 = "Nz9OEKdMuMZEdYE6eLTKmA=="
|
|
_BODY = b"video bytes"
|
|
|
|
|
|
@pytest.fixture
|
|
def deps(tmp_path: Path) -> Iterator[downloader.WorkerDeps]:
|
|
"""Build worker dependencies backed by a sandboxed index.
|
|
|
|
Args:
|
|
tmp_path: Per-test temporary directory.
|
|
|
|
Yields:
|
|
downloader.WorkerDeps: Ready dependencies; resources closed after.
|
|
"""
|
|
incoming = tmp_path / "incoming"
|
|
incoming.mkdir()
|
|
conn = db.open_index(tmp_path / "index.db")
|
|
session = MagicMock()
|
|
built = downloader.WorkerDeps(
|
|
conn=conn,
|
|
session=session,
|
|
incoming=incoming,
|
|
events=queue.SimpleQueue(),
|
|
stop=threading.Event(),
|
|
include_archive=False,
|
|
)
|
|
try:
|
|
yield built
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def _json(
|
|
payload: object,
|
|
*,
|
|
last_modified: str | None = None,
|
|
not_modified: bool = False,
|
|
not_found: bool = False,
|
|
) -> JsonResponse:
|
|
"""Build a JsonResponse with sensible defaults.
|
|
|
|
Args:
|
|
payload: Decoded body.
|
|
last_modified: Header value the response carried.
|
|
not_modified: Whether the server answered 304.
|
|
not_found: Whether the thread has gone.
|
|
|
|
Returns:
|
|
JsonResponse: Response double.
|
|
"""
|
|
return JsonResponse(
|
|
payload=payload,
|
|
last_modified=last_modified,
|
|
not_modified=not_modified,
|
|
not_found=not_found,
|
|
)
|
|
|
|
|
|
def _remote(md5: str = _MD5, tim: int = 1) -> RemoteFile:
|
|
"""Build a RemoteFile.
|
|
|
|
Args:
|
|
md5: Identity.
|
|
tim: CDN stem.
|
|
|
|
Returns:
|
|
RemoteFile: Fixture value.
|
|
"""
|
|
return RemoteFile(
|
|
md5=md5,
|
|
tim=tim,
|
|
ext=".webm",
|
|
orig_name="clip",
|
|
fsize=len(_BODY),
|
|
width=480,
|
|
height=360,
|
|
thread_no=7,
|
|
post_no=tim,
|
|
)
|
|
|
|
|
|
def _drain(events: queue.SimpleQueue[DownloadEvent]) -> list[DownloadEvent]:
|
|
"""Pull everything currently queued.
|
|
|
|
Args:
|
|
events: Queue to empty.
|
|
|
|
Returns:
|
|
list[DownloadEvent]: Events in order.
|
|
"""
|
|
out: list[DownloadEvent] = []
|
|
while not events.empty():
|
|
out.append(events.get_nowait())
|
|
return out
|
|
|
|
|
|
def test_first_step_reads_the_thread_list(deps: downloader.WorkerDeps) -> None:
|
|
worker = downloader.Worker(deps)
|
|
payload = [{"threads": [{"no": 7, "last_modified": 100}]}]
|
|
with patch.object(net, "get_json", return_value=_json(payload)):
|
|
assert worker.run_once() is TaskKind.SCAN
|
|
|
|
|
|
def test_scan_then_fetch_records_files(deps: downloader.WorkerDeps) -> None:
|
|
worker = downloader.Worker(deps)
|
|
listing = [{"threads": [{"no": 7, "last_modified": 100}]}]
|
|
thread = {
|
|
"posts": [
|
|
{
|
|
"no": 1,
|
|
"tim": 1,
|
|
"ext": ".webm",
|
|
"md5": _MD5,
|
|
"fsize": 11,
|
|
"w": 4,
|
|
"h": 3,
|
|
"filename": "clip",
|
|
},
|
|
],
|
|
}
|
|
with patch.object(
|
|
net,
|
|
"get_json",
|
|
side_effect=[_json(listing), _json(thread, last_modified="stamp")],
|
|
):
|
|
worker.run_once()
|
|
worker.run_once()
|
|
|
|
assert store.known_md5s(deps.conn) == {_MD5}
|
|
assert store_threads.known_threads(deps.conn) == {7: 100}
|
|
assert store_threads.http_last_modified(deps.conn, 7) == "stamp"
|
|
assert any(isinstance(event, Indexed) for event in _drain(deps.events))
|
|
|
|
|
|
def test_an_unchanged_thread_costs_only_a_304(deps: downloader.WorkerDeps) -> None:
|
|
worker = downloader.Worker(deps)
|
|
store_threads.record_thread(deps.conn, ThreadRef(7, 50), "old-stamp")
|
|
listing = [{"threads": [{"no": 7, "last_modified": 100}]}]
|
|
with patch.object(
|
|
net,
|
|
"get_json",
|
|
side_effect=[_json(listing), _json(None, not_modified=True)],
|
|
) as fake:
|
|
worker.run_once()
|
|
worker.run_once()
|
|
assert fake.call_args.args[2] == "old-stamp"
|
|
assert store_threads.known_threads(deps.conn) == {7: 100}
|
|
assert store.known_md5s(deps.conn) == set()
|
|
|
|
|
|
def test_a_vanished_thread_is_marked_gone(deps: downloader.WorkerDeps) -> None:
|
|
worker = downloader.Worker(deps)
|
|
listing = [{"threads": [{"no": 7, "last_modified": 100}]}]
|
|
with patch.object(
|
|
net,
|
|
"get_json",
|
|
side_effect=[_json(listing), _json(None, not_found=True)],
|
|
):
|
|
worker.run_once()
|
|
worker.run_once()
|
|
row = deps.conn.execute("SELECT status FROM threads WHERE thread_no=7").fetchone()
|
|
assert row["status"] == "gone"
|
|
|
|
|
|
def test_archive_threads_are_included_when_enabled(
|
|
deps: downloader.WorkerDeps,
|
|
) -> None:
|
|
deps.include_archive = True
|
|
worker = downloader.Worker(deps)
|
|
with patch.object(
|
|
net,
|
|
"get_json",
|
|
side_effect=[
|
|
_json([{"threads": [{"no": 7, "last_modified": 1}]}]),
|
|
_json([88]),
|
|
],
|
|
):
|
|
worker.run_once()
|
|
with patch.object(net, "get_json", side_effect=[_json({"posts": []})]) as fake:
|
|
worker.run_once()
|
|
assert "/thread/" in fake.call_args.args[1]
|
|
|
|
|
|
def test_a_board_without_an_archive_is_tolerated(
|
|
deps: downloader.WorkerDeps,
|
|
) -> None:
|
|
deps.include_archive = True
|
|
worker = downloader.Worker(deps)
|
|
with patch.object(
|
|
net,
|
|
"get_json",
|
|
side_effect=[_json([]), _json(None, not_found=True)],
|
|
):
|
|
assert worker.run_once() is TaskKind.SCAN
|