mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-27 14:16:02 +00:00
Preserve validation details when rendering labels (#12874)
* Preserve label rendering validation errors * Preserve label validation diagnostics before output deletion Keep logging validation errors in both rendering wrappers before re-raising the original exception. LabelTemplate.print deletes failed DataOutput records, so the error log preserves the diagnostic after the result is removed. Update the mocked tests to require logging while retaining the original validation details. * Apply Ruff preview formatting to label rendering tests
This commit is contained in:
@@ -51,6 +51,9 @@ class LabelPrintingMixin:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return label.render(instance, request=request, user=user)
|
return label.render(instance, request=request, user=user)
|
||||||
|
except ValidationError:
|
||||||
|
log_error('render_to_pdf', plugin=self.slug)
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
log_error('render_to_pdf', plugin=self.slug)
|
log_error('render_to_pdf', plugin=self.slug)
|
||||||
raise ValidationError(_('Error rendering label to PDF'))
|
raise ValidationError(_('Error rendering label to PDF'))
|
||||||
@@ -68,6 +71,9 @@ class LabelPrintingMixin:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return label.render_as_string(instance, request=request, user=user)
|
return label.render_as_string(instance, request=request, user=user)
|
||||||
|
except ValidationError:
|
||||||
|
log_error('render_to_html', plugin=self.slug)
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
log_error('render_to_html', plugin=self.slug)
|
log_error('render_to_html', plugin=self.slug)
|
||||||
raise ValidationError(_('Error rendering label to HTML'))
|
raise ValidationError(_('Error rendering label to HTML'))
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import os
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.test import SimpleTestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from pdfminer.high_level import extract_text
|
from pdfminer.high_level import extract_text
|
||||||
@@ -24,6 +26,65 @@ from report.tests import PrintTestMixins
|
|||||||
from stock.models import StockItem, StockLocation
|
from stock.models import StockItem, StockLocation
|
||||||
|
|
||||||
|
|
||||||
|
class LabelRenderingTests(SimpleTestCase):
|
||||||
|
"""Test error handling in the label rendering wrappers."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Create a label plugin without loading the plugin registry."""
|
||||||
|
|
||||||
|
class TestLabelPlugin(LabelPrintingMixin, InvenTreePlugin):
|
||||||
|
NAME = 'Test Label Printer'
|
||||||
|
|
||||||
|
self.plugin = TestLabelPlugin()
|
||||||
|
|
||||||
|
@mock.patch('plugin.base.label.mixins.log_error')
|
||||||
|
def test_validation_errors(self, log_error):
|
||||||
|
"""Log validation errors while preserving messages, codes, and parameters."""
|
||||||
|
for wrapper, renderer in [
|
||||||
|
('render_to_pdf', 'render'),
|
||||||
|
('render_to_html', 'render_as_string'),
|
||||||
|
]:
|
||||||
|
with self.subTest(wrapper=wrapper):
|
||||||
|
log_error.reset_mock()
|
||||||
|
error = ValidationError({
|
||||||
|
'serial': ValidationError(
|
||||||
|
'Missing serial number for %(part)s',
|
||||||
|
code='missing_serial',
|
||||||
|
params={'part': 'Test part'},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
label = mock.Mock(spec=LabelTemplate)
|
||||||
|
getattr(label, renderer).side_effect = error
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError) as raised:
|
||||||
|
getattr(self.plugin, wrapper)(label, mock.sentinel.instance, None)
|
||||||
|
|
||||||
|
self.assertIs(raised.exception, error)
|
||||||
|
self.assertEqual(
|
||||||
|
raised.exception.message_dict,
|
||||||
|
{'serial': ['Missing serial number for Test part']},
|
||||||
|
)
|
||||||
|
log_error.assert_called_once_with(wrapper, plugin=self.plugin.slug)
|
||||||
|
|
||||||
|
@mock.patch('plugin.base.label.mixins.log_error')
|
||||||
|
def test_unexpected_errors(self, log_error):
|
||||||
|
"""Log unexpected errors and return the existing generic messages."""
|
||||||
|
for wrapper, renderer, message in [
|
||||||
|
('render_to_pdf', 'render', 'Error rendering label to PDF'),
|
||||||
|
('render_to_html', 'render_as_string', 'Error rendering label to HTML'),
|
||||||
|
]:
|
||||||
|
with self.subTest(wrapper=wrapper):
|
||||||
|
log_error.reset_mock()
|
||||||
|
label = mock.Mock(spec=LabelTemplate)
|
||||||
|
getattr(label, renderer).side_effect = RuntimeError('Rendering failed')
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError) as raised:
|
||||||
|
getattr(self.plugin, wrapper)(label, mock.sentinel.instance, None)
|
||||||
|
|
||||||
|
self.assertEqual(raised.exception.messages, [message])
|
||||||
|
log_error.assert_called_once_with(wrapper, plugin=self.plugin.slug)
|
||||||
|
|
||||||
|
|
||||||
class LabelMixinTests(PrintTestMixins, InvenTreeAPITestCase):
|
class LabelMixinTests(PrintTestMixins, InvenTreeAPITestCase):
|
||||||
"""Test that the Label mixin operates correctly."""
|
"""Test that the Label mixin operates correctly."""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user