feat: enforce community source cooldown
This commit is contained in:
@@ -10,17 +10,24 @@ from sqlalchemy.orm import Session
|
||||
from app.community_importer import CommunityImportError, stage_observations
|
||||
from app.database import Base
|
||||
from app.models import DataSource, ExternalObservation
|
||||
from rf4_research.community_sources import parse_rf4db_catches
|
||||
from rf4_research.community_sources import parse_rf4db_catches, parse_rf4map_point, parse_rf4posts_spot
|
||||
|
||||
|
||||
FIXTURE = Path(__file__).parents[3] / "tests" / "fixtures" / "rf4db_catches_sample.html"
|
||||
FIXTURES = FIXTURE.parent
|
||||
|
||||
|
||||
def record(source: str = "rf4db", external_id: str = "catch-1") -> dict[str, object]:
|
||||
urls = {
|
||||
"rf4db": f"https://rf4db.com/ru/catches/{external_id}",
|
||||
"rf4stat-fishing": f"https://rf4-stat.ru/fishing/{external_id}",
|
||||
"rf4map": f"https://rf4map.ru/points/{external_id}",
|
||||
"rf4posts-spot": f"https://rf4-posts.com/ru/spots/{external_id}",
|
||||
}
|
||||
return {
|
||||
"source_system": source,
|
||||
"source_external_id": external_id,
|
||||
"source_url": f"https://rf4db.com/ru/catches/{external_id}" if source == "rf4db" else f"https://rf4-stat.ru/fishing/{external_id}",
|
||||
"source_url": urls.get(source, f"https://rf4-stat.ru/fishing/{external_id}"),
|
||||
"fish": "Щука",
|
||||
"fish_external_id": "pike",
|
||||
"waterbody": "Тестовое озеро",
|
||||
@@ -66,6 +73,14 @@ def test_external_ids_are_isolated_by_source(db: Session) -> None:
|
||||
assert (created, updated) == (2, 0)
|
||||
|
||||
|
||||
def test_research_sources_can_enter_disabled_staging(db: Session) -> None:
|
||||
created, updated = stage_observations(db, [record("rf4map"), record("rf4posts-spot")])
|
||||
|
||||
assert (created, updated) == (2, 0)
|
||||
assert db.get(DataSource, "rf4map").enabled is False
|
||||
assert db.get(DataSource, "rf4posts-spot").enabled is False
|
||||
|
||||
|
||||
def test_parser_json_can_be_staged_without_losing_provenance(db: Session) -> None:
|
||||
parsed = parse_rf4db_catches(FIXTURE.read_text(encoding="utf-8"))
|
||||
payload = json.loads(json.dumps([asdict(item) for item in parsed], default=str))
|
||||
@@ -76,6 +91,26 @@ def test_parser_json_can_be_staged_without_losing_provenance(db: Session) -> Non
|
||||
assert (item.source_system, item.fish_external_id, item.x, item.y) == ("rf4db", "pike", 71, 92)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("fixture_name", "source_url", "parser"), [
|
||||
("rf4map_point_sample.html", "https://rf4map.ru/points/275", parse_rf4map_point),
|
||||
(
|
||||
"rf4posts_spot_sample.html",
|
||||
"https://rf4-posts.com/ru/spots/d0c6d9c6-4ebf-49a7-98a8-9a562553a8ee",
|
||||
parse_rf4posts_spot,
|
||||
),
|
||||
])
|
||||
def test_new_parser_json_can_be_staged(
|
||||
db: Session, fixture_name: str, source_url: str, parser,
|
||||
) -> None:
|
||||
parsed = parser((FIXTURES / fixture_name).read_text(encoding="utf-8"), source_url=source_url)
|
||||
payload = json.loads(json.dumps([asdict(item) for item in parsed], default=str))
|
||||
|
||||
created, updated = stage_observations(db, payload)
|
||||
|
||||
assert (created, updated) == (len(parsed), 0)
|
||||
assert db.scalar(select(func.count()).select_from(ExternalObservation)) == len(parsed)
|
||||
|
||||
|
||||
def test_invalid_source_rolls_back_caller_transaction(db: Session) -> None:
|
||||
with pytest.raises(CommunityImportError, match="unsupported source_system"):
|
||||
stage_observations(db, [record("unknown")])
|
||||
|
||||
Reference in New Issue
Block a user