Răsfoiți Sursa

Initial implementation of tests for plugins framework

Jeremy Stretch 5 ani în urmă
părinte
comite
30e330c887

+ 2 - 1
netbox/extras/plugins/urls.py

@@ -19,7 +19,8 @@ plugin_admin_patterns = [
 
 # Register base/API URL patterns for each plugin
 for plugin in settings.PLUGINS:
-    app = apps.get_app_config(plugin)
+    plugin_name = plugin.split('.')[-1]
+    app = apps.get_app_config(plugin_name)
     base_url = getattr(app, 'base_url') or app.label
 
     # Check if the plugin specifies any base URLs

+ 15 - 0
netbox/extras/tests/dummy_plugin/__init__.py

@@ -0,0 +1,15 @@
+from extras.plugins import PluginConfig
+
+
+class DummyPluginConfig(PluginConfig):
+    name = 'extras.tests.dummy_plugin'
+    verbose_name = 'Dummy plugin'
+    version = '0.0'
+    description = 'For testing purposes only'
+    base_url = 'dummy-plugin'
+    middleware = [
+        'extras.tests.dummy_plugin.middleware.DummyMiddleware'
+    ]
+
+
+config = DummyPluginConfig

+ 9 - 0
netbox/extras/tests/dummy_plugin/admin.py

@@ -0,0 +1,9 @@
+from django.contrib import admin
+
+from netbox.admin import admin_site
+from .models import DummyModel
+
+
+@admin.register(DummyModel, site=admin_site)
+class AnimalAdmin(admin.ModelAdmin):
+    list_display = ('name', 'number')

+ 9 - 0
netbox/extras/tests/dummy_plugin/api/serializers.py

@@ -0,0 +1,9 @@
+from rest_framework.serializers import ModelSerializer
+from extras.tests.dummy_plugin.models import DummyModel
+
+
+class DummySerializer(ModelSerializer):
+
+    class Meta:
+        model = DummyModel
+        fields = ('id', 'name', 'sound')

+ 6 - 0
netbox/extras/tests/dummy_plugin/api/urls.py

@@ -0,0 +1,6 @@
+from rest_framework import routers
+from .views import DummyViewSet
+
+router = routers.DefaultRouter()
+router.register('dummy-models', DummyViewSet)
+urlpatterns = router.urls

+ 8 - 0
netbox/extras/tests/dummy_plugin/api/views.py

@@ -0,0 +1,8 @@
+from rest_framework.viewsets import ModelViewSet
+from extras.tests.dummy_plugin.models import DummyModel
+from .serializers import DummySerializer
+
+
+class DummyViewSet(ModelViewSet):
+    queryset = DummyModel.objects.all()
+    serializer_class = DummySerializer

+ 7 - 0
netbox/extras/tests/dummy_plugin/middleware.py

@@ -0,0 +1,7 @@
+class DummyMiddleware:
+
+    def __init__(self, get_response):
+        self.get_response = get_response
+
+    def __call__(self, request):
+        return self.get_response(request)

+ 23 - 0
netbox/extras/tests/dummy_plugin/migrations/0001_initial.py

@@ -0,0 +1,23 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='DummyModel',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=20)),
+                ('number', models.IntegerField(default=100)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+    ]

+ 0 - 0
netbox/extras/tests/dummy_plugin/migrations/__init__.py


+ 13 - 0
netbox/extras/tests/dummy_plugin/models.py

@@ -0,0 +1,13 @@
+from django.db import models
+
+
+class DummyModel(models.Model):
+    name = models.CharField(
+        max_length=20
+    )
+    number = models.IntegerField(
+        default=100
+    )
+
+    class Meta:
+        ordering = ['name']

+ 28 - 0
netbox/extras/tests/dummy_plugin/navigation.py

@@ -0,0 +1,28 @@
+from extras.plugins import PluginMenuButton, PluginMenuItem
+from utilities.choices import ButtonColorChoices
+
+
+menu_items = (
+    PluginMenuItem(
+        link='plugins:dummy_plugin:dummy_models',
+        link_text='Item 1',
+        buttons=(
+            PluginMenuButton(
+                link='plugins:netbox_animal_sounds:random_animal',
+                title='Random animal',
+                icon_class='fa-question'
+            ),
+            PluginMenuButton(
+                link='admin:netbox_animal_sounds_animal_add',
+                title='Add a new animal',
+                icon_class='fa-plus',
+                color=ButtonColorChoices.GREEN,
+                permissions=['netbox_animal_sounds.add_animal']
+            ),
+        )
+    ),
+    PluginMenuItem(
+        link='plugins:dummy_plugin:dummy_models',
+        link_text='Item 2',
+    ),
+)

