diff --git a/openwisp_firmware_upgrader/tests/test_admin.py b/openwisp_firmware_upgrader/tests/test_admin.py index bd76756e3..344218a01 100644 --- a/openwisp_firmware_upgrader/tests/test_admin.py +++ b/openwisp_firmware_upgrader/tests/test_admin.py @@ -123,6 +123,9 @@ def build_list_url(self): @override_settings(LANGUAGE_CODE="en") class TestAdmin(BaseTestAdmin, TestCase): + _mock_upgrade = "openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt.upgrade" + _mock_connect = "openwisp_controller.connection.models.DeviceConnection.connect" + def test_build_list(self): self._login() build = self._create_build() @@ -380,7 +383,7 @@ def test_save_device_with_deleted_devicefirmware(self): "openwisp_firmware_upgrader.utils.get_upgrader_class_from_device_connection" ) def test_device_firmware_upgrade_without_device_connection( - self, captured_stderr, mocked_func, *args + self, captured_stderr, mocked_func ): self._login() device_fw = self._create_device_firmware() @@ -396,7 +399,7 @@ def test_device_firmware_upgrade_without_device_connection( mocked_func.assert_not_called() self.assertEqual(response.status_code, 200) - def test_save_device_after_credentials_deleted(self, *args): + def test_save_device_after_credentials_deleted(self): """Regression test for #250.""" self._login() device_fw = self._create_device_firmware(installed=True) @@ -420,7 +423,7 @@ def test_save_device_after_credentials_deleted(self, *args): self.assertEqual(response.status_code, 200) self.assertNotContains(response, "Please correct the error") - def test_change_image_and_add_credentials_together(self, *args): + def test_change_image_and_add_credentials_together(self): """Regression test for #250.""" self._login() device_fw = self._create_device_firmware() @@ -461,14 +464,10 @@ def test_change_image_and_add_credentials_together(self, *args): self.assertEqual(device_fw.image, new_image) self.assertEqual(device.deviceconnection_set.count(), 1) - def test_add_credentials_with_cancelled_upgrade_operation(self, *args): + def test_add_credentials_with_cancelled_upgrade_operation(self): """Regression test for adding credentials while a cancelled upgrade is shown.""" - with mock.patch( - "openwisp_controller.connection.models.DeviceConnection.connect", - return_value=True, - ), mock.patch( - "openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt.upgrade", - return_value=True, + with mock.patch(self._mock_connect, return_value=True), mock.patch( + self._mock_upgrade, return_value=True ): self._login() device = self._create_config(organization=self._get_org()).device @@ -569,7 +568,7 @@ def test_deactivated_device_upgrade_operation_readonly(self): # deactivated devices are readonly self.assertNotContains(response, 'name="upgradeoperation_set-0-DELETE"') - def test_device_upgrade_shared_firmware(self, *args): + def test_device_upgrade_shared_firmware(self): org = self._get_org() administrator = self._create_administrator(organizations=[org]) shared_image = self._create_firmware_image(organization=None) @@ -746,7 +745,7 @@ def _get_org_option(org): self.assertEqual(response.status_code, 200) self.assertNotContains(response, "By organization") - def test_batch_upgrade_operation_filters(self, *args): + def test_batch_upgrade_operation_filters(self): """Test that filter UI elements are displayed correctly for organization admin""" env = self._create_upgrade_env() org_admin = self._create_administrator(organizations=[env["d1"].organization]) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..bcbdaf5a3 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,9 @@ +[pytest] +addopts = -p no:warnings --create-db --reuse-db --nomigrations +DJANGO_SETTINGS_MODULE = openwisp2.settings +python_files = pytest*.py +python_classes = *Test* +pythonpath = tests +log_cli = true +asyncio_mode = strict +asyncio_debug = true diff --git a/runtests.py b/runtests.py index 8fe1822f8..1df6b17c1 100755 --- a/runtests.py +++ b/runtests.py @@ -4,6 +4,7 @@ import os import sys +import pytest from django.core.management import execute_from_command_line @@ -28,11 +29,21 @@ def run_tests(extra_args, settings_module, test_app): # Configure Django settings for test execution # (sets Celery to eager mode, configures in-memory channels layer, etc.) os.environ.setdefault("TESTING", "1") - args = sys.argv[1:] + args = sys.argv.copy()[1:] + exclude_pytest = "--exclude-pytest" in args + if exclude_pytest: + args.pop(args.index("--exclude-pytest")) # normal tests vs SAMPLE_APP if not os.environ.get("SAMPLE_APP", False): test_app = "openwisp_firmware_upgrader" + app_dir = "openwisp_firmware_upgrader/" else: test_app = "openwisp2" + app_dir = "tests/openwisp2/" # Run Django tests - run_tests(args, "openwisp2.settings", test_app) + django_tests = run_tests(args, "openwisp2.settings", test_app) + # Run pytest tests (only when testing extensions and if not explicltly excluded) + if os.environ.get("SAMPLE_APP", False) and not exclude_pytest: + # Used to test django-channels + sys.exit(pytest.main([app_dir])) + sys.exit(django_tests) diff --git a/tests/media/.gitignore b/tests/media/.gitignore new file mode 100644 index 000000000..ae48598e8 --- /dev/null +++ b/tests/media/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!floorplan.jpg diff --git a/tests/media/floorplan.jpg b/tests/media/floorplan.jpg new file mode 100644 index 000000000..e69de29bb diff --git a/tests/openwisp2/sample_connection/__init__.py b/tests/openwisp2/sample_connection/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/openwisp2/sample_connection/admin.py b/tests/openwisp2/sample_connection/admin.py new file mode 100644 index 000000000..5e9199435 --- /dev/null +++ b/tests/openwisp2/sample_connection/admin.py @@ -0,0 +1 @@ +from openwisp_controller.connection import admin # noqa diff --git a/tests/openwisp2/sample_connection/api/__init__.py b/tests/openwisp2/sample_connection/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/openwisp2/sample_connection/api/views.py b/tests/openwisp2/sample_connection/api/views.py new file mode 100644 index 000000000..fd2207cbc --- /dev/null +++ b/tests/openwisp2/sample_connection/api/views.py @@ -0,0 +1,50 @@ +from openwisp_controller.connection.api.views import ( + CommandDetailsView as BaseCommandDetailsView, +) +from openwisp_controller.connection.api.views import ( + CommandListCreateView as BaseCommandListCreateView, +) +from openwisp_controller.connection.api.views import ( + CredentialDetailView as BaseCredentialDetailView, +) +from openwisp_controller.connection.api.views import ( + CredentialListCreateView as BaseCredentialListCreateView, +) +from openwisp_controller.connection.api.views import ( + DeviceConnectionDetailView as BaseDeviceConnectionDetailView, +) +from openwisp_controller.connection.api.views import ( + DeviceConnectionListCreateView as BaseDeviceConnectionListCreateView, +) + + +class CommandDetailsView(BaseCommandDetailsView): + pass + + +class CommandListCreateView(BaseCommandListCreateView): + pass + + +class CredentialListCreateView(BaseCredentialListCreateView): + pass + + +class CredentialDetailView(BaseCredentialDetailView): + pass + + +class DeviceConnectionListCreateView(BaseDeviceConnectionListCreateView): + pass + + +class DeviceConnectionDetailView(BaseDeviceConnectionDetailView): + pass + + +command_list_create_view = CommandListCreateView.as_view() +command_details_view = CommandDetailsView.as_view() +credential_list_create_view = CredentialListCreateView.as_view() +credential_detail_view = CredentialDetailView.as_view() +deviceconnection_list_create_view = DeviceConnectionListCreateView.as_view() +deviceconnection_detail_view = DeviceConnectionDetailView.as_view() diff --git a/tests/openwisp2/sample_connection/apps.py b/tests/openwisp2/sample_connection/apps.py new file mode 100644 index 000000000..1cd087c9c --- /dev/null +++ b/tests/openwisp2/sample_connection/apps.py @@ -0,0 +1,9 @@ +from openwisp_controller.connection.apps import ConnectionConfig + + +class SampleConnectionConfig(ConnectionConfig): + name = "openwisp2.sample_connection" + label = "sample_connection" + + +del ConnectionConfig diff --git a/tests/openwisp2/sample_connection/migrations/0001_initial.py b/tests/openwisp2/sample_connection/migrations/0001_initial.py new file mode 100644 index 000000000..e2c1d216b --- /dev/null +++ b/tests/openwisp2/sample_connection/migrations/0001_initial.py @@ -0,0 +1,286 @@ +# Generated by Django 3.0.6 on 2020-05-10 18:11 + +import uuid + +import django +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields +import swapper +from django.conf import settings +from django.db import migrations, models + +import openwisp_controller.connection.base.models +import openwisp_users.mixins +from openwisp_controller.connection import settings as connection_settings +from openwisp_controller.connection.commands import COMMAND_CHOICES, get_command_choices + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + swapper.dependency( + *swapper.split(settings.AUTH_USER_MODEL), version="0004_default_groups" + ), + swapper.dependency( + *swapper.split(settings.CONFIG_DEVICE_MODEL), + version="0004_add_device_model", + ), + ] + + operations = [ + migrations.CreateModel( + name="Credentials", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ("name", models.CharField(db_index=True, max_length=64, unique=True)), + ( + "connector", + models.CharField( + choices=connection_settings.CONNECTORS, + db_index=True, + max_length=128, + verbose_name="connection type", + ), + ), + ( + "params", + models.TextField( + default=dict, + help_text="global connection parameters", + verbose_name="parameters", + ), + ), + ( + "auto_add", + models.BooleanField( + default=False, + help_text=( + "automatically add these credentials to the " + "devices of this organization; if no organization is " + "specified will be added to all the new devices" + ), + verbose_name="auto add", + ), + ), + ("details", models.CharField(blank=True, max_length=64, null=True)), + ( + "organization", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=swapper.get_model_name("openwisp_users", "Organization"), + verbose_name="organization", + ), + ), + ], + options={ + "verbose_name": "Access credentials", + "verbose_name_plural": "Access credentials", + "abstract": False, + }, + bases=( + openwisp_controller.connection.base.models.ConnectorMixin, + openwisp_users.mixins.ValidateOrgMixin, + models.Model, + ), + ), + migrations.CreateModel( + name="DeviceConnection", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ( + "update_strategy", + models.CharField( + blank=True, + choices=connection_settings.UPDATE_STRATEGIES, + db_index=True, + help_text="leave blank to determine automatically", + max_length=128, + verbose_name="update strategy", + ), + ), + ("enabled", models.BooleanField(db_index=True, default=True)), + ( + "params", + models.TextField( + blank=True, + default=dict, + help_text=( + "local connection parameters (will override the " + "global parameters if specified)" + ), + verbose_name="parameters", + ), + ), + ( + "is_working", + models.BooleanField(blank=True, default=None, null=True), + ), + ( + "failure_reason", + models.TextField(blank=True, verbose_name="reason of failure"), + ), + ("last_attempt", models.DateTimeField(blank=True, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), + ( + "credentials", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="sample_connection.Credentials", + ), + ), + ( + "device", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=swapper.get_model_name("config", "Device"), + ), + ), + ], + options={ + "verbose_name": "Device connection", + "verbose_name_plural": "Device connections", + "unique_together": {("device", "credentials")}, + "abstract": False, + }, + bases=( + openwisp_controller.connection.base.models.ConnectorMixin, + models.Model, + ), + ), + migrations.CreateModel( + name="Command", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("in-progress", "in progress"), + ("success", "success"), + ("failed", "failed"), + ], + default="in-progress", + max_length=12, + ), + ), + ( + "type", + models.CharField( + choices=( + COMMAND_CHOICES + if django.VERSION < (5, 0) + else get_command_choices + ), + max_length=16, + ), + ), + ( + "input", + models.TextField( + blank=True, + null=True, + ), + ), + ("output", models.TextField(blank=True)), + ( + "connection", + models.ForeignKey( + null=True, + blank=True, + on_delete=django.db.models.deletion.SET_NULL, + to=swapper.get_model_name("connection", "DeviceConnection"), + ), + ), + ( + "device", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=swapper.get_model_name("config", "Device"), + ), + ), + ], + options={ + "verbose_name": "Command", + "verbose_name_plural": "Commands", + "ordering": ("created",), + "abstract": False, + "swappable": swapper.swappable_setting("connection", "Command"), + }, + ), + ] diff --git a/tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py b/tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py new file mode 100644 index 000000000..469610b20 --- /dev/null +++ b/tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py @@ -0,0 +1,19 @@ +from django.db import migrations + +from openwisp_controller.connection.migrations import ( + assign_command_permissions_to_groups, + assign_permissions_to_groups, +) + + +class Migration(migrations.Migration): + dependencies = [("sample_connection", "0001_initial")] + + operations = [ + migrations.RunPython( + assign_permissions_to_groups, reverse_code=migrations.RunPython.noop + ), + migrations.RunPython( + assign_command_permissions_to_groups, reverse_code=migrations.RunPython.noop + ), + ] diff --git a/tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py b/tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py new file mode 100644 index 000000000..ff3b8dbed --- /dev/null +++ b/tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py @@ -0,0 +1,20 @@ +# Generated by Django 3.1.6 on 2021-02-11 22:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("sample_connection", "0002_default_group_permissions"), + ] + + operations = [ + migrations.AlterField( + model_name="credentials", + name="name", + field=models.CharField(db_index=True, max_length=64), + ), + migrations.AlterUniqueTogether( + name="credentials", unique_together={("name", "organization")} + ), + ] diff --git a/tests/openwisp2/sample_connection/migrations/0004_replace_jsonfield_with_django_builtin.py b/tests/openwisp2/sample_connection/migrations/0004_replace_jsonfield_with_django_builtin.py new file mode 100644 index 000000000..f74802cae --- /dev/null +++ b/tests/openwisp2/sample_connection/migrations/0004_replace_jsonfield_with_django_builtin.py @@ -0,0 +1,47 @@ +# Generated by Django 5.2.11 on 2026-02-06 06:11 + +import django.core.serializers.json +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("sample_connection", "0003_name_unique_per_organization"), + ] + + operations = [ + migrations.AlterField( + model_name="command", + name="input", + field=models.JSONField( + blank=True, + encoder=django.core.serializers.json.DjangoJSONEncoder, + null=True, + ), + ), + migrations.AlterField( + model_name="credentials", + name="params", + field=models.JSONField( + default=dict, + encoder=django.core.serializers.json.DjangoJSONEncoder, + help_text="global connection parameters", + verbose_name="parameters", + ), + ), + migrations.AlterField( + model_name="deviceconnection", + name="params", + field=models.JSONField( + blank=True, + default=dict, + encoder=django.core.serializers.json.DjangoJSONEncoder, + help_text=( + "local connection parameters (will override the global " + "parameters if specified)" + ), + verbose_name="parameters", + ), + ), + ] diff --git a/tests/openwisp2/sample_connection/migrations/__init__.py b/tests/openwisp2/sample_connection/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/openwisp2/sample_connection/models.py b/tests/openwisp2/sample_connection/models.py new file mode 100644 index 000000000..7964c5471 --- /dev/null +++ b/tests/openwisp2/sample_connection/models.py @@ -0,0 +1,29 @@ +from django.db import models + +from openwisp_controller.connection.base.models import ( + AbstractCommand, + AbstractCredentials, + AbstractDeviceConnection, +) + + +class DetailsModel(models.Model): + details = models.CharField(max_length=64, blank=True, null=True) + + class Meta: + abstract = True + + +class Credentials(DetailsModel, AbstractCredentials): + class Meta(AbstractCredentials.Meta): + abstract = False + + +class DeviceConnection(DetailsModel, AbstractDeviceConnection): + class Meta(AbstractDeviceConnection.Meta): + abstract = False + + +class Command(AbstractCommand): + class Meta(AbstractCommand.Meta): + abstract = False diff --git a/tests/openwisp2/sample_connection/pytest.py b/tests/openwisp2/sample_connection/pytest.py new file mode 100644 index 000000000..7dfda7ca0 --- /dev/null +++ b/tests/openwisp2/sample_connection/pytest.py @@ -0,0 +1,14 @@ +from swapper import load_model + +from openwisp_controller.connection.tests.pytest import ( + TestCommandsConsumer as BaseTestCommandsConsumer, +) + +Command = load_model("connection", "Command") + + +class TestCommandsConsumer(BaseTestCommandsConsumer): + app_label = Command._meta.app_label + + +del BaseTestCommandsConsumer diff --git a/tests/openwisp2/sample_connection/tests.py b/tests/openwisp2/sample_connection/tests.py new file mode 100644 index 000000000..7a5186c36 --- /dev/null +++ b/tests/openwisp2/sample_connection/tests.py @@ -0,0 +1,88 @@ +from django.urls import reverse + +from openwisp_controller.connection import settings as conn_settings +from openwisp_controller.connection.tests.test_admin import ( + TestCommandInlines as BaseTestCommandInlines, +) +from openwisp_controller.connection.tests.test_admin import ( + TestConnectionAdmin as BaseTestConnectionAdmin, +) +from openwisp_controller.connection.tests.test_api import ( + TestConnectionApi as BaseTestConnectionApi, +) +from openwisp_controller.connection.tests.test_models import ( + TestModels as BaseTestModels, +) +from openwisp_controller.connection.tests.test_models import ( + TestModelsTransaction as BaseTestModelsTransaction, +) +from openwisp_controller.connection.tests.test_notifications import ( + TestNotifications as BaseTestNotifications, +) +from openwisp_controller.connection.tests.test_notifications import ( + TestNotificationTransaction as BaseTestNotificationTransaction, +) +from openwisp_controller.connection.tests.test_ssh import TestSsh as BaseTestSsh +from openwisp_controller.connection.tests.test_tasks import TestTasks as BaseTestTasks + + +class TestConnectionAdmin(BaseTestConnectionAdmin): + config_app_label = "config" + app_label = "sample_connection" + + +class TestCommandInlines(BaseTestCommandInlines): + config_app_label = "config" + + +class TestModels(BaseTestModels): + app_label = "sample_connection" + + +class TestModelsTransaction(BaseTestModelsTransaction): + app_label = "sample_connection" + + +class TestTasks(BaseTestTasks): + pass + + +class TestSsh(BaseTestSsh): + pass + + +class TestNotifications(BaseTestNotifications): + app_label = "sample_connection" + config_app_label = "config" + + +class TestNotificationTransaction(BaseTestNotificationTransaction): + app_label = "sample_connection" + config_app_label = "config" + + +class TestConnectionApi(BaseTestConnectionApi): + def test_post_deviceconnection_list(self): + d1 = self._create_device() + self._create_config(device=d1) + path = reverse("connection_api:deviceconnection_list", args=(d1.pk,)) + data = { + "credentials": self._get_credentials().pk, + "update_strategy": conn_settings.UPDATE_STRATEGIES[0][0], + "enabled": True, + "failure_reason": "", + } + with self.assertNumQueries(13): + response = self.client.post(path, data, content_type="application/json") + self.assertEqual(response.status_code, 201) + + +del BaseTestCommandInlines +del BaseTestConnectionAdmin +del BaseTestModels +del BaseTestModelsTransaction +del BaseTestSsh +del BaseTestTasks +del BaseTestNotifications +del BaseTestNotificationTransaction +del BaseTestConnectionApi diff --git a/tests/openwisp2/sample_firmware_upgrader/tests.py b/tests/openwisp2/sample_firmware_upgrader/tests.py index 5abbdbefb..579449dc1 100644 --- a/tests/openwisp2/sample_firmware_upgrader/tests.py +++ b/tests/openwisp2/sample_firmware_upgrader/tests.py @@ -30,6 +30,9 @@ from openwisp_firmware_upgrader.tests.test_private_storage import ( TestPrivateStorage as BaseTestPrivateStorage, ) +from openwisp_firmware_upgrader.tests.test_selenium import ( + TestDeviceAdmin as BaseTestDeviceAdmin, +) from openwisp_firmware_upgrader.tests.test_tasks import TestTasks as BaseTestTasks BatchUpgradeOperation = load_model("BatchUpgradeOperation") @@ -41,8 +44,8 @@ class TestAdmin(BaseTestAdmin): - app_label = "sample_firmware_upgrader" - build_list_url = reverse(f"admin:{app_label}_build_changelist") + _mock_connect = "openwisp2.sample_connection.models.DeviceConnection.connect" + build_list_url = reverse(f"admin:{BaseTestAdmin.app_label}_build_changelist") def test_category_details(self): self._login() @@ -73,7 +76,9 @@ def test_firmware_image_details(self): def test_device_firmware_details(self): self._login() device_fw = self._create_device_firmware(details="sample device_fw details") - path = reverse("admin:config_device_change", args=[device_fw.device_id]) + path = reverse( + f"admin:{self.config_app_label}_device_change", args=[device_fw.device_id] + ) r = self.client.get(path) self.assertContains( r, @@ -101,14 +106,18 @@ def test_upgrede_operation_details(self): uo = UpgradeOperation.objects.first() uo.details = "Test Upgrade device details" uo.save() - url = reverse("admin:config_device_change", args=[device_fw.device.pk]) + url = reverse( + f"admin:{self.config_app_label}_device_change", args=[device_fw.device.pk] + ) r = self.client.get(url) self.assertContains(r, '
Test Upgrade device details') class TestAdminTransaction(BaseTestAdminTransaction): - app_label = "sample_firmware_upgrader" - build_list_url = reverse(f"admin:{app_label}_build_changelist") + _mock_connect = "openwisp2.sample_connection.models.DeviceConnection.connect" + build_list_url = reverse( + f"admin:{BaseTestAdminTransaction.app_label}_build_changelist" + ) class TestModels(BaseTestModels): @@ -116,6 +125,7 @@ class TestModels(BaseTestModels): class TestModelsTransaction(BaseTestModelsTransaction): + _mock_connect = "openwisp2.sample_connection.models.DeviceConnection.connect" pass @@ -127,7 +137,13 @@ class TestPrivateStorage(BaseTestPrivateStorage): pass +class TestDeviceAdmin(BaseTestDeviceAdmin): + _mock_connect = "openwisp2.sample_connection.models.DeviceConnection.connect" + pass + + class TestTasks(BaseTestTasks): + _mock_connect = "openwisp2.sample_connection.models.DeviceConnection.connect" pass @@ -158,6 +174,7 @@ class TestOrgAPIMixin(BaseTestOrgAPIMixin): del BaseTestModelsTransaction del BaseTestOpenwrtUpgrader del BaseTestPrivateStorage +del BaseTestDeviceAdmin del BaseTestTasks del BaseTestBuildViews del BaseTestCategoryViews diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index 96b318a90..bfb2ca54a 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -240,6 +240,19 @@ "sample_firmware_upgrader.UpgradeOperation" ) + # For controller extended apps: + # Replace Connection + connection_index = INSTALLED_APPS.index("openwisp_controller.connection") + INSTALLED_APPS.remove("openwisp_controller.connection") + INSTALLED_APPS.insert(connection_index, "openwisp2.sample_connection") + # Extended apps + EXTENDED_APPS.append("openwisp_controller.connection") + # Swapper + CONNECTION_CREDENTIALS_MODEL = "sample_connection.Credentials" + CONNECTION_DEVICECONNECTION_MODEL = "sample_connection.DeviceConnection" + CONNECTION_COMMAND_MODEL = "sample_connection.Command" + + TEST_RUNNER = "openwisp_utils.tests.TimeLoggingTestRunner" # local settings must be imported before test runner otherwise they'll be ignored diff --git a/tests/openwisp2/urls.py b/tests/openwisp2/urls.py index 11c91c14b..0d17209c8 100644 --- a/tests/openwisp2/urls.py +++ b/tests/openwisp2/urls.py @@ -1,3 +1,5 @@ +import os + from django.conf import settings from django.conf.urls.static import static from django.contrib import admin @@ -5,11 +7,32 @@ from django.urls import include, path, reverse_lazy from django.views.generic import RedirectView +from openwisp_controller.connection.api.urls import ( + get_api_urls as get_connection_api_urls, +) from openwisp_users.api.urls import get_api_urls +from .sample_connection.api import views as connection_api_views + redirect_view = RedirectView.as_view(url=reverse_lazy("admin:index")) -urlpatterns = [ +urlpatterns = [] + +if os.environ.get("SAMPLE_APP", False): + urlpatterns += [ + path( + "", + include( + ( + get_connection_api_urls(connection_api_views), + "connection_api", + ), + namespace="connection_api", + ), + ), + ] + +urlpatterns += [ path("admin/", admin.site.urls), path("", redirect_view, name="index"), path("", include("openwisp_controller.urls")),