feat(registries): add SME and budget imports and fix source API workflows
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 9m37s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 4m18s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m48s
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 9m37s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 4m18s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m48s
This commit is contained in:
97
tests/apps/parsers/test_snapshot_refresh_api.py
Normal file
97
tests/apps/parsers/test_snapshot_refresh_api.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Оба публичных входа запускают один импорт и исключают конкурирующий запуск."""
|
||||
|
||||
from importlib import import_module
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from apps.core.models import BackgroundJob
|
||||
from core.celery import app as celery_app
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from tests.apps.user.factories import UserFactory
|
||||
|
||||
SOURCES = [
|
||||
("budget-process-registry", "budget_ubpandnubp", "parse_budget_registry"),
|
||||
(
|
||||
"sme-support-recipients-registry",
|
||||
"fns_sme_support_recipients",
|
||||
"parse_sme_support_recipients",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_openapi_exposes_refresh_conflict_and_pollable_task():
|
||||
client = APIClient()
|
||||
client.force_authenticate(UserFactory.create_user(is_staff=True))
|
||||
response = client.get(reverse("schema-swagger-ui"), {"format": "openapi"})
|
||||
assert response.status_code == 200
|
||||
schema = response.data
|
||||
path = next(path for path in schema["paths"] if "/parsers/run/" in path)
|
||||
responses = schema["paths"][path]["post"]["responses"]
|
||||
assert "409" in responses
|
||||
assert responses["202"]["schema"]["$ref"].endswith("/ParserRunEnvelope")
|
||||
assert "task_id" in schema["definitions"]["ParserRunResponse"]["properties"]
|
||||
card_path = next(
|
||||
path for path in schema["paths"] if "/sources/{slug}/refresh/" in path
|
||||
)
|
||||
assert "409" in schema["paths"][card_path]["post"]["responses"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("slug,source,task", SOURCES)
|
||||
@pytest.mark.parametrize("first", ["card", "parser"])
|
||||
def test_refresh_is_pollable_and_other_entrypoint_conflicts(slug, source, task, first):
|
||||
client = APIClient()
|
||||
client.force_authenticate(UserFactory.create_user(is_staff=True))
|
||||
urls = {
|
||||
"card": f"/api/v1/sources/{slug}/refresh/",
|
||||
"parser": f"/api/v1/parsers/run/{source}/",
|
||||
}
|
||||
second = "parser" if first == "card" else "card"
|
||||
import_module("apps.parsers.tasks")
|
||||
registered_task = celery_app.tasks[f"parsers.{source}.refresh"]
|
||||
with patch.object(registered_task, "apply_async") as dispatch:
|
||||
response = client.post(urls[first], {"params": {}}, format="json")
|
||||
assert response.status_code == 202, response.data
|
||||
payload = response.data.get("data", response.data)
|
||||
job = BackgroundJob.objects.get(task_id=payload["task_id"])
|
||||
assert payload["status"] == "queued"
|
||||
assert dispatch.call_args.kwargs["task_id"] == job.task_id
|
||||
assert dispatch.call_args.kwargs["kwargs"] == {"requested_by_id": job.user_id}
|
||||
assert job.meta["refresh_task_ids"] == [job.task_id]
|
||||
assert client.get(f"/api/v1/jobs/{job.task_id}/").status_code == 200
|
||||
duplicate = client.post(urls[second], {}, format="json")
|
||||
assert duplicate.status_code == 409, duplicate.data
|
||||
assert BackgroundJob.objects.count() == 1
|
||||
assert dispatch.call_count == 1
|
||||
job.complete()
|
||||
next_run = client.post(urls[second], {}, format="json")
|
||||
assert next_run.status_code == 202
|
||||
assert BackgroundJob.objects.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("slug,source,task", SOURCES)
|
||||
def test_refresh_requires_administrator_and_rejects_loader_parameters(
|
||||
slug, source, task
|
||||
):
|
||||
client = APIClient()
|
||||
client.force_authenticate(UserFactory.create_user())
|
||||
import_module("apps.parsers.tasks")
|
||||
registered_task = celery_app.tasks[f"parsers.{source}.refresh"]
|
||||
with patch.object(registered_task, "apply_async") as dispatch:
|
||||
for url in [
|
||||
f"/api/v1/sources/{slug}/refresh/",
|
||||
f"/api/v1/parsers/run/{source}/",
|
||||
]:
|
||||
assert client.post(url, {}, format="json").status_code == 403
|
||||
client.force_authenticate(UserFactory.create_user(is_staff=True))
|
||||
response = client.post(
|
||||
f"/api/v1/sources/{slug}/refresh/",
|
||||
{"params": {"url": "https://example.invalid"}},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 400
|
||||
dispatch.assert_not_called()
|
||||
Reference in New Issue
Block a user