mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	* Simplified dockerfile - Changed from alpine to python:slim - Removed some database libs (because we *connect* to a db, not host it) * - Add gettext as required package - Only create inventree user as part of production build (leave admin access for dev build) * Tweaks for tasks.py * Fix user permissions (drop to inventree user) * Drop to the 'inventree' user level as part of init.sh - As we have mounted volumes at 'run time' we need to ensure that the inventree user has correct permissions! - Ref: https://stackoverflow.com/questions/39397548/how-to-give-non-root-user-in-docker-container-access-to-a-volume-mounted-on-the * Adjust user setup - Only drop to non-root user as part of "production" build - Mounted external volumes make it tricky when in the dev build - Might want to revisit this later on * More dockerfile changes - reduce required system packages - * Add new docker github workflow * Print some more debug * GITHUB_BASE_REF * Add gnupg to base requirements * Improve debug output during testing * Refactoring updates for label printing API - Update weasyprint version to 55.0 - Generate labels as pdf files - Provide filename to label printing plugin - Additional unit testing - Improve extraction of some hidden debug data during TESTING - Fix a spelling mistake (notifaction -> notification) * Working on github action * More testing * Add requirement for pdf2image * Fix label printing plugin and update unit testing * Add required packages for CI * Move docker files to the top level directory - This allows us to build the production image directly from soure - Don't need to re-download the source code from github - Note: The docker install guide will need to be updated! * Fix for docker ci file * Print GIT SHA * Bake git information into the production image * Add some exta docstrings to dockerfile * Simplify version check script * Extract git commit info * Extract docker tag from check_version.py * Newline * More work on the docker workflow * Dockerfile fixes - Directory / path issues * Dockerfile fixes - Directory / path issues * Ignore certain steps on a pull request * Add poppler-utils to CI * Consolidate version check into existing CI file * Don't run docker workflow on pull request * Pass docker image tag through to the build Also check .j2k files * Add supervisord.conf example file back in * Remove --no-cache-dir option from pip install
		
			
				
	
	
		
			228 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Unit tests for the label printing mixin"""
 | |
| import os
 | |
| 
 | |
| from django.apps import apps
 | |
| from django.urls import reverse
 | |
| 
 | |
| from PIL import Image
 | |
| 
 | |
| from common.models import InvenTreeSetting
 | |
| from InvenTree.api_tester import InvenTreeAPITestCase
 | |
| from label.models import PartLabel, StockItemLabel, StockLocationLabel
 | |
| from part.models import Part
 | |
| from plugin.base.label.mixins import LabelPrintingMixin
 | |
| from plugin.helpers import MixinNotImplementedError
 | |
| from plugin.plugin import InvenTreePlugin
 | |
| from plugin.registry import registry
 | |
| from stock.models import StockItem, StockLocation
 | |
| 
 | |
| 
 | |
| class LabelMixinTests(InvenTreeAPITestCase):
 | |
|     """Test that the Label mixin operates correctly"""
 | |
| 
 | |
|     fixtures = [
 | |
|         'category',
 | |
|         'part',
 | |
|         'location',
 | |
|         'stock',
 | |
|     ]
 | |
| 
 | |
|     roles = 'all'
 | |
| 
 | |
|     def do_activate_plugin(self):
 | |
|         """Activate the 'samplelabel' plugin"""
 | |
| 
 | |
|         config = registry.get_plugin('samplelabel').plugin_config()
 | |
|         config.active = True
 | |
|         config.save()
 | |
| 
 | |
|     def do_url(self, parts, plugin_ref, label, url_name: str = 'api-part-label-print', url_single: str = 'part', invalid: bool = False):
 | |
|         """Generate an URL to print a label"""
 | |
|         # Construct URL
 | |
|         kwargs = {}
 | |
|         if label:
 | |
|             kwargs["pk"] = label.pk
 | |
| 
 | |
|         url = reverse(url_name, kwargs=kwargs)
 | |
| 
 | |
|         # Append part filters
 | |
|         if not parts:
 | |
|             pass
 | |
|         elif len(parts) == 1:
 | |
|             url += f'?{url_single}={parts[0].pk}'
 | |
|         elif len(parts) > 1:
 | |
|             url += '?' + '&'.join([f'{url_single}s={item.pk}' for item in parts])
 | |
| 
 | |
|         # Append an invalid item
 | |
|         if invalid:
 | |
|             url += f'&{url_single}{"s" if len(parts) > 1 else ""}=abc'
 | |
| 
 | |
|         # Append plugin reference
 | |
|         if plugin_ref:
 | |
|             url += f'&plugin={plugin_ref}'
 | |
| 
 | |
|         return url
 | |
| 
 | |
|     def test_wrong_implementation(self):
 | |
|         """Test that a wrong implementation raises an error"""
 | |
| 
 | |
|         class WrongPlugin(LabelPrintingMixin, InvenTreePlugin):
 | |
|             pass
 | |
| 
 | |
|         with self.assertRaises(MixinNotImplementedError):
 | |
|             plugin = WrongPlugin()
 | |
|             plugin.print_label(filename='test')
 | |
| 
 | |
|     def test_installed(self):
 | |
|         """Test that the sample printing plugin is installed"""
 | |
| 
 | |
|         # Get all label plugins
 | |
|         plugins = registry.with_mixin('labels')
 | |
|         self.assertEqual(len(plugins), 1)
 | |
| 
 | |
|         # But, it is not 'active'
 | |
|         plugins = registry.with_mixin('labels', active=True)
 | |
|         self.assertEqual(len(plugins), 0)
 | |
| 
 | |
|     def test_api(self):
 | |
|         """Test that we can filter the API endpoint by mixin"""
 | |
| 
 | |
|         url = reverse('api-plugin-list')
 | |
| 
 | |
|         # Try POST (disallowed)
 | |
|         response = self.client.post(url, {})
 | |
|         self.assertEqual(response.status_code, 405)
 | |
| 
 | |
|         response = self.client.get(
 | |
|             url,
 | |
|             {
 | |
|                 'mixin': 'labels',
 | |
|                 'active': True,
 | |
|             }
 | |
|         )
 | |
| 
 | |
|         # No results matching this query!
 | |
|         self.assertEqual(len(response.data), 0)
 | |
| 
 | |
|         # What about inactive?
 | |
|         response = self.client.get(
 | |
|             url,
 | |
|             {
 | |
|                 'mixin': 'labels',
 | |
|                 'active': False,
 | |
|             }
 | |
|         )
 | |
| 
 | |
|         self.assertEqual(len(response.data), 0)
 | |
| 
 | |
|         self.do_activate_plugin()
 | |
|         # Should be available via the API now
 | |
|         response = self.client.get(
 | |
|             url,
 | |
|             {
 | |
|                 'mixin': 'labels',
 | |
|                 'active': True,
 | |
|             }
 | |
|         )
 | |
| 
 | |
|         self.assertEqual(len(response.data), 1)
 | |
|         data = response.data[0]
 | |
|         self.assertEqual(data['key'], 'samplelabel')
 | |
| 
 | |
|     def test_printing_process(self):
 | |
|         """Test that a label can be printed"""
 | |
| 
 | |
|         # Ensure the labels were created
 | |
|         apps.get_app_config('label').create_labels()
 | |
| 
 | |
|         # Lookup references
 | |
|         part = Part.objects.first()
 | |
|         plugin_ref = 'samplelabel'
 | |
|         label = PartLabel.objects.first()
 | |
| 
 | |
|         url = self.do_url([part], plugin_ref, label)
 | |
| 
 | |
|         # Non-exsisting plugin
 | |
|         response = self.get(f'{url}123', expected_code=404)
 | |
|         self.assertIn(f'Plugin \'{plugin_ref}123\' not found', str(response.content, 'utf8'))
 | |
| 
 | |
|         # Inactive plugin
 | |
|         response = self.get(url, expected_code=400)
 | |
|         self.assertIn(f'Plugin \'{plugin_ref}\' is not enabled', str(response.content, 'utf8'))
 | |
| 
 | |
|         # Active plugin
 | |
|         self.do_activate_plugin()
 | |
| 
 | |
|         # Print one part
 | |
|         self.get(url, expected_code=200)
 | |
| 
 | |
|         # Print multiple parts
 | |
|         self.get(self.do_url(Part.objects.all()[:2], plugin_ref, label), expected_code=200)
 | |
| 
 | |
|         # Print multiple parts without a plugin
 | |
|         self.get(self.do_url(Part.objects.all()[:2], None, label), expected_code=200)
 | |
| 
 | |
|         # Print multiple parts without a plugin in debug mode
 | |
|         InvenTreeSetting.set_setting('REPORT_DEBUG_MODE', True, None)
 | |
|         response = self.get(self.do_url(Part.objects.all()[:2], None, label), expected_code=200)
 | |
|         self.assertIn('@page', str(response.content))
 | |
| 
 | |
|         # Print no part
 | |
|         self.get(self.do_url(None, plugin_ref, label), expected_code=400)
 | |
| 
 | |
|         # Test that the labels have been printed
 | |
|         # The sample labelling plugin simply prints to file
 | |
|         self.assertTrue(os.path.exists('label.pdf'))
 | |
| 
 | |
|         # Read the raw .pdf data - ensure it contains some sensible information
 | |
|         with open('label.pdf', 'rb') as f:
 | |
|             pdf_data = str(f.read())
 | |
|             self.assertIn('WeasyPrint', pdf_data)
 | |
| 
 | |
|         # Check that the .png file has already been created
 | |
|         self.assertTrue(os.path.exists('label.png'))
 | |
| 
 | |
|         # And that it is a valid image file
 | |
|         Image.open('label.png')
 | |
| 
 | |
|     def test_printing_endpoints(self):
 | |
|         """Cover the endpoints not covered by `test_printing_process`"""
 | |
|         plugin_ref = 'samplelabel'
 | |
| 
 | |
|         # Activate the label components
 | |
|         apps.get_app_config('label').create_labels()
 | |
|         self.do_activate_plugin()
 | |
| 
 | |
|         def run_print_test(label, qs, url_name, url_single):
 | |
|             """Run tests on single and multiple page printing
 | |
