diff --git a/.github/workflows/docker_test.yaml b/.github/workflows/docker_test.yaml index d96621ee66..b4bb37715d 100644 --- a/.github/workflows/docker_test.yaml +++ b/.github/workflows/docker_test.yaml @@ -3,9 +3,8 @@ # This CI action runs on pushes to either the master or stable branches # 1. Build the development docker image (as per the documentation) -# 2. Install requied python libs into the docker container -# 3. Launch the container -# 4. Check that the API endpoint is available +# 2. Launch the development server, and update the installation +# 3. Run unit tests within the docker context name: Docker Test @@ -26,12 +25,14 @@ jobs: - name: Build Docker Image run: | cd docker - docker-compose -f docker-compose.sqlite.yml build - docker-compose -f docker-compose.sqlite.yml run inventree-dev-server invoke update - docker-compose -f docker-compose.sqlite.yml up -d - - name: Sleepy Time - run: sleep 60 - - name: Test API + docker-compose build + docker-compose run inventree-dev-server invoke update + docker-compose up -d + - name: Wait for Server run: | - pip install requests - python3 ci/check_api_endpoint.py + cd docker + docker-compose run inventree-dev-server invoke wait + - name: Run unit tests + run: | + cd docker + docker-compose run inventree-dev-server invoke test diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 8b2f4c133a..501eed0834 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -1,5 +1,7 @@ import json -from test.support import EnvironmentVarGuard +import os + +from unittest import mock from django.test import TestCase, override_settings import django.core.exceptions as django_exceptions @@ -449,17 +451,20 @@ class TestSettings(TestCase): def setUp(self) -> None: self.user_mdl = get_user_model() - self.env = EnvironmentVarGuard() # Create a user for auth user = get_user_model() self.user = user.objects.create_superuser('testuser1', 'test1@testing.com', 'password1') self.client.login(username='testuser1', password='password1') - def run_reload(self): + def in_env_context(self, envs={}): + """Patch the env to include the given dict""" + return mock.patch.dict(os.environ, envs) + + def run_reload(self, envs={}): from plugin import registry - with self.env: + with self.in_env_context(envs): settings.USER_ADDED = False registry.reload_plugins() @@ -475,25 +480,28 @@ class TestSettings(TestCase): self.assertEqual(user_count(), 1) # not enough set - self.env.set('INVENTREE_ADMIN_USER', 'admin') # set username - self.run_reload() + self.run_reload({ + 'INVENTREE_ADMIN_USER': 'admin' + }) self.assertEqual(user_count(), 1) # enough set - self.env.set('INVENTREE_ADMIN_USER', 'admin') # set username - self.env.set('INVENTREE_ADMIN_EMAIL', 'info@example.com') # set email - self.env.set('INVENTREE_ADMIN_PASSWORD', 'password123') # set password - self.run_reload() + self.run_reload({ + 'INVENTREE_ADMIN_USER': 'admin', # set username + 'INVENTREE_ADMIN_EMAIL': 'info@example.com', # set email + 'INVENTREE_ADMIN_PASSWORD': 'password123' # set password + }) self.assertEqual(user_count(), 2) # create user manually self.user_mdl.objects.create_user('testuser', 'test@testing.com', 'password') self.assertEqual(user_count(), 3) # check it will not be created again - self.env.set('INVENTREE_ADMIN_USER', 'testuser') - self.env.set('INVENTREE_ADMIN_EMAIL', 'test@testing.com') - self.env.set('INVENTREE_ADMIN_PASSWORD', 'password') - self.run_reload() + self.run_reload({ + 'INVENTREE_ADMIN_USER': 'testuser', + 'INVENTREE_ADMIN_EMAIL': 'test@testing.com', + 'INVENTREE_ADMIN_PASSWORD': 'password', + }) self.assertEqual(user_count(), 3) # make sure to clean up @@ -517,20 +525,30 @@ class TestSettings(TestCase): def test_helpers_cfg_file(self): # normal run - not configured - self.assertIn('InvenTree/InvenTree/config.yaml', config.get_config_file()) + + valid = [ + 'inventree/config.yaml', + 'inventree/dev/config.yaml', + ] + + self.assertTrue(any([opt in config.get_config_file().lower() for opt in valid])) # with env set - with self.env: - self.env.set('INVENTREE_CONFIG_FILE', 'my_special_conf.yaml') - self.assertIn('InvenTree/InvenTree/my_special_conf.yaml', config.get_config_file()) + with self.in_env_context({'INVENTREE_CONFIG_FILE': 'my_special_conf.yaml'}): + self.assertIn('inventree/inventree/my_special_conf.yaml', config.get_config_file().lower()) def test_helpers_plugin_file(self): # normal run - not configured - self.assertIn('InvenTree/InvenTree/plugins.txt', config.get_plugin_file()) + + valid = [ + 'inventree/plugins.txt', + 'inventree/dev/plugins.txt', + ] + + self.assertTrue(any([opt in config.get_plugin_file().lower() for opt in valid])) # with env set - with self.env: - self.env.set('INVENTREE_PLUGIN_FILE', 'my_special_plugins.txt') + with self.in_env_context({'INVENTREE_PLUGIN_FILE': 'my_special_plugins.txt'}): self.assertIn('my_special_plugins.txt', config.get_plugin_file()) def test_helpers_setting(self): @@ -539,8 +557,7 @@ class TestSettings(TestCase): self.assertEqual(config.get_setting(TEST_ENV_NAME, None, '123!'), '123!') # with env set - with self.env: - self.env.set(TEST_ENV_NAME, '321') + with self.in_env_context({TEST_ENV_NAME: '321'}): self.assertEqual(config.get_setting(TEST_ENV_NAME, None), '321') diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index 28df4503c9..98e545ee66 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -1046,24 +1046,29 @@ class PartDetailTests(InvenTreeAPITestCase): ) self.assertEqual(response.status_code, 400) + self.assertIn('Upload a valid image', str(response.data)) - # Now try to upload a valid image file - img = PIL.Image.new('RGB', (128, 128), color='red') - img.save('dummy_image.jpg') + # Now try to upload a valid image file, in multiple formats + for fmt in ['jpg', 'png', 'bmp', 'webp']: + fn = f'dummy_image.{fmt}' - with open('dummy_image.jpg', 'rb') as dummy_image: - response = upload_client.patch( - url, - { - 'image': dummy_image, - }, - format='multipart', - ) + img = PIL.Image.new('RGB', (128, 128), color='red') + img.save(fn) - self.assertEqual(response.status_code, 200) + with open(fn, 'rb') as dummy_image: + response = upload_client.patch( + url, + { + 'image': dummy_image, + }, + format='multipart', + ) - # And now check that the image has been set - p = Part.objects.get(pk=pk) + self.assertEqual(response.status_code, 200) + + # And now check that the image has been set + p = Part.objects.get(pk=pk) + self.assertIsNotNone(p.image) def test_details(self): """ diff --git a/InvenTree/part/test_part.py b/InvenTree/part/test_part.py index 09e8f97c64..2df3c10b01 100644 --- a/InvenTree/part/test_part.py +++ b/InvenTree/part/test_part.py @@ -2,6 +2,7 @@ from allauth.account.models import EmailAddress +from django.conf import settings from django.contrib.auth import get_user_model from django.test import TestCase @@ -64,11 +65,21 @@ class TemplateTagTest(TestCase): def test_hash(self): result_hash = inventree_extras.inventree_commit_hash() - self.assertGreater(len(result_hash), 5) + if settings.DOCKER: + # Testing inside docker environment *may* return an empty git commit hash + # In such a case, skip this check + pass + else: + self.assertGreater(len(result_hash), 5) def test_date(self): d = inventree_extras.inventree_commit_date() - self.assertEqual(len(d.split('-')), 3) + if settings.DOCKER: + # Testing inside docker environment *may* return an empty git commit hash + # In such a case, skip this check + pass + else: + self.assertEqual(len(d.split('-')), 3) def test_github(self): self.assertIn('github.com', inventree_extras.inventree_github_url()) diff --git a/docker/Dockerfile b/docker/Dockerfile index cefd2c2b61..1b7c16db30 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as base +FROM alpine:3.14 as base # GitHub source ARG repository="https://github.com/inventree/InvenTree.git" @@ -62,13 +62,13 @@ RUN apk -U upgrade RUN apk add --no-cache git make bash \ gcc libgcc g++ libstdc++ \ gnupg \ - libjpeg-turbo libjpeg-turbo-dev jpeg jpeg-dev \ + libjpeg-turbo libjpeg-turbo-dev jpeg jpeg-dev libwebp-dev \ libffi libffi-dev \ zlib zlib-dev \ # Special deps for WeasyPrint (these will be deprecated once WeasyPrint drops cairo requirement) cairo cairo-dev pango pango-dev gdk-pixbuf \ # Fonts - fontconfig ttf-droid ttf-liberation ttf-dejavu ttf-opensans ttf-ubuntu-font-family font-croscore font-noto \ + fontconfig ttf-droid ttf-liberation ttf-dejavu ttf-opensans font-croscore font-noto \ # Core python python3 python3-dev py3-pip \ # SQLite support diff --git a/requirements.txt b/requirements.txt index 43d077104f..c7d546578d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,7 +39,7 @@ inventree # Install the latest version of the Inve isort==5.10.1 # DEV: python import sorting markdown==3.3.4 # Force particular version of markdown pep8-naming==0.11.1 # PEP naming convention extension -pillow==9.0.1 # Image manipulation +pillow==9.1.0 # Image manipulation py-moneyed==0.8.0 # Specific version requirement for py-moneyed pygments==2.7.4 # Syntax highlighting python-barcode[images]==0.13.1 # Barcode generator