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
148 lines
5.4 KiB
Python
148 lines
5.4 KiB
Python
"""Общий прогресс ручного запуска, в том числе после завершения его частей."""
|
||
|
||
from datetime import timedelta
|
||
from unittest.mock import MagicMock
|
||
|
||
import pytest
|
||
from apps.core.models import BackgroundJob, JobStatus
|
||
from apps.parsers.source_cards import SourceCardService
|
||
from django.test import override_settings
|
||
from django.utils import timezone
|
||
|
||
|
||
@pytest.mark.django_db
|
||
def test_all_jobs_are_visible_before_dispatch_and_completed_parts_stay_in_progress():
|
||
definition = SourceCardService.get_definition("manufacturers-and-products")
|
||
observed_counts = []
|
||
tasks = [MagicMock(), MagicMock(), MagicMock()]
|
||
|
||
def dispatch(**kwargs):
|
||
jobs = list(BackgroundJob.objects.all())
|
||
observed_counts.append(len(jobs))
|
||
assert {job.task_id for job in jobs} == set(jobs[0].meta["refresh_task_ids"])
|
||
|
||
for task in tasks:
|
||
task.apply_async.side_effect = dispatch
|
||
specs = tuple(
|
||
(task, name, source)
|
||
for task, name, source in zip(
|
||
tasks,
|
||
[
|
||
"apps.parsers.tasks.parse_industrial_production",
|
||
"apps.parsers.tasks.parse_industrial_products",
|
||
"apps.parsers.tasks.parse_manufactures",
|
||
],
|
||
["industrial", "industrial_products", "manufactures"],
|
||
strict=False,
|
||
)
|
||
)
|
||
result = SourceCardService._enqueue_refresh_group(
|
||
definition, specs, requested_by_id=1, kwargs={}
|
||
)
|
||
assert observed_counts == [3, 3, 3]
|
||
jobs = [BackgroundJob.objects.get(task_id=item["task_id"]) for item in result]
|
||
jobs[0].complete()
|
||
jobs[1].update_progress(50)
|
||
jobs[2].update_progress(0)
|
||
card = SourceCardService.get_card(definition.slug)
|
||
assert card["progress"] == 50
|
||
assert len(card["active_tasks"]) == 2
|
||
jobs[1].complete()
|
||
assert SourceCardService.get_card(definition.slug)["progress"] == 67
|
||
jobs[2].complete()
|
||
for card in [
|
||
SourceCardService.get_card(definition.slug),
|
||
next(
|
||
card
|
||
for card in SourceCardService.list_cards()
|
||
if card["slug"] == definition.slug
|
||
),
|
||
]:
|
||
assert (card["progress"], card["status"], card["active_tasks"]) == (
|
||
100,
|
||
"success",
|
||
[],
|
||
)
|
||
|
||
|
||
@pytest.mark.django_db
|
||
def test_failed_run_keeps_progress_and_error_after_last_active_job_disappears():
|
||
job = BackgroundJob.objects.create(
|
||
task_id="failed-refresh",
|
||
task_name="apps.parsers.tasks.parse_arbitration_cases",
|
||
meta={
|
||
"source_card": "arbitration-cases",
|
||
"refresh_task_ids": ["failed-refresh"],
|
||
},
|
||
)
|
||
job.update_progress(60)
|
||
job.fail("Ошибка загрузки")
|
||
card = SourceCardService.get_card("arbitration-cases")
|
||
assert (card["status"], card["progress"], card["error_message"]) == (
|
||
"error",
|
||
60,
|
||
"Ошибка загрузки",
|
||
)
|
||
|
||
|
||
@pytest.mark.django_db
|
||
def test_broker_failure_keeps_all_registered_parts_in_final_failed_state():
|
||
definition = SourceCardService.get_definition("defense-unreliable-suppliers")
|
||
task = MagicMock()
|
||
task.apply_async.side_effect = RuntimeError("broker unavailable")
|
||
with pytest.raises(RuntimeError):
|
||
SourceCardService._enqueue_refresh_group(
|
||
definition,
|
||
[
|
||
(task, "test.first", "unfair_suppliers"),
|
||
(task, "test.second", "fas_goz"),
|
||
],
|
||
requested_by_id=1,
|
||
kwargs={},
|
||
)
|
||
jobs = list(BackgroundJob.objects.all())
|
||
assert len(jobs) == 2
|
||
assert all(job.status == JobStatus.FAILURE and job.error for job in jobs)
|
||
assert task.apply_async.call_count == 1
|
||
|
||
|
||
@pytest.mark.django_db
|
||
def test_completed_new_run_does_not_hide_active_older_run():
|
||
definition = SourceCardService.get_definition("manufacturers-and-products")
|
||
specs = [(MagicMock(), name, "industrial") for name in definition.task_names]
|
||
first = SourceCardService._enqueue_refresh_group(
|
||
definition, specs, requested_by_id=None, kwargs={}
|
||
)
|
||
second = SourceCardService._enqueue_refresh_group(
|
||
definition, specs, requested_by_id=None, kwargs={}
|
||
)
|
||
for item in second:
|
||
BackgroundJob.objects.get(task_id=item["task_id"]).complete()
|
||
card = SourceCardService.get_card(definition.slug)
|
||
assert {job["task_id"] for job in card["active_tasks"]} == {
|
||
job["task_id"] for job in first
|
||
}
|
||
assert (card["status"], card["progress"]) == ("in_progress", 50)
|
||
for item in first:
|
||
BackgroundJob.objects.get(task_id=item["task_id"]).complete()
|
||
assert SourceCardService.get_card(definition.slug)["progress"] == 100
|
||
|
||
|
||
@pytest.mark.django_db
|
||
@pytest.mark.parametrize(
|
||
"status,time_field",
|
||
[(JobStatus.PENDING, "created_at"), (JobStatus.STARTED, "updated_at")],
|
||
)
|
||
@override_settings(
|
||
PARSER_STALE_LOAD_MAX_AGE_MINUTES=10, PARSER_STALE_PENDING_JOB_MAX_AGE_MINUTES=10
|
||
)
|
||
def test_latest_task_does_not_reactivate_a_stale_job(status, time_field):
|
||
definition = SourceCardService.get_definition("arbitration-cases")
|
||
job = BackgroundJob.objects.create(
|
||
task_id="stale-task", task_name=definition.task_names[0], status=status
|
||
)
|
||
BackgroundJob.objects.filter(pk=job.pk).update(
|
||
**{time_field: timezone.now() - timedelta(minutes=15)}
|
||
)
|
||
assert SourceCardService.get_card(definition.slug)["active_tasks"] == []
|