gendesign/tradein-mvp/backend/tests/test_admin_yandex_session_endpoints.py
bot-backend 5be64c6688
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m18s
Deploy Trade-In / test (push) Successful in 4m4s
Deploy Trade-In / build-backend (push) Successful in 1m36s
Deploy Trade-In / deploy (push) Successful in 2m0s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
fix(tradein/scrapers): хранилище авторизованной сессии Яндекс.Недвижимости (#3195)
2026-08-28 19:02:05 +00:00

168 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Offline tests для Yandex session-cookie admin-эндпоинтов (#3192 MVP).
Покрытие 2 эндпоинтов (db мокается, NO live network/DB), зеркалит паттерн
test_domclick_admin_apis.py / test_admin_cian_session_endpoints.py
(dependency_overrides[get_db] + TestClient):
- POST /api/v1/admin/scrape/yandex/upload-cookies
- GET /api/v1/admin/scrape/yandex/session-status
Про app-уровневый 403/401 для не-админа: намеренно НЕ тестируется — как и у
Cian/DomClick-соседей, admin.py не гейтит auth сам (см. модульный docstring
"Auth: Caddy basic_auth гейтит /trade-in/api/v1/admin/* — application layer
открыт"), TestClient здесь Caddy обходит по конструкции. Это не регрессия
именно этого эндпоинта, а свойство всего роутера.
"""
from __future__ import annotations
import os
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
from datetime import UTC, datetime, timedelta
from unittest.mock import MagicMock, patch
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
@pytest.fixture
def client() -> TestClient:
from app.api.v1 import admin as admin_module
from app.core.db import get_db
app = FastAPI()
app.include_router(admin_module.router, prefix="/api/v1/admin")
def fake_db():
yield MagicMock()
app.dependency_overrides[get_db] = fake_db
return TestClient(app)
_UPLOAD_URL = "/api/v1/admin/scrape/yandex/upload-cookies"
_STATUS_URL = "/api/v1/admin/scrape/yandex/session-status"
_SESSION_ID_COOKIE = {"name": "Session_id", "value": "abc", "domain": ".yandex.ru", "path": "/"}
_YANDEXUID_COOKIE = {"name": "yandexuid", "value": "def", "domain": ".yandex.ru", "path": "/"}
_YM_COOKIE = {"name": "_ym_uid", "value": "ghi", "domain": ".yandex.ru", "path": "/"}
# ── POST /scrape/yandex/upload-cookies ────────────────────────────────────────
def test_upload_cookies_503_when_no_encryption_key(client: TestClient) -> None:
with patch("app.api.v1.admin.settings.cookie_encryption_key", ""):
resp = client.post(
_UPLOAD_URL,
json={"account_label": "acc", "cookies": [_SESSION_ID_COOKIE]},
)
assert resp.status_code == 503
def test_upload_cookies_400_when_no_session_id_marker(client: TestClient) -> None:
"""Дамп без Session_id — похоже на анонимную сессию, отказ, ничего не сохраняем."""
with (
patch("app.api.v1.admin.settings.cookie_encryption_key", "test-key"),
patch("app.api.v1.admin.yandex_session_svc.save_session") as mock_save,
):
resp = client.post(
_UPLOAD_URL,
json={"account_label": "acc", "cookies": [_YANDEXUID_COOKIE, _YM_COOKIE]},
)
assert resp.status_code == 400
assert "Session_id" in resp.json()["detail"]
mock_save.assert_not_called()
def test_upload_cookies_400_when_account_label_empty(client: TestClient) -> None:
with patch("app.api.v1.admin.settings.cookie_encryption_key", "test-key"):
resp = client.post(
_UPLOAD_URL,
json={"account_label": " ", "cookies": [_SESSION_ID_COOKIE]},
)
assert resp.status_code == 400
assert "account_label" in resp.json()["detail"]
def test_upload_cookies_success_filters_analytics_and_saves(client: TestClient) -> None:
with (
patch("app.api.v1.admin.settings.cookie_encryption_key", "test-key"),
patch("app.api.v1.admin.yandex_session_svc.save_session") as mock_save,
):
resp = client.post(
_UPLOAD_URL,
json={
"account_label": "my-account",
"cookies": [_SESSION_ID_COOKIE, _YANDEXUID_COOKIE, _YM_COOKIE],
},
)
assert resp.status_code == 200
body = resp.json()
assert body == {"ok": True, "accountLabel": "my-account", "cookieCount": 2}
mock_save.assert_called_once()
_, kwargs = mock_save.call_args
assert kwargs["account_label"] == "my-account"
saved_names = {c["name"] for c in kwargs["cookies"]}
assert saved_names == {"Session_id", "yandexuid"} # _ym_uid отфильтрован
# ── GET /scrape/yandex/session-status ─────────────────────────────────────────
def test_session_status_no_key_returns_no_session(client: TestClient) -> None:
with patch("app.api.v1.admin.settings.cookie_encryption_key", ""):
resp = client.get(_STATUS_URL)
assert resp.status_code == 200
assert resp.json() == {"hasSession": False, "expiresAt": None, "expiresSoon": False}
def test_session_status_no_session_in_db(client: TestClient) -> None:
with (
patch("app.api.v1.admin.settings.cookie_encryption_key", "test-key"),
patch("app.api.v1.admin.yandex_session_svc.load_session", return_value=None),
patch("app.api.v1.admin.yandex_session_svc.session_expires_at", return_value=None),
):
resp = client.get(_STATUS_URL)
assert resp.status_code == 200
assert resp.json() == {"hasSession": False, "expiresAt": None, "expiresSoon": False}
def test_session_status_valid_session_far_from_expiry(client: TestClient) -> None:
far_future = datetime.now(UTC) + timedelta(days=20)
with (
patch("app.api.v1.admin.settings.cookie_encryption_key", "test-key"),
patch(
"app.api.v1.admin.yandex_session_svc.load_session",
return_value=[_SESSION_ID_COOKIE],
),
patch(
"app.api.v1.admin.yandex_session_svc.session_expires_at",
return_value=far_future,
),
):
resp = client.get(_STATUS_URL)
assert resp.status_code == 200
body = resp.json()
assert body["hasSession"] is True
assert body["expiresSoon"] is False
def test_session_status_expiring_soon(client: TestClient) -> None:
soon = datetime.now(UTC) + timedelta(days=2)
with (
patch("app.api.v1.admin.settings.cookie_encryption_key", "test-key"),
patch(
"app.api.v1.admin.yandex_session_svc.load_session",
return_value=[_SESSION_ID_COOKIE],
),
patch("app.api.v1.admin.yandex_session_svc.session_expires_at", return_value=soon),
):
resp = client.get(_STATUS_URL)
assert resp.status_code == 200
body = resp.json()
assert body["hasSession"] is True
assert body["expiresSoon"] is True