from __future__ import annotations import unittest from mobilede_scraper.core.utils import deep_find_key from mobilede_scraper.core.config import ( MOBILEDE_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(MOBILEDE_DEFAULT_MAKES) - len(_LARGE_MAKES)) self.assertEqual(len(segs), expected) # Все бренды из списка присутствуют. makes_in_segments = {s["make"] for s in segs} for make in MOBILEDE_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()