Arthur 13 órája
szülő
commit
7cff6a27d4
2 módosított fájl, 102 hozzáadás és 1 törlés
  1. 50 0
      netbox/extras/tests/test_api.py
  2. 52 1
      netbox/extras/tests/test_views.py

+ 50 - 0
netbox/extras/tests/test_api.py

@@ -1907,6 +1907,7 @@ class ScriptRunExecutionTestCase(APITestCase):
         site = ObjectVar(model=Site)
         site = ObjectVar(model=Site)
         sites = MultiObjectVar(model=Site, required=False)
         sites = MultiObjectVar(model=Site, required=False)
         label = StringVar(default='hello')
         label = StringVar(default='hello')
+        flag = BooleanVar(default=True)
 
 
         def run(self, data, commit=True):
         def run(self, data, commit=True):
             return 'ok'
             return 'ok'
@@ -2046,6 +2047,55 @@ class ScriptRunExecutionTestCase(APITestCase):
         self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
         self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
         mock_enqueue.assert_not_called()
         mock_enqueue.assert_not_called()
 
 
+    @patch('extras.jobs.ScriptJob.enqueue')
+    def test_run_ignores_undeclared_keys(self, mock_enqueue):
+        # Binding 'data' to the script's form means keys which don't correspond to a declared
+        # variable are dropped rather than forwarded to run(). This is the contract documented
+        # under "Running Custom Scripts > Via the API"; pin it so it can't regress silently.
+        payload = {
+            'data': {'site': self.sites[0].pk, 'bogus': 'ignored', 'id': 99},
+            'commit': True,
+        }
+
+        response = self.client.post(self.url, payload, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        data = mock_enqueue.call_args.kwargs['data']
+        self.assertEqual(data['site'], self.sites[0])
+        self.assertNotIn('bogus', data)
+        self.assertNotIn('id', data)
+
+    @patch('extras.jobs.ScriptJob.enqueue')
+    def test_run_rejects_omitted_required_var(self, mock_enqueue):
+        # 'site' is required and declares no default, so unlike 'label' it cannot be
+        # back-filled: omitting it must 400 rather than enqueue a job that fails at runtime.
+        payload = {'data': {'label': 'hi'}, 'commit': True}
+
+        response = self.client.post(self.url, payload, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
+        self.assertIn('site', response.data['data'])
+        mock_enqueue.assert_not_called()
+
+    @patch('extras.jobs.ScriptJob.enqueue')
+    def test_run_resolves_booleanvar_default_and_explicit_values(self, mock_enqueue):
+        # BooleanVar renders as a checkbox, and CheckboxInput reads a missing key as False.
+        # An omitted BooleanVar must therefore pick up its declared default, while an
+        # explicitly supplied False must not be overwritten by that default.
+        for case, supplied, expected in (
+            ('omitted', {}, True),
+            ('explicit False', {'flag': False}, False),
+            ('explicit True', {'flag': True}, True),
+        ):
+            with self.subTest(case=case):
+                mock_enqueue.reset_mock()
+                payload = {'data': {'site': self.sites[0].pk, **supplied}, 'commit': True}
+
+                response = self.client.post(self.url, payload, format='json', **self.header)
+
+                self.assertHttpStatus(response, status.HTTP_200_OK)
+                self.assertIs(mock_enqueue.call_args.kwargs['data']['flag'], expected)
+
 
 
 class CreatedUpdatedFilterTestCase(APITestCase):
 class CreatedUpdatedFilterTestCase(APITestCase):
 
 

+ 52 - 1
netbox/extras/tests/test_views.py

@@ -14,7 +14,7 @@ from core.models import Job, ObjectType
 from dcim.models import DeviceType, Manufacturer, Site
 from dcim.models import DeviceType, Manufacturer, Site
 from extras.choices import *
 from extras.choices import *
 from extras.models import *
 from extras.models import *
-from extras.scripts import BooleanVar, IntegerVar
+from extras.scripts import BooleanVar, IntegerVar, StringVar
 from extras.scripts import Script as PythonClass
 from extras.scripts import Script as PythonClass
 from users.models import Group, ObjectPermission, User
 from users.models import Group, ObjectPermission, User
 from utilities.testing import TestCase, ViewTestCases
 from utilities.testing import TestCase, ViewTestCases
@@ -1279,6 +1279,57 @@ class ScriptModuleCreateViewTestCase(TestCase):
         self.assertEqual(response.context['return_url'], reverse('extras:script_list'))
         self.assertEqual(response.context['return_url'], reverse('extras:script_list'))
 
 
 
 
+class ScriptDefaultBackfillTestCase(TestCase):
+    """
+    The UI and the REST API now share prepare_script_form(), so the UI's back-filling of
+    declared defaults (previously inline in ScriptView.post()) must keep working after
+    that logic moved into the helper.
+    """
+    user_permissions = ['extras.view_script', 'extras.run_script']
+
+    class TestScriptClass(PythonClass):
+        class Meta:
+            name = 'Backfill test'
+            commit_default = False
+
+        label = StringVar(default='hello')
+        flag = BooleanVar(default=True)
+
+        def run(self, data, commit):
+            return 'Complete'
+
+    @classmethod
+    def setUpTestData(cls):
+        with patch.object(ScriptModule, 'sync_classes'):
+            module = ScriptModule.objects.create(
+                file_root=ManagedFileRootPathChoices.SCRIPTS,
+                file_path='backfill_script.py',
+            )
+        cls.script = Script.objects.create(module=module, name='Backfill test', is_executable=True)
+
+    def setUp(self):
+        super().setUp()
+        python_class_patch = patch.object(Script, 'python_class', new=self.TestScriptClass)
+        python_class_patch.start()
+        self.addCleanup(python_class_patch.stop)
+
+    @tag('regression')
+    def test_ui_backfills_declared_defaults(self):
+        url = reverse('extras:script', kwargs={'pk': self.script.pk})
+
+        with (
+            patch('extras.views.any_workers_for_queue', return_value=True),
+            patch('extras.jobs.ScriptJob.enqueue') as mock_enqueue,
+        ):
+            mock_enqueue.return_value.pk = 1
+            response = self.client.post(url, {'_commit': 'true'})
+
+        self.assertEqual(response.status_code, 302)
+        data = mock_enqueue.call_args.kwargs['data']
+        self.assertEqual(data['label'], 'hello')
+        self.assertIs(data['flag'], True)
+
+
 class ScriptValidationErrorTestCase(TestCase):
 class ScriptValidationErrorTestCase(TestCase):
     user_permissions = ['extras.view_script', 'extras.run_script']
     user_permissions = ['extras.view_script', 'extras.run_script']