| 
 | |
|             Args:
 | |
|                 label (_type_): class of the label
 | |
|                 qs (_type_): class of the base queryset
 | |
|                 url_name (_type_): url for endpoints
 | |
|                 url_single (_type_): item lookup reference
 | |
|             """
 | |
|             label = label.objects.first()
 | |
|             qs = qs.objects.all()
 | |
| 
 | |
|             # List endpoint
 | |
|             self.get(self.do_url(None, None, None, f'{url_name}-list', url_single), expected_code=200)
 | |
| 
 | |
|             # List endpoint with filter
 | |
|             self.get(self.do_url(qs[:2], None, None, f'{url_name}-list', url_single, invalid=True), expected_code=200)
 | |
| 
 | |
|             # Single page printing
 | |
|             self.get(self.do_url(qs[:1], plugin_ref, label, f'{url_name}-print', url_single), expected_code=200)
 | |
| 
 | |
|             # Multi page printing
 | |
|             self.get(self.do_url(qs[:2], plugin_ref, label, f'{url_name}-print', url_single), expected_code=200)
 | |
| 
 | |
|         # Test StockItemLabels
 | |
|         run_print_test(StockItemLabel, StockItem, 'api-stockitem-label', 'item')
 | |
| 
 | |
|         # Test StockLocationLabels
 | |
|         run_print_test(StockLocationLabel, StockLocation, 'api-stocklocation-label', 'location')
 | |
| 
 | |
|         # Test PartLabels
 | |
|         run_print_test(PartLabel, Part, 'api-part-label', 'part')
 |