Files
mostovik-backend/tests/apps/parsers/test_checko_e2e.py
Aleksandr Meshchriakov 052389d921 refactor(parsers): перенести тесты в ROOT_DIR/tests и синхронизировать контракты задач
- перенесены тесты parsers из src/apps/parsers/tests в tests/apps/parsers

- обновлены тесты задач под текущее поведение Celery (ошибки пробрасываются исключениями)

- убрана зависимость тестов от внешнего брокера через локальные eager-вызовы

- добавлены/уточнены фабрики и импорты для единой структуры тестов

- обновлены README и CHANGELOG с новым правилом размещения тестов и запуском
2026-03-04 15:35:50 +01:00

562 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
E2E тесты для Checko API клиента.
Тесты выполняются через локальный HTTP сервер (без внешних API).
"""
from __future__ import annotations
import json
from urllib.parse import parse_qs
from apps.parsers.clients.checko import (
CheckoAPIError,
CheckoClient,
CheckoNotFoundError,
CompanyRequest,
ContractLaw,
ContractsRequest,
EnforcementsRequest,
FinancesRequest,
InspectionsRequest,
LegalCasesRequest,
ObjectType,
SearchRequest,
SearchType,
)
from apps.parsers.clients.checko.datasets import (
OKFS,
OKOPF,
OKVED2,
AccountCodes,
)
from django.test import TestCase
from tests.utils import Response, TestHTTPServer
from tests.utils.fixtures import fake
def _digits(length: int) -> str:
return "".join(str(fake.random_int(0, 9)) for _ in range(length))
def _meta_ok() -> dict:
return {
"status": "ok",
"today_request_count": fake.random_int(min=1, max=10),
"balance": float(fake.pydecimal(left_digits=2, right_digits=2, positive=True)),
}
def _client_for(server: TestHTTPServer, api_key: str = "test_key") -> CheckoClient:
return CheckoClient(
api_key=api_key,
base_url=f"{server.base_url}/v2",
http_adapter=server.adapter,
)
def _json_response(payload: dict) -> Response:
return Response(
status=200,
body=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
headers={"Content-Type": "application/json; charset=utf-8"},
)
class CheckoClientE2ETestCase(TestCase):
"""E2E тесты клиента CheckoClient через локальный HTTP сервер."""
def test_get_company_by_inn(self):
inn = _digits(10)
ogrn = _digits(13)
short_name = fake.company()
with TestHTTPServer() as server:
server.add_json(
"/v2/company",
{
"data": {
"ogrn": ogrn,
"inn": inn,
"short_name": short_name,
"full_name": fake.company(),
"status": {"code": "100", "name": "Действующее"},
"legal_address": {
"full_address": fake.address().replace("\n", ", "),
"postal_code": _digits(6),
},
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_company(CompanyRequest(inn=inn))
self.assertEqual(response.meta.status, "ok")
self.assertEqual(response.data.inn, inn)
self.assertEqual(response.data.ogrn, ogrn)
self.assertEqual(response.data.short_name, short_name)
self.assertIsNotNone(response.data.legal_address)
def test_get_company_by_ogrn(self):
inn = _digits(10)
ogrn = _digits(13)
with TestHTTPServer() as server:
server.add_json(
"/v2/company",
{
"data": {
"ogrn": ogrn,
"inn": inn,
"short_name": fake.company(),
"status": {"code": "100", "name": "Действующее"},
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_company(CompanyRequest(ogrn=ogrn))
self.assertEqual(response.meta.status, "ok")
self.assertEqual(response.data.ogrn, ogrn)
self.assertEqual(response.data.inn, inn)
def test_get_company_with_source(self):
inn = _digits(10)
source_data = {"raw": fake.text(max_nb_chars=50)}
with TestHTTPServer() as server:
server.add_json(
"/v2/company",
{
"data": {
"inn": inn,
"ogrn": _digits(13),
"short_name": fake.company(),
"status": {"code": "100", "name": "Действующее"},
},
"meta": _meta_ok(),
"source_data": source_data,
},
)
client = _client_for(server)
response = client.get_company(CompanyRequest(inn=inn, source=True))
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.source_data)
def test_search_by_name(self):
inn = _digits(10)
ogrn = _digits(13)
name = fake.company()
with TestHTTPServer() as server:
server.add_json(
"/v2/search",
{
"data": {
"records": [
{"inn": inn, "ogrn": ogrn, "short_name": name},
],
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.search(
SearchRequest(
by=SearchType.NAME,
obj=ObjectType.ORGANIZATION,
query=f"{name} {fake.word()}",
limit=10,
)
)
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
self.assertGreater(len(response.data.organizations), 0)
def test_search_active_only(self):
inn = _digits(10)
with TestHTTPServer() as server:
server.add_json(
"/v2/search",
{
"data": {
"records": [
{
"inn": inn,
"status": {"code": "100", "name": "Действующее"},
},
],
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.search(
SearchRequest(
by=SearchType.NAME,
obj=ObjectType.ORGANIZATION,
query=fake.company(),
active=True,
limit=5,
)
)
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
def test_get_finances(self):
inn = _digits(10)
ogrn = _digits(13)
with TestHTTPServer() as server:
server.add_json(
"/v2/finances",
{
"data": {
"inn": inn,
"ogrn": ogrn,
"reports": [
{
"year": fake.random_int(min=2020, max=2025),
"period": 12,
"lines": [
{
"code": _digits(4),
"value": fake.random_int(10, 9999),
}
],
}
],
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_finances(FinancesRequest(inn=inn))
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
self.assertEqual(response.data.inn, inn)
def test_get_contracts_fz44(self):
inn = _digits(10)
registry_number = _digits(12)
with TestHTTPServer() as server:
server.add_json(
"/v2/contracts",
{
"data": {
"contracts": [
{"registry_number": registry_number, "law": "44-FZ"},
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_contracts(
ContractsRequest(inn=inn, law=ContractLaw.FZ44, limit=10)
)
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
self.assertGreater(len(response.data.contracts), 0)
def test_get_contracts_fz223(self):
inn = _digits(10)
with TestHTTPServer() as server:
server.add_json(
"/v2/contracts",
{
"data": {
"contracts": [
{"registry_number": _digits(12), "law": "223-FZ"},
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_contracts(
ContractsRequest(inn=inn, law=ContractLaw.FZ223, limit=10)
)
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
def test_get_inspections(self):
inn = _digits(10)
with TestHTTPServer() as server:
server.add_json(
"/v2/inspections",
{
"data": {
"inspections": [
{
"id": fake.uuid4(),
"status": fake.word(),
"authority_name": fake.company(),
"subject": fake.word(),
}
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_inspections(InspectionsRequest(inn=inn, limit=10))
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
self.assertGreater(len(response.data.inspections), 0)
def test_get_enforcements(self):
inn = _digits(10)
with TestHTTPServer() as server:
server.add_json(
"/v2/enforcements",
{
"data": {
"enforcements": [
{
"number": _digits(12),
"status": fake.word(),
"debt_amount": fake.random_int(1_000, 10_000),
}
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
"total_debt": fake.random_int(1_000, 100_000),
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_enforcements(EnforcementsRequest(inn=inn, limit=10))
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
self.assertGreater(len(response.data.enforcements), 0)
def test_get_legal_cases(self):
inn = _digits(10)
with TestHTTPServer() as server:
server.add_json(
"/v2/legal-cases",
{
"data": {
"cases": [
{
"case_number": fake.bothify(text="A-####/##"),
"status": fake.word(),
},
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
"total_claim_amount": fake.random_int(1000, 10000),
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_legal_cases(LegalCasesRequest(inn=inn, limit=10))
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
self.assertGreater(len(response.data.cases), 0)
def test_get_legal_cases_filtered(self):
inn = _digits(10)
with TestHTTPServer() as server:
server.add_json(
"/v2/legal-cases",
{
"data": {
"cases": [
{
"case_number": fake.bothify(text="A-####/##"),
"status": fake.word(),
},
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
},
},
"meta": _meta_ok(),
},
)
client = _client_for(server)
response = client.get_legal_cases(
LegalCasesRequest(inn=inn, actual=True, active=True, limit=5)
)
self.assertEqual(response.meta.status, "ok")
self.assertIsNotNone(response.data)
class CheckoClientIteratorsE2ETestCase(TestCase):
"""E2E тесты итераторов с автопагинацией."""
def test_iter_contracts_pagination(self):
inn = _digits(10)
def contracts_handler(request, _body):
page = int(parse_qs(request.query).get("page", ["1"])[0])
total_pages = 3
contracts = [
{"registry_number": f"{page}-{_digits(10)}", "law": "44-FZ"},
]
return _json_response(
{
"data": {
"contracts": contracts,
"pagination": {
"total_records": total_pages,
"total_pages": total_pages,
"current_page": page,
},
},
"meta": _meta_ok(),
}
)
with TestHTTPServer() as server:
server.add_route("GET", "/v2/contracts", contracts_handler)
client = _client_for(server)
contracts = list(
client.iter_contracts(
ContractsRequest(inn=inn, law=ContractLaw.FZ44, limit=1)
)
)
self.assertGreater(len(contracts), 1)
def test_iter_legal_cases_pagination(self):
inn = _digits(10)
def cases_handler(request, _body):
page = int(parse_qs(request.query).get("page", ["1"])[0])
total_pages = 2
cases = [
{"case_number": f"A-{page}-{_digits(5)}", "status": fake.word()},
]
return _json_response(
{
"data": {
"cases": cases,
"pagination": {
"total_records": total_pages,
"total_pages": total_pages,
"current_page": page,
},
},
"meta": _meta_ok(),
}
)
with TestHTTPServer() as server:
server.add_route("GET", "/v2/legal-cases", cases_handler)
client = _client_for(server)
cases = list(client.iter_legal_cases(LegalCasesRequest(inn=inn, limit=1)))
self.assertGreater(len(cases), 1)
class CheckoClientErrorE2ETestCase(TestCase):
"""E2E тесты обработки ошибок."""
def test_company_not_found(self):
with TestHTTPServer() as server:
server.add_json(
"/v2/company",
{
"data": None,
"meta": {"status": "error", "message": "не найден"},
},
)
client = _client_for(server)
with self.assertRaises(CheckoNotFoundError):
client.get_company(CompanyRequest(inn=_digits(10)))
def test_invalid_api_key(self):
with TestHTTPServer() as server:
server.add_json(
"/v2/company",
{
"data": None,
"meta": {"status": "error", "message": "Invalid API key"},
},
)
client = _client_for(server, api_key="invalid_key")
with self.assertRaises(CheckoAPIError):
client.get_company(CompanyRequest(inn=_digits(10)))
class CheckoDatasetsE2ETestCase(TestCase):
"""E2E тесты справочников (локальные JSON данные)."""
def test_okved2_load_and_search(self):
items = OKVED2.all()
self.assertGreater(len(items), 0)
sample = items[0]
self.assertIsNotNone(OKVED2.get(sample.code))
search_term = sample.name.split(" ")[0]
results = OKVED2.search(search_term)
self.assertGreater(len(results), 0)
def test_okfs_load(self):
items = OKFS.all()
self.assertGreater(len(items), 0)
def test_okopf_load(self):
items = OKOPF.all()
self.assertGreater(len(items), 0)
def test_account_codes_load(self):
items = AccountCodes.all()
self.assertGreater(len(items), 0)
sample = items[0]
self.assertIsNotNone(AccountCodes.get(sample.code))