97 lines
4.5 KiB
Python
97 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy.pool import StaticPool
|
|
|
|
from app.database import Base, get_session
|
|
from app.main import app
|
|
from app.models import Bait, BaitKind, CatchReport, Fish, ModerationStatus, SourceType, Spot, Waterbody
|
|
|
|
|
|
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
|
|
Base.metadata.create_all(engine)
|
|
|
|
|
|
def override_session():
|
|
with Session(engine) as session:
|
|
yield session
|
|
|
|
|
|
app.dependency_overrides[get_session] = override_session
|
|
client = TestClient(app)
|
|
|
|
|
|
def setup_module() -> None:
|
|
with Session(engine) as db:
|
|
waterbody = Waterbody(slug="test-lake", name_ru="Тестовое озеро", unlock_level=1)
|
|
fish = Fish(slug="pike", name_ru="Щука", trophy_weight_g=10_000)
|
|
bait = Bait(name="Тестовая приманка", normalized_name="тестовая приманка", kind=BaitKind.lure)
|
|
spot = Spot(waterbody=waterbody, x=10, y=20, description="Тестовая точка")
|
|
db.add_all([waterbody, fish, bait, spot])
|
|
now = datetime.now(timezone.utc)
|
|
for index in range(3):
|
|
db.add(CatchReport(fish=fish, spot=spot, waterbody=waterbody, bait=bait, weight_g=3000 + index * 1000, fishing_method="spinning", reported_at=now - timedelta(hours=index), caught_at=now - timedelta(hours=index), player_name=f"Player {index}", source_type=SourceType.manual_import, source_confidence=90, moderation_status=ModerationStatus.approved))
|
|
db.commit()
|
|
|
|
|
|
def test_activity_filters_and_explains_score() -> None:
|
|
response = client.get("/api/v1/activity?waterbody=test-lake&fish=pike&hours=24")
|
|
assert response.status_code == 200
|
|
payload = response.json()
|
|
assert len(payload) == 1
|
|
assert payload[0]["catches"] == 3
|
|
assert payload[0]["unique_players"] == 3
|
|
assert "3 свежих уловов" in payload[0]["explanation"]
|
|
|
|
|
|
def test_invalid_period_is_rejected() -> None:
|
|
assert client.get("/api/v1/activity?hours=13").status_code == 422
|
|
|
|
|
|
def test_spot_detail_and_catches() -> None:
|
|
spot_id = client.get("/api/v1/activity").json()[0]["spot_id"]
|
|
detail = client.get(f"/api/v1/spots/{spot_id}")
|
|
catches = client.get(f"/api/v1/spots/{spot_id}/catches")
|
|
assert detail.status_code == 200
|
|
assert detail.json()["catches_24h"] == 3
|
|
assert catches.status_code == 200
|
|
assert len(catches.json()) == 3
|
|
|
|
|
|
def test_records_list_is_empty_before_import() -> None:
|
|
response = client.get("/api/v1/records")
|
|
assert response.status_code == 200
|
|
assert response.json() == []
|
|
|
|
|
|
def test_user_report_requires_moderation_before_activity() -> None:
|
|
created = client.post("/api/v1/catch-reports", json={"fish_slug": "pike", "waterbody_slug": "test-lake", "x": 77, "y": 88, "weight_g": 5500, "bait_name": "Новая приманка", "player_name": "Reporter"})
|
|
assert created.status_code == 201
|
|
assert created.json()["moderation_status"] == "pending"
|
|
report_id = created.json()["id"]
|
|
headers = {"Authorization": "Bearer change-me-in-production"}
|
|
pending = client.get("/api/v1/admin/catch-reports", headers=headers)
|
|
assert pending.status_code == 200
|
|
assert any(item["id"] == report_id for item in pending.json())
|
|
approved = client.patch(f"/api/v1/admin/catch-reports/{report_id}", headers=headers, json={"status": "approved", "reason": "fixture verified"})
|
|
assert approved.status_code == 200
|
|
activity = client.get("/api/v1/activity?waterbody=test-lake&fish=pike&hours=24").json()
|
|
assert any(item["x"] == 77 and item["catches"] == 1 for item in activity)
|
|
|
|
|
|
def test_admin_requires_token() -> None:
|
|
assert client.get("/api/v1/admin/catch-reports").status_code == 401
|
|
|
|
|
|
def test_pending_report_accepts_one_validated_screenshot(monkeypatch) -> None:
|
|
created = client.post("/api/v1/catch-reports", json={"fish_slug": "pike", "waterbody_slug": "test-lake", "x": 91, "y": 92, "weight_g": 4200}).json()
|
|
monkeypatch.setattr("app.main.upload_screenshot", lambda raw: "reports/test.jpg" if raw == b"image-bytes" else "unexpected")
|
|
response = client.post(f"/api/v1/catch-reports/{created['id']}/screenshot", files={"screenshot": ("catch.jpg", b"image-bytes", "image/jpeg")})
|
|
assert response.status_code == 204
|
|
duplicate = client.post(f"/api/v1/catch-reports/{created['id']}/screenshot", files={"screenshot": ("catch.jpg", b"image-bytes", "image/jpeg")})
|
|
assert duplicate.status_code == 409
|