+ 20 - 0
netbox/extras/tests/dummy_plugin/template_content.py

@@ -0,0 +1,20 @@
+from extras.plugins import PluginTemplateExtension
+
+
+class SiteContent(PluginTemplateExtension):
+    model = 'dcim.site'
+
+    def left_page(self):
+        return "SITE CONTENT - LEFT PAGE"
+
+    def right_page(self):
+        return "SITE CONTENT - RIGHT PAGE"
+
+    def full_width_page(self):
+        return "SITE CONTENT - FULL WIDTH PAGE"
+
+    def full_buttons(self):
+        return "SITE CONTENT - BUTTONS"
+
+
+template_extensions = [SiteContent]

+ 8 - 0
netbox/extras/tests/dummy_plugin/urls.py

@@ -0,0 +1,8 @@
+from django.urls import path
+
+from . import views
+
+
+urlpatterns = (
+    path('models/', views.DummyModelsView.as_view(), name='dummy_models'),
+)

+ 11 - 0
netbox/extras/tests/dummy_plugin/views.py

@@ -0,0 +1,11 @@
+from django.http import HttpResponse
+from django.views.generic import View
+
+from .models import DummyModel
+
+
+class DummyModelsView(View):
+
+    def get(self, request):
+        instance_count = DummyModel.objects.count()
+        return HttpResponse(f"Instances: {instance_count}")

+ 74 - 0
netbox/extras/tests/test_plugins.py

@@ -0,0 +1,74 @@
+from django.conf import settings
+from django.test import Client, TestCase
+from django.urls import reverse
+
+from extras.registry import registry
+from extras.tests.dummy_plugin.models import DummyModel
+from extras.tests.dummy_plugin.template_content import SiteContent
+
+
+class PluginTest(TestCase):
+
+    def test_config(self):
+
+        self.assertIn('extras.tests.dummy_plugin.DummyPluginConfig', settings.INSTALLED_APPS)
+
+    def test_models(self):
+
+        # Test saving an instance
+        instance = DummyModel(name='Instance 1', number=100)
+        instance.save()
+        self.assertIsNotNone(instance.pk)
+
+        # Test deleting an instance
+        instance.delete()
+        self.assertIsNone(instance.pk)
+
+    def test_admin(self):
+
+        # Test admin view URL resolution
+        url = reverse('admin:dummy_plugin_dummymodel_add')
+        self.assertEqual(url, '/admin/dummy_plugin/dummymodel/add/')
+
+    def test_views(self):
+
+        # Test URL resolution
+        url = reverse('plugins:dummy_plugin:dummy_models')
+        self.assertEqual(url, '/plugins/dummy-plugin/models/')
+
+        # Test GET request
+        client = Client()
+        response = client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_api_views(self):
+
+        # Test URL resolution
+        url = reverse('plugins-api:dummy_plugin-api:dummymodel-list')
+        self.assertEqual(url, '/api/plugins/dummy-plugin/dummy-models/')
+
+        # Test GET request
+        client = Client()
+        response = client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_menu_items(self):
+        """
+        Check that plugin MenuItems and MenuButtons are registered.
+        """
+        self.assertIn('Dummy plugin', registry['plugin_menu_items'])
+        menu_items = registry['plugin_menu_items']['Dummy plugin']
+        self.assertEqual(len(menu_items), 2)
+        self.assertEqual(len(menu_items[0].buttons), 2)
+
+    def test_template_extensions(self):
+        """
+        Check that plugin TemplateExtensions are registered.
+        """
+        self.assertIn(SiteContent, registry['plugin_template_extensions']['dcim.site'])
+
+    def test_middleware(self):
+        """
+        Check that plugin middleware is registered.
+        """
+        self.assertIn('extras.tests.dummy_plugin.middleware.DummyMiddleware', settings.MIDDLEWARE)

+ 4 - 0
netbox/netbox/configuration.testing.py

@@ -14,6 +14,10 @@ DATABASE = {
     'CONN_MAX_AGE': 300,
 }
 
+PLUGINS = [
+    'extras.tests.dummy_plugin'
+]
+
 REDIS = {
     'tasks': {
         'HOST': 'localhost',