Some checks failed
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m39s
CI/CD Pipeline / Run Tests (pull_request) Successful in 3m0s
CI/CD Pipeline / Run API Inventory E2E Tests (pull_request) Successful in 35s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
225 lines
8.4 KiB
Python
225 lines
8.4 KiB
Python
"""Tests for exchange admin configuration."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
from apps.exchange.admin import ExchangeConnectionAdmin
|
|
from apps.exchange.forms import ExchangeConnectionAdminForm
|
|
from apps.exchange.models import ExchangeConnection
|
|
from apps.exchange.services import ExchangePeriodicTaskService
|
|
from django.contrib.admin.sites import AdminSite
|
|
from django.contrib.messages.storage.fallback import FallbackStorage
|
|
from django.test import RequestFactory, TestCase
|
|
from django_celery_beat.models import IntervalSchedule, PeriodicTask
|
|
|
|
from tests.apps.exchange.factories import ExchangeConnectionFactory
|
|
from tests.apps.user.factories import UserFactory
|
|
|
|
_DB_PASSWORD = "secret" # noqa: S105
|
|
|
|
|
|
class ExchangeAdminTest(TestCase):
|
|
def setUp(self):
|
|
self.site = AdminSite()
|
|
self.admin = ExchangeConnectionAdmin(ExchangeConnection, self.site)
|
|
self.factory = RequestFactory()
|
|
self.user = UserFactory.create_superuser()
|
|
|
|
def _request(self, path: str):
|
|
request = self.factory.get(path)
|
|
request.user = self.user
|
|
request.session = {}
|
|
request._messages = FallbackStorage(request)
|
|
return request
|
|
|
|
def _post_request(self, path: str, data: dict):
|
|
request = self.factory.post(path, data=data)
|
|
request.user = self.user
|
|
request.session = {}
|
|
request._messages = FallbackStorage(request)
|
|
return request
|
|
|
|
def test_exchange_admin_has_custom_routes(self):
|
|
route_names = [route.name for route in self.admin.get_urls()]
|
|
|
|
self.assertIn("exchange_exchangeconnection_test_connection", route_names)
|
|
self.assertIn("exchange_exchangeconnection_copy_data", route_names)
|
|
self.assertIn("exchange_exchangeconnection_periodic_tasks", route_names)
|
|
self.assertIn("exchange_exchangeconnection_periodic_task_change", route_names)
|
|
|
|
def test_exchange_admin_changelist_renders_custom_buttons(self):
|
|
response = self.admin.changelist_view(
|
|
self._request("/admin/exchange/exchangeconnection/")
|
|
)
|
|
response.render()
|
|
content = response.content.decode("utf-8")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("Проверить новое подключение", content)
|
|
self.assertIn("Запустить обмен", content)
|
|
self.assertIn("Настроить периодический обмен", content)
|
|
self.assertIn("Добавить подключение обмена", content)
|
|
|
|
def test_exchange_connection_admin_form_keeps_existing_password_when_blank(self):
|
|
connection = ExchangeConnectionFactory(password=_DB_PASSWORD)
|
|
form = ExchangeConnectionAdminForm(
|
|
data={
|
|
"server": connection.server,
|
|
"port": connection.port,
|
|
"username": connection.username,
|
|
"password": "",
|
|
"database_name": connection.database_name,
|
|
"schema_name": connection.schema_name,
|
|
},
|
|
instance=connection,
|
|
)
|
|
|
|
self.assertTrue(form.is_valid(), form.errors)
|
|
saved_connection = form.save()
|
|
|
|
self.assertEqual(saved_connection.get_decrypted_password(), _DB_PASSWORD)
|
|
|
|
@patch("apps.exchange.admin.ExchangeConnectionService.validate_saved_connection")
|
|
def test_validate_selected_connections_calls_service(self, validate_mock):
|
|
connection = ExchangeConnectionFactory()
|
|
|
|
self.admin.validate_selected_connections(
|
|
self._request("/admin/exchange/exchangeconnection/"),
|
|
ExchangeConnection.objects.filter(id=connection.id),
|
|
)
|
|
|
|
validate_mock.assert_called_once_with(connection)
|
|
|
|
@patch("apps.exchange.admin.ExchangeConnectionService.activate_connection")
|
|
def test_activate_selected_connection_calls_service(self, activate_mock):
|
|
connection = ExchangeConnectionFactory()
|
|
|
|
self.admin.activate_selected_connection(
|
|
self._request("/admin/exchange/exchangeconnection/"),
|
|
ExchangeConnection.objects.filter(id=connection.id),
|
|
)
|
|
|
|
activate_mock.assert_called_once_with(connection)
|
|
|
|
@patch("apps.exchange.admin.ExchangeConnectionService.test_connection_payload")
|
|
def test_test_connection_view_success_redirects_after_validation(
|
|
self,
|
|
test_connection_mock,
|
|
):
|
|
test_connection_mock.return_value = {
|
|
"status": "success",
|
|
"message": "Подключение проверено.",
|
|
}
|
|
request = self._post_request(
|
|
"/admin/exchange/exchangeconnection/test-connection/",
|
|
{
|
|
"server": "127.0.0.1",
|
|
"port": 5432,
|
|
"username": "postgres",
|
|
"password": _DB_PASSWORD,
|
|
"database_name": "target_db",
|
|
"schema_name": "public",
|
|
},
|
|
)
|
|
|
|
response = self.admin.test_connection_view(request)
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
test_connection_mock.assert_called_once_with(
|
|
server="127.0.0.1",
|
|
port=5432,
|
|
username="postgres",
|
|
password=_DB_PASSWORD,
|
|
database_name="target_db",
|
|
schema_name="public",
|
|
)
|
|
|
|
@patch("apps.exchange.admin.BackgroundJobService.create_job")
|
|
@patch("apps.exchange.admin.copy_parsers_data_async")
|
|
def test_copy_data_view_enqueues_background_job(self, task_mock, create_job_mock):
|
|
active_connection = ExchangeConnectionFactory(is_active=True)
|
|
task_mock.delay.return_value = SimpleNamespace(id="task-123")
|
|
request = self._post_request(
|
|
"/admin/exchange/exchangeconnection/copy-data/",
|
|
{
|
|
"mode": "all",
|
|
"truncate_before_copy": "on",
|
|
},
|
|
)
|
|
|
|
response = self.admin.copy_data_view(request)
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
task_mock.delay.assert_called_once_with(
|
|
connection_id=active_connection.id,
|
|
payload={
|
|
"mode": "all",
|
|
"table": None,
|
|
"tables": None,
|
|
"truncate_before_copy": True,
|
|
},
|
|
requested_by_id=self.user.id,
|
|
)
|
|
create_job_mock.assert_called_once()
|
|
|
|
def test_periodic_tasks_view_lists_existing_tasks(self):
|
|
interval = IntervalSchedule.objects.create(every=2, period="hours")
|
|
PeriodicTask.objects.create(
|
|
name="exchange-copy-hourly",
|
|
task=ExchangePeriodicTaskService.TASK_NAME,
|
|
interval=interval,
|
|
kwargs='{"payload": {"mode": "all", "truncate_before_copy": true}}',
|
|
)
|
|
|
|
response = self.admin.periodic_tasks_view(
|
|
self._request("/admin/exchange/exchangeconnection/periodic-tasks/")
|
|
)
|
|
response.render()
|
|
content = response.content.decode("utf-8")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("exchange-copy-hourly", content)
|
|
self.assertIn("Каждые 2 hours", content)
|
|
|
|
@patch("apps.exchange.admin.ExchangePeriodicTaskService.create_periodic_task")
|
|
def test_periodic_tasks_view_creates_task(self, create_task_mock):
|
|
request = self._post_request(
|
|
"/admin/exchange/exchangeconnection/periodic-tasks/",
|
|
{
|
|
"name": "exchange-copy-nightly",
|
|
"description": "Nightly sync",
|
|
"enabled": "on",
|
|
"mode": "selected",
|
|
"tables": ["parsers_manufacturer"],
|
|
"truncate_before_copy": "on",
|
|
"notify_on_error": "on",
|
|
"schedule_type": "daily",
|
|
"crontab_minute": 15,
|
|
"crontab_hour": 3,
|
|
},
|
|
)
|
|
|
|
response = self.admin.periodic_tasks_view(request)
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
create_task_mock.assert_called_once_with(
|
|
name="exchange-copy-nightly",
|
|
description="Nightly sync",
|
|
enabled=True,
|
|
payload={
|
|
"mode": "selected",
|
|
"table": None,
|
|
"tables": ["parsers_manufacturer"],
|
|
"truncate_before_copy": True,
|
|
"notify_on_error": True,
|
|
},
|
|
schedule={
|
|
"type": "crontab",
|
|
"minute": "15",
|
|
"hour": "3",
|
|
"day_of_week": "*",
|
|
"day_of_month": "*",
|
|
"month_of_year": "*",
|
|
},
|
|
)
|