All checks were successful
Deploy Trade-In / test (push) Successful in 49s
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m13s
Deploy Trade-In / build-backend (push) Successful in 1m32s
Deploy Trade-In / deploy (push) Successful in 7m16s
167 lines
5.7 KiB
Python
167 lines
5.7 KiB
Python
"""Tests for domclick_session — cookie management service (MVP, #2000).
|
|
|
|
Зеркалит test_cian_session.py (MagicMock db, никакого live DB) — без verify_session,
|
|
т.к. domclick_session MVP её не реализует (см. модуль docstring).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from app.services.domclick_session import (
|
|
DOMCLICK_REQUIRED_COOKIES,
|
|
load_session,
|
|
mark_session_invalid,
|
|
save_session,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixtures
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.fixture()
|
|
def mock_db() -> MagicMock:
|
|
db = MagicMock()
|
|
# Default: no row found (load_session → None)
|
|
db.execute.return_value.mappings.return_value.first.return_value = None
|
|
return db
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# DOMCLICK_REQUIRED_COOKIES
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_required_cookies_set_not_empty() -> None:
|
|
assert isinstance(DOMCLICK_REQUIRED_COOKIES, set)
|
|
assert len(DOMCLICK_REQUIRED_COOKIES) >= 5
|
|
|
|
|
|
def test_required_cookies_contains_key_names() -> None:
|
|
assert "CAS_ID" in DOMCLICK_REQUIRED_COOKIES
|
|
assert "qrator_jsid2" in DOMCLICK_REQUIRED_COOKIES
|
|
assert "sessionId" in DOMCLICK_REQUIRED_COOKIES
|
|
assert "_ym_uid" in DOMCLICK_REQUIRED_COOKIES
|
|
|
|
|
|
def test_required_cookies_includes_real_sber_id_dump() -> None:
|
|
"""Real Sber-ID authenticated test-account DevTools export (2026-07-04)."""
|
|
real_cookies_from_browser = {
|
|
"qrator_jsid2",
|
|
"CAS_ID",
|
|
"CAS_ID_SIGNED",
|
|
"sessionId",
|
|
"UNIQ_SESSION_ID",
|
|
"_visitId",
|
|
"ns_session",
|
|
}
|
|
missing = real_cookies_from_browser - DOMCLICK_REQUIRED_COOKIES
|
|
assert not missing, f"Filter missing real cookies: {missing}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# save_session
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_save_session_calls_pgp_sym_encrypt(mock_db: MagicMock) -> None:
|
|
save_session(mock_db, account_cas_id=12345, cookies={"CAS_ID": "12345"})
|
|
args, _ = mock_db.execute.call_args
|
|
sql_text = str(args[0])
|
|
assert "pgp_sym_encrypt" in sql_text
|
|
assert "domclick_session_cookies" in sql_text
|
|
|
|
|
|
def test_save_session_uses_cast_not_colon_colon(mock_db: MagicMock) -> None:
|
|
"""Verify psycopg v3 compatibility — no ::bigint syntax in SQL."""
|
|
save_session(mock_db, account_cas_id=99, cookies={"CAS_ID": "99"})
|
|
args, _ = mock_db.execute.call_args
|
|
sql_text = str(args[0])
|
|
assert "CAST(" in sql_text
|
|
assert "::bigint" not in sql_text
|
|
|
|
|
|
def test_save_session_commits(mock_db: MagicMock) -> None:
|
|
save_session(mock_db, account_cas_id=12345, cookies={"CAS_ID": "12345"})
|
|
assert mock_db.commit.called
|
|
|
|
|
|
def test_save_session_on_conflict_do_update(mock_db: MagicMock) -> None:
|
|
save_session(mock_db, account_cas_id=1, cookies={"CAS_ID": "1"})
|
|
args, _ = mock_db.execute.call_args
|
|
sql_text = str(args[0])
|
|
assert "ON CONFLICT" in sql_text
|
|
assert "DO UPDATE" in sql_text
|
|
|
|
|
|
def test_save_session_passes_correct_params(mock_db: MagicMock) -> None:
|
|
save_session(mock_db, account_cas_id=777, cookies={"qrator_jsid2": "x"}, ttl_days=14)
|
|
call_args = mock_db.execute.call_args
|
|
params = call_args[0][1] if len(call_args[0]) > 1 else call_args[1].get("params", {})
|
|
assert params["cas_id"] == 777
|
|
assert params["ttl_days"] == 14
|
|
cookies_payload = json.loads(params["cookies_json"])
|
|
assert cookies_payload == {"qrator_jsid2": "x"}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# load_session
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_load_session_returns_none_when_empty(mock_db: MagicMock) -> None:
|
|
result = load_session(mock_db)
|
|
assert result is None
|
|
|
|
|
|
def test_load_session_decodes_json(mock_db: MagicMock) -> None:
|
|
mock_row = {
|
|
"account_cas_id": 12345,
|
|
"cookies_json": json.dumps({"CAS_ID": "12345", "qrator_jsid2": "abc"}),
|
|
"expires_at_estimate": None,
|
|
}
|
|
mock_db.execute.return_value.mappings.return_value.first.return_value = mock_row
|
|
result = load_session(mock_db)
|
|
assert result == {"CAS_ID": "12345", "qrator_jsid2": "abc"}
|
|
|
|
|
|
def test_load_session_updates_last_used_at(mock_db: MagicMock) -> None:
|
|
mock_row = {
|
|
"account_cas_id": 42,
|
|
"cookies_json": json.dumps({"sessionId": "s"}),
|
|
"expires_at_estimate": None,
|
|
}
|
|
mock_db.execute.return_value.mappings.return_value.first.return_value = mock_row
|
|
load_session(mock_db)
|
|
# Должно быть минимум 2 вызова execute: SELECT + UPDATE last_used_at
|
|
assert mock_db.execute.call_count >= 2
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# mark_session_invalid
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_mark_session_invalid_updates_table(mock_db: MagicMock) -> None:
|
|
mark_session_invalid(mock_db, account_cas_id=12345)
|
|
args, _ = mock_db.execute.call_args
|
|
sql = str(args[0])
|
|
assert "last_invalid_at" in sql
|
|
assert "domclick_session_cookies" in sql
|
|
|
|
|
|
def test_mark_session_invalid_commits(mock_db: MagicMock) -> None:
|
|
mark_session_invalid(mock_db, account_cas_id=99)
|
|
assert mock_db.commit.called
|
|
|
|
|
|
def test_mark_session_invalid_uses_cast(mock_db: MagicMock) -> None:
|
|
mark_session_invalid(mock_db, account_cas_id=5)
|
|
args, _ = mock_db.execute.call_args
|
|
sql = str(args[0])
|
|
assert "CAST(" in sql
|
|
assert "::bigint" not in sql
|