114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
from __future__ import annotations
|
||
|
||
import unittest
|
||
|
||
from iaai_scraper.core.utils import deep_find_key
|
||
from iaai_scraper.core.config import (
|
||
IAAI_DEFAULT_MAKES,
|
||
_LARGE_MAKES,
|
||
_YEAR_SPLITS,
|
||
ProxyConfig,
|
||
build_listing_segments_for_makes,
|
||
parse_listing_segments,
|
||
)
|
||
|
||
|
||
class TestDeepFindKey(unittest.TestCase):
|
||
def test_finds_nested_and_respects_depth(self) -> None:
|
||
payload = {
|
||
"root": {
|
||
"target": "a",
|
||
"nested": [{"target": "b"}, {"x": 1}],
|
||
}
|
||
}
|
||
self.assertEqual(deep_find_key(payload, {"target"}), ["a", "b"])
|
||
|
||
deep_payload = {"l1": {"l2": {"l3": {"target": "value"}}}}
|
||
self.assertEqual(deep_find_key(deep_payload, {"target"}, max_depth=2), [])
|
||
self.assertEqual(deep_find_key(deep_payload, {"target"}, max_depth=8), ["value"])
|
||
|
||
|
||
class TestParseListingSegments(unittest.TestCase):
|
||
def test_empty_and_invalid_return_empty(self) -> None:
|
||
self.assertEqual(parse_listing_segments(""), [])
|
||
self.assertEqual(parse_listing_segments(" "), [])
|
||
self.assertEqual(parse_listing_segments("invalid"), [])
|
||
|
||
def test_auto_segments_complete_coverage(self) -> None:
|
||
"""auto: все бренды покрыты, крупные разбиты по годам, годы без дыр."""
|
||
segs = parse_listing_segments("auto")
|
||
|
||
# Точное число сегментов.
|
||
expected = len(_LARGE_MAKES) * len(_YEAR_SPLITS) + (len(IAAI_DEFAULT_MAKES) - len(_LARGE_MAKES))
|
||
self.assertEqual(len(segs), expected)
|
||
|
||
# Все бренды из списка присутствуют.
|
||
makes_in_segments = {s["make"] for s in segs}
|
||
for make in IAAI_DEFAULT_MAKES:
|
||
self.assertIn(make, makes_in_segments)
|
||
|
||
# Крупные бренды разбиты на 3 сегмента с годами.
|
||
toyota = [s for s in segs if s["make"] == "TOYOTA"]
|
||
self.assertEqual(len(toyota), 3)
|
||
for s in toyota:
|
||
self.assertIsNotNone(s["year_min"])
|
||
|
||
# Мелкие бренды без годов.
|
||
lexus = [s for s in segs if s["make"] == "LEXUS"]
|
||
self.assertEqual(len(lexus), 1)
|
||
self.assertIsNone(lexus[0]["year_min"])
|
||
|
||
# Годовые диапазоны покрывают 1950-2027.
|
||
years = set()
|
||
for yr_min, yr_max in _YEAR_SPLITS:
|
||
years.update(range(yr_min, yr_max + 1))
|
||
for year in range(1950, 2027):
|
||
self.assertIn(year, years)
|
||
|
||
def test_json_input_formats(self) -> None:
|
||
# Массив строк.
|
||
segs = parse_listing_segments('["toyota", "ford"]')
|
||
self.assertEqual(len(segs), 2)
|
||
self.assertEqual(segs[0]["make"], "TOYOTA")
|
||
|
||
# Массив объектов с годами.
|
||
segs = parse_listing_segments('[{"make":"BMW","year_min":2020,"year_max":2025}]')
|
||
self.assertEqual(segs[0]["make"], "BMW")
|
||
self.assertEqual(segs[0]["year_min"], 2020)
|
||
self.assertEqual(segs[0]["year_max"], 2025)
|
||
|
||
def test_build_listing_segments_for_makes(self) -> None:
|
||
segs = build_listing_segments_for_makes(["toyota", "Lexus", "TOYOTA", " "])
|
||
|
||
toyota = [s for s in segs if s["make"] == "TOYOTA"]
|
||
lexus = [s for s in segs if s["make"] == "LEXUS"]
|
||
|
||
self.assertEqual(len(toyota), len(_YEAR_SPLITS))
|
||
self.assertEqual(len(lexus), 1)
|
||
self.assertIsNone(lexus[0]["year_min"])
|
||
|
||
|
||
class TestProxyConfig(unittest.TestCase):
|
||
def test_requests_proxy_url_includes_encoded_credentials(self) -> None:
|
||
cfg = ProxyConfig(
|
||
server="http://144.31.139.6:3128",
|
||
username="carsproxy",
|
||
password="pass@word:with/slash",
|
||
)
|
||
|
||
self.assertEqual(
|
||
cfg.to_requests_proxy_url(),
|
||
"http://carsproxy:pass%40word%3Awith%2Fslash@144.31.139.6:3128",
|
||
)
|
||
self.assertEqual(
|
||
cfg.to_requests_proxies(),
|
||
{
|
||
"http": "http://carsproxy:pass%40word%3Awith%2Fslash@144.31.139.6:3128",
|
||
"https": "http://carsproxy:pass%40word%3Awith%2Fslash@144.31.139.6:3128",
|
||
},
|
||
)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|