diff --git a/openwisp_controller/config/base/base.py b/openwisp_controller/config/base/base.py index 8b55ad019..e28bc6700 100644 --- a/openwisp_controller/config/base/base.py +++ b/openwisp_controller/config/base/base.py @@ -203,7 +203,7 @@ def clean_netjsonconfig_backend(cls, backend): 'Invalid configuration triggered by "#/{0}", ' "validator says:\n\n{1}".format(trigger, error) ) - raise ValidationError(message) + raise ValidationError({"config": message}) @cached_property def backend_class(self): diff --git a/openwisp_controller/config/tests/test_config.py b/openwisp_controller/config/tests/test_config.py index 10bedd043..3e0b90513 100644 --- a/openwisp_controller/config/tests/test_config.py +++ b/openwisp_controller/config/tests/test_config.py @@ -152,7 +152,7 @@ def test_netjson_validation(self): try: c.full_clean() except ValidationError as e: - self.assertIn("Invalid configuration", e.message_dict["__all__"][0]) + self.assertIn("Invalid configuration", e.message_dict["config"][0]) else: self.fail("ValidationError not raised") diff --git a/openwisp_controller/config/tests/test_template.py b/openwisp_controller/config/tests/test_template.py index a8118a71c..e553bbccb 100644 --- a/openwisp_controller/config/tests/test_template.py +++ b/openwisp_controller/config/tests/test_template.py @@ -801,6 +801,36 @@ def test_task_called(self, mocked_task): template.save() mocked_task.assert_not_called() + def test_validation_fix_attached_to_config_field(self): + """ + Ensure netjsonconfig validation errors are attached to the 'config' field + """ + config = { + "interfaces": [ + { + "name": "vpn_test", + "type": "openvpn", + "mode": "server", + # Missing required fields like keys, etc. + } + ] + } + t = Template( + name="validation_test", + backend="netjsonconfig.OpenWrt", + config=config, + ) + try: + t.full_clean() + except ValidationError as e: + self.assertIn("config", e.message_dict) + self.assertIn( + 'Invalid configuration triggered by "#/interfaces/0"', + e.message_dict["config"][0], + ) + else: + self.fail("ValidationError not raised") + @mock.patch.object(task_logger, "warning") def test_task_failure(self, mocked_warning): update_template_related_config_status.delay(uuid.uuid4())