"""Тесты pure-парсера CBR KeyRate (offline, без живой сети). Покрывают: парсинг (date, value), обработку tz-суффикса, сортировку, пустой / битый / без-KR XML → []. Фикстуры — из реального формата KeyRateXML-ответа DailyInfoWebServ (verified live 2026-06-02). """ from __future__ import annotations from datetime import date from app.services.scrapers.cbr_macro import parse_key_rate_xml # Реальный shape KeyRateXML (без diffgram). 3 строки, descending по дате, # tz-суффикс +03:00, точка как десятичный разделитель. FIXTURE_KEYRATE_XML = ( '' '' "" '' "" '' "
2023-09-15T00:00:00+03:00
12.00
" "
2023-08-15T00:00:00+03:00
12.00
" "
2023-08-14T00:00:00+03:00
8.50
" "
" "
" "
" "
" "
" ) # Diffgram-вариант (метод KeyRate) — парсер должен его тоже понимать (находит KR # где угодно), несмотря на schema-блок и namespace на KR. FIXTURE_KEYRATE_DIFFGRAM = ( '' "" '' '' '' "
2023-09-29T00:00:00+03:00
13.00
" '' "
2023-09-15T00:00:00+03:00
12.00
" "
" "
" ) def test_parse_basic_rows() -> None: """3 KR-строки → 3 кортежа (date, float), отсортированы по возрастанию даты.""" rows = parse_key_rate_xml(FIXTURE_KEYRATE_XML) assert rows == [ (date(2023, 8, 14), 8.5), (date(2023, 8, 15), 12.0), (date(2023, 9, 15), 12.0), ] def test_parse_returns_sorted_ascending() -> None: """Вход descending — выход обязан быть ascending по дате.""" rows = parse_key_rate_xml(FIXTURE_KEYRATE_XML) dates = [d for d, _ in rows] assert dates == sorted(dates) def test_parse_strips_timezone_suffix() -> None: """DT с tz-суффиксом '+03:00' парсится в чистую дату (без сдвига).""" rows = parse_key_rate_xml(FIXTURE_KEYRATE_XML) assert (date(2023, 8, 15), 12.0) in rows # значение float, не строка assert all(isinstance(v, float) for _, v in rows) def test_parse_value_types() -> None: """Дробная ставка 8.50 → 8.5 (float).""" rows = dict(parse_key_rate_xml(FIXTURE_KEYRATE_XML)) assert rows[date(2023, 8, 14)] == 8.5 def test_parse_diffgram_variant() -> None: """Diffgram-обёртка (метод KeyRate) тоже парсится — KR находится в любом namespace / вложенности.""" rows = parse_key_rate_xml(FIXTURE_KEYRATE_DIFFGRAM) assert rows == [ (date(2023, 9, 15), 12.0), (date(2023, 9, 29), 13.0), ] def test_parse_empty_string() -> None: assert parse_key_rate_xml("") == [] assert parse_key_rate_xml(" ") == [] def test_parse_empty_response_no_kr() -> None: """Валидный, но пустой KeyRateXMLResponse (период без данных) → [].""" xml = ( '' '' "" ) assert parse_key_rate_xml(xml) == [] def test_parse_malformed_xml() -> None: """Битый XML → [] (не бросает).""" assert parse_key_rate_xml("
2023") == [] def test_parse_skips_kr_with_missing_fields() -> None: """KR без DT или без Rate пропускается; валидные остаются.""" xml = ( "" "
2023-08-15T00:00:00+03:00
12.00
" "
2023-08-16T00:00:00+03:00
" # нет Rate "11.00" # нет DT "
" ) assert parse_key_rate_xml(xml) == [(date(2023, 8, 15), 12.0)] def test_parse_dedups_by_date_last_wins() -> None: """Дубли по дате схлопываются — последний выигрывает.""" xml = ( "" "
2023-08-15T00:00:00+03:00
12.00
" "
2023-08-15T00:00:00+03:00
13.00
" "
" ) assert parse_key_rate_xml(xml) == [(date(2023, 8, 15), 13.0)]