from __future__ import annotations from unittest.mock import MagicMock, patch from apps.exchange.services import ExchangeServiceError from apps.exchange.tasks import copy_parsers_data_async, dispatch_periodic_exchange_copy from django.test import SimpleTestCase class ExchangeTasksTest(SimpleTestCase): def test_dispatch_periodic_exchange_copy_enqueues_copy_with_active_connection(self): active_connection = MagicMock(id=15) with patch( "apps.exchange.tasks.ExchangeConnectionService.get_active_connection", return_value=active_connection, ) as get_connection_mock, patch( "apps.exchange.tasks.copy_parsers_data_async.delay", return_value=MagicMock(id="task-15"), ) as delay_mock: result = dispatch_periodic_exchange_copy.run( payload={"mode": "all", "truncate_before_copy": True} ) self.assertEqual( result, { "status": "queued", "task_id": "task-15", "connection_id": 15, }, ) get_connection_mock.assert_called_once_with() delay_mock.assert_called_once_with( connection_id=15, payload={"mode": "all", "truncate_before_copy": True}, requested_by_id=None, ) def test_dispatch_periodic_exchange_copy_fails_without_active_connection(self): with patch( "apps.exchange.tasks.ExchangeConnectionService.get_active_connection", side_effect=ExchangeServiceError("Активное подключение не найдено"), ), self.assertRaisesMessage( ExchangeServiceError, "Активное подключение не найдено", ): dispatch_periodic_exchange_copy.run(payload={"mode": "all"}) def test_copy_parsers_data_async_completes_with_existing_job(self): background_job = MagicMock() connection = MagicMock() copy_parsers_data_async.push_request(id="task-1") try: with patch( "apps.exchange.tasks.BackgroundJobService.get_by_task_id_or_none", return_value=background_job, ) as get_job_mock, patch( "apps.exchange.tasks.ExchangeConnection.objects.filter", ) as filter_mock, patch( "apps.exchange.tasks.ExchangeConnectionService.copy_parsers_data", return_value={ "mode": "all", "tables": ["fake_table"], "rows_by_table": {"fake_table": 3}, "total_rows": 3, "truncate_before_copy": True, }, ) as copy_mock: filter_mock.return_value.first.return_value = connection result = copy_parsers_data_async.run( connection_id=11, payload={"mode": "all", "truncate_before_copy": True}, requested_by_id=7, ) finally: copy_parsers_data_async.pop_request() self.assertEqual(result["status"], "success") self.assertEqual(result["connection_id"], 11) get_job_mock.assert_called_once_with("task-1") background_job.mark_started.assert_called_once_with() background_job.update_progress.assert_any_call( 10, "Проверка структуры целевой БД", ) background_job.update_progress.assert_any_call(90, "Фиксация результата") background_job.complete.assert_called_once_with(result=result) copy_mock.assert_called_once_with( connection=connection, mode="all", truncate_before_copy=True, ) def test_copy_parsers_data_async_creates_job_and_fails_when_connection_missing( self, ): background_job = MagicMock() copy_parsers_data_async.push_request(id=None) try: with patch( "apps.exchange.tasks.uuid.uuid4", return_value="generated-task-id" ), patch( "apps.exchange.tasks.BackgroundJobService.get_by_task_id_or_none", return_value=None, ), patch( "apps.exchange.tasks.BackgroundJobService.create_job", return_value=background_job, ) as create_job_mock, patch( "apps.exchange.tasks.ExchangeConnection.objects.filter", ) as filter_mock, self.assertRaisesMessage( ValueError, "Active exchange connection not found: 42", ): filter_mock.return_value.first.return_value = None copy_parsers_data_async.run( connection_id=42, payload={"mode": "all"}, requested_by_id=3, ) finally: copy_parsers_data_async.pop_request() create_job_mock.assert_called_once_with( task_id="generated-task-id", task_name="apps.exchange.tasks.copy_parsers_data_async", user_id=3, meta={"connection_id": 42, "mode": "all"}, ) background_job.fail.assert_called_once_with( error="Активное подключение не найдено" ) def test_copy_parsers_data_async_marks_failure_and_reraises(self): background_job = MagicMock() connection = MagicMock() copy_parsers_data_async.push_request(id="task-2") try: with patch( "apps.exchange.tasks.BackgroundJobService.get_by_task_id_or_none", return_value=background_job, ), patch( "apps.exchange.tasks.ExchangeConnection.objects.filter", ) as filter_mock, patch( "apps.exchange.tasks.ExchangeConnectionService.copy_parsers_data", side_effect=RuntimeError("copy failed"), ), patch( "apps.exchange.tasks.logger.exception" ) as logger_mock, self.assertRaisesMessage(RuntimeError, "copy failed"): filter_mock.return_value.first.return_value = connection copy_parsers_data_async.run( connection_id=9, payload={ "mode": "selected", "tables": ["fake_table"], }, ) finally: copy_parsers_data_async.pop_request() background_job.fail.assert_called_once_with(error="copy failed") logger_mock.assert_called_once()