refactor(parsers): перенести тесты в ROOT_DIR/tests и синхронизировать контракты задач
- перенесены тесты parsers из src/apps/parsers/tests в tests/apps/parsers - обновлены тесты задач под текущее поведение Celery (ошибки пробрасываются исключениями) - убрана зависимость тестов от внешнего брокера через локальные eager-вызовы - добавлены/уточнены фабрики и импорты для единой структуры тестов - обновлены README и CHANGELOG с новым правилом размещения тестов и запуском
This commit is contained in:
561
tests/apps/parsers/test_checko_e2e.py
Normal file
561
tests/apps/parsers/test_checko_e2e.py
Normal file
@@ -0,0 +1,561 @@
|
||||
"""
|
||||
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))
|
||||
Reference in New Issue
Block a user