256 lines
9.2 KiB
Python
256 lines
9.2 KiB
Python
from __future__ import annotations
|
||
|
||
from decimal import Decimal
|
||
|
||
from apps.parsers.clients.common.schemas import GenericParserItem
|
||
from apps.parsers.clients.trudvsem import TrudvsemClient
|
||
from apps.parsers.clients.vacancies import (
|
||
HHVacanciesClient,
|
||
SuperJobVacanciesClient,
|
||
VacanciesClient,
|
||
VacanciesClientError,
|
||
)
|
||
from apps.parsers.models import ParserLoadLog
|
||
from django.test import SimpleTestCase
|
||
|
||
from tests.utils import TestHTTPServer
|
||
|
||
|
||
class TrudvsemClientTest(SimpleTestCase):
|
||
def test_fetch_vacancies_by_company_inn_uses_company_endpoint(self):
|
||
with TestHTTPServer() as server:
|
||
server.add_json(
|
||
"/vacancies/company/inn/7701000001",
|
||
{
|
||
"results": {
|
||
"vacancies": [
|
||
{
|
||
"vacancy": {
|
||
"id": "trudvsem-1",
|
||
"company": {
|
||
"inn": "7701000001",
|
||
"ogrn": "1027700000001",
|
||
"name": "АО Реестр",
|
||
},
|
||
"job-name": "Инженер",
|
||
"salary": {"from": 100000},
|
||
}
|
||
}
|
||
]
|
||
}
|
||
},
|
||
)
|
||
client = TrudvsemClient(
|
||
base_url=server.base_url,
|
||
http_adapter=server.adapter,
|
||
)
|
||
|
||
records = client.fetch_vacancies(
|
||
company_inn="7701000001",
|
||
limit=100,
|
||
offset=0,
|
||
)
|
||
|
||
self.assertEqual(len(records), 1)
|
||
self.assertEqual(records[0].external_id, "trudvsem-1")
|
||
self.assertEqual(records[0].inn, "7701000001")
|
||
self.assertEqual(records[0].ogrn, "1027700000001")
|
||
|
||
|
||
class HHVacanciesClientTest(SimpleTestCase):
|
||
def test_fetch_vacancies_maps_hh_items_to_generic_records(self):
|
||
with TestHTTPServer() as server:
|
||
server.add_json(
|
||
"/vacancies",
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "123",
|
||
"name": "Инженер",
|
||
"published_at": "2026-05-06T10:00:00+0300",
|
||
"salary": {"from": 120000, "to": 150000},
|
||
"employer": {"id": "77", "name": "АО Пример"},
|
||
"alternate_url": "https://hh.ru/vacancy/123",
|
||
"type": {"name": "Открытая"},
|
||
"archived": False,
|
||
}
|
||
]
|
||
},
|
||
)
|
||
client = HHVacanciesClient(
|
||
base_url=server.base_url,
|
||
http_adapter=server.adapter,
|
||
)
|
||
|
||
records = client.fetch_vacancies(limit=10, offset=0, text="Инженер")
|
||
|
||
self.assertEqual(len(records), 1)
|
||
record = records[0]
|
||
self.assertEqual(record.source, ParserLoadLog.Source.TRUDVSEM)
|
||
self.assertEqual(record.external_id, "hh:123")
|
||
self.assertEqual(record.title, "Инженер")
|
||
self.assertEqual(record.organisation_name, "АО Пример")
|
||
self.assertEqual(record.amount, Decimal("120000"))
|
||
self.assertEqual(record.url, "https://hh.ru/vacancy/123")
|
||
self.assertEqual(record.payload["vacancy_source"], "hh")
|
||
|
||
def test_fetch_vacancies_tolerates_unexpected_nested_shapes(self):
|
||
with TestHTTPServer() as server:
|
||
server.add_json(
|
||
"/vacancies",
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "124",
|
||
"name": "Инженер",
|
||
"salary": "по договоренности",
|
||
"employer": "АО Пример",
|
||
"type": "open",
|
||
}
|
||
]
|
||
},
|
||
)
|
||
client = HHVacanciesClient(
|
||
base_url=server.base_url,
|
||
http_adapter=server.adapter,
|
||
)
|
||
|
||
records = client.fetch_vacancies(limit=10)
|
||
|
||
self.assertEqual(len(records), 1)
|
||
self.assertEqual(records[0].external_id, "hh:124")
|
||
self.assertEqual(records[0].organisation_name, "")
|
||
self.assertIsNone(records[0].amount)
|
||
|
||
|
||
class SuperJobVacanciesClientTest(SimpleTestCase):
|
||
def test_fetch_vacancies_maps_superjob_objects_to_generic_records(self):
|
||
with TestHTTPServer() as server:
|
||
server.add_json(
|
||
"/2.0/vacancies/",
|
||
{
|
||
"objects": [
|
||
{
|
||
"id": 456,
|
||
"id_client": 99,
|
||
"profession": "Технолог",
|
||
"firm_name": "ООО Пример",
|
||
"payment_from": 90000,
|
||
"payment_to": 130000,
|
||
"date_published": 1778064000,
|
||
"link": "https://www.superjob.ru/vakansii/456.html",
|
||
"is_archive": False,
|
||
}
|
||
],
|
||
"total": 1,
|
||
"more": False,
|
||
},
|
||
)
|
||
client = SuperJobVacanciesClient(
|
||
app_id="test-app-id",
|
||
base_url=server.base_url,
|
||
http_adapter=server.adapter,
|
||
)
|
||
|
||
records = client.fetch_vacancies(limit=10, offset=0, text="Технолог")
|
||
|
||
self.assertEqual(len(records), 1)
|
||
record = records[0]
|
||
self.assertEqual(record.source, ParserLoadLog.Source.TRUDVSEM)
|
||
self.assertEqual(record.external_id, "superjob:456")
|
||
self.assertEqual(record.title, "Технолог")
|
||
self.assertEqual(record.organisation_name, "ООО Пример")
|
||
self.assertEqual(record.amount, Decimal("90000"))
|
||
self.assertEqual(record.url, "https://www.superjob.ru/vakansii/456.html")
|
||
self.assertEqual(record.payload["vacancy_source"], "superjob")
|
||
|
||
|
||
class VacanciesClientTest(SimpleTestCase):
|
||
def test_fetch_vacancies_combines_enabled_sources(self):
|
||
class _Provider:
|
||
def __init__(self, source_name: str):
|
||
self.source_name = source_name
|
||
|
||
def fetch_vacancies(self, **kwargs):
|
||
return [
|
||
GenericParserItem(
|
||
source=ParserLoadLog.Source.TRUDVSEM,
|
||
external_id=f"{self.source_name}:1",
|
||
payload={"vacancy_source": self.source_name, "kwargs": kwargs},
|
||
)
|
||
]
|
||
|
||
client = VacanciesClient(
|
||
source_clients={
|
||
"trudvsem": _Provider("trudvsem"),
|
||
"hh": _Provider("hh"),
|
||
"superjob": _Provider("superjob"),
|
||
}
|
||
)
|
||
|
||
records = client.fetch_vacancies(limit=10, offset=5, text="инженер")
|
||
|
||
self.assertEqual(
|
||
[record.payload["vacancy_source"] for record in records],
|
||
["trudvsem", "hh", "superjob"],
|
||
)
|
||
|
||
def test_fetch_vacancies_skips_sources_without_company_inn_support(self):
|
||
class _Provider:
|
||
supports_company_inn = False
|
||
|
||
def fetch_vacancies(self, **kwargs):
|
||
return [
|
||
GenericParserItem(
|
||
source=ParserLoadLog.Source.TRUDVSEM,
|
||
external_id="unsupported:1",
|
||
)
|
||
]
|
||
|
||
class _InnProvider:
|
||
supports_company_inn = True
|
||
|
||
def fetch_vacancies(self, **kwargs):
|
||
return [
|
||
GenericParserItem(
|
||
source=ParserLoadLog.Source.TRUDVSEM,
|
||
external_id="trudvsem:1",
|
||
)
|
||
]
|
||
|
||
client = VacanciesClient(
|
||
source_clients={
|
||
"trudvsem": _InnProvider(),
|
||
"hh": _Provider(),
|
||
"superjob": _Provider(),
|
||
}
|
||
)
|
||
|
||
records = client.fetch_vacancies(company_inn="7700000000")
|
||
|
||
self.assertEqual([record.external_id for record in records], ["trudvsem:1"])
|
||
|
||
def test_fetch_vacancies_skips_unconfigured_optional_source_by_default(self):
|
||
class _Provider:
|
||
supports_company_inn = True
|
||
|
||
def fetch_vacancies(self, **kwargs):
|
||
return [
|
||
GenericParserItem(
|
||
source=ParserLoadLog.Source.TRUDVSEM,
|
||
external_id="trudvsem:1",
|
||
)
|
||
]
|
||
|
||
client = VacanciesClient(source_clients={"trudvsem": _Provider()})
|
||
|
||
records = client.fetch_vacancies()
|
||
|
||
self.assertEqual([record.external_id for record in records], ["trudvsem:1"])
|
||
|
||
def test_fetch_vacancies_requires_superjob_app_id_when_explicitly_selected(self):
|
||
client = VacanciesClient(sources=["superjob"], superjob_app_id="")
|
||
|
||
with self.assertRaises(VacanciesClientError):
|
||
client.fetch_vacancies()
|