Skip to content

Commit 7abf178

Browse files
committed
поведенческое тестирование
1 parent 727dc18 commit 7abf178

4 files changed

Lines changed: 64 additions & 20 deletions

File tree

chizhik_api/manager.py

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
import os
3+
from collections import defaultdict
44
from dataclasses import dataclass, field
55
from typing import Any
66

@@ -12,6 +12,11 @@
1212
HumanPage,
1313
api_child_field,
1414
)
15+
from human_requests.network_analyzer.anomaly_sniffer import (
16+
HeaderAnomalySniffer,
17+
WaitHeader,
18+
WaitSource,
19+
)
1520
from human_requests.abstraction import FetchResponse, HttpMethod, Proxy
1621
from playwright.async_api import TimeoutError as PWTimeoutError
1722

@@ -31,6 +36,8 @@ class ChizhikAPI(ApiParent):
3136
"""Время ожидания ответа от сервера в миллисекундах."""
3237
headless: bool = True
3338
"""Запускать браузер в headless режиме?"""
39+
test_mode: bool = False
40+
"""Режим тестирования предполагает более глубокий _warmup который не требуется для обычного использования"""
3441
proxy: str | dict | Proxy | None = field(default_factory=Proxy.from_env)
3542
"""Прокси-сервер для всех запросов (если нужен). По умолчанию берет из окружения (если есть).
3643
Принимает как формат Playwright, так и строчный формат."""
@@ -51,6 +58,11 @@ class ChizhikAPI(ApiParent):
5158
page: HumanPage = field(init=False, repr=False)
5259
"""Внутренний страница сессии браузера"""
5360

61+
unstandard_headers: dict[str, str] = field(init=False, repr=False)
62+
"""Список нестандартных заголовков пойманных при инициализации"""
63+
unstandard_urls: dict[str, list[str]] = field(init=False, repr=False)
64+
"""Список нестандартных заголовков пойманных при инициализации"""
65+
5466
Geolocation: ClassGeolocation = api_child_field(ClassGeolocation)
5567
"""API для работы с геолокацией."""
5668
Catalog: ClassCatalog = api_child_field(ClassCatalog)
@@ -74,30 +86,43 @@ async def _warmup(self) -> None:
7486
proxy=px.as_dict(),
7587
**self.browser_opts,
7688
block_images=True,
89+
i_know_what_im_doing=True,
7790
).start()
7891

7992
self.session = HumanBrowser.replace(br)
8093
self.ctx = await self.session.new_context()
8194
self.page = await self.ctx.new_page()
8295
self.page.on_error_screenshot_path = "screenshot.png"
96+
97+
if self.test_mode:
98+
sniffer = HeaderAnomalySniffer(
99+
include_subresources=True, # или False, если интересны только документы
100+
url_filter=lambda u: u.startswith(self.CATALOG_URL),
101+
)
102+
await sniffer.start(self.ctx)
103+
104+
await self.page.goto(self.MAIN_SITE_URL, wait_until="networkidle")
105+
await self.page.wait_for_selector("next-route-announcer", state="attached")
106+
107+
result_sniffer = await sniffer.complete()
108+
109+
# Результат: {заголовок: [уникальные значения]}
110+
result = defaultdict(set)
111+
112+
# Проходим по всем URL в 'request'
113+
for _url, headers in result_sniffer["request"].items():
114+
for header, values in headers.items():
115+
result[header].update(values) # добавляем значения, set уберёт дубли
116+
117+
# Преобразуем set обратно в list
118+
self.unstandard_headers = {k: list(v)[0] for k, v in result.items()}
119+
self.unstandard_urls = result_sniffer["request"]
120+
83121
await self.page.goto(self.CATALOG_URL, wait_until="networkidle")
84122

85-
ok = False
86-
try_count = 3
87-
while not ok and try_count > 0:
88-
try_count -= 1
89-
try:
90-
await self.page.wait_for_selector(
91-
"pre", timeout=self.timeout_ms, state="attached"
92-
)
93-
ok = True
94-
except PWTimeoutError:
95-
await self.page.reload()
96-
if not ok:
97-
raise RuntimeError(await self.page.content())
98-
99-
# await self.page.wait_for_load_state("networkidle")
100-
# await asyncio.sleep(3)
123+
await self.page.wait_for_selector(
124+
"pre", timeout=self.timeout_ms, state="attached"
125+
)
101126

102127
async def __aexit__(self, *exc):
103128
"""Выход из контекстного менеджера с закрытием сессии."""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "object",
3+
"$schema": "https://json-schema.org/draft/2020-12/schema"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "object",
3+
"$schema": "https://json-schema.org/draft/2020-12/schema"
4+
}

tests/api_test.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import aiohttp
44
import pytest
5-
from human_requests import autotest_depends_on, autotest_hook, autotest_params
5+
from human_requests import autotest_depends_on, autotest_hook, autotest_params, autotest_data
66
from human_requests.abstraction import Proxy
7-
from human_requests.autotest import AutotestCallContext, AutotestContext
7+
from human_requests.autotest import AutotestCallContext, AutotestContext,AutotestDataContext
8+
89
from PIL import Image
910

1011
from chizhik_api import ChizhikAPI
@@ -24,7 +25,7 @@ def anyio_backend():
2425
@pytest.fixture(scope="session")
2526
async def api():
2627
"""Фикстура для инициализации API в рамках сессии."""
27-
async with ChizhikAPI() as api_instance:
28+
async with ChizhikAPI(headless=False, test_mode=True) as api_instance:
2829
yield api_instance
2930

3031

@@ -87,6 +88,16 @@ def _product_info_params(ctx: AutotestCallContext) -> dict[str, int]:
8788
pytest.fail("ProductService.info depends on Catalog.products_list.")
8889

8990

91+
92+
@autotest_data(name="unstandard_headers")
93+
def _unstandard_headers_data(ctx: AutotestDataContext) -> dict[str, Any]:
94+
return ctx.api.unstandard_headers
95+
96+
@autotest_data(name="unstandard_urls")
97+
def _unstandard_urls_data(ctx: AutotestDataContext) -> dict[str, Any]:
98+
return ctx.api.unstandard_urls
99+
100+
90101
async def test_download_image(api):
91102
url = (
92103
"https://chizhik.x5static.net/media/chizhik-assets/categories/icon/"

0 commit comments

Comments
 (0)