From db42ffcf7c398470ce7ebb93cc648a56258460e2 Mon Sep 17 00:00:00 2001
From: Oliver <oliver.henry.walters@gmail.com>
Date: Thu, 2 Mar 2023 13:51:52 +1100
Subject: [PATCH] Slow tests (#4435)

* Add integration for django-slowtest

* Sample test improvement

- Reduces test from 0.7s to 0.2s

* Run CI tests with slowreport

* Fix requirements file

* Fix test command

* Fix bulk_create in unit tests

* Remove bulk_create entirely

* remove another bulk_create call

* Reduce long test from ~1000 seconds to ~1 second
---
 InvenTree/InvenTree/settings.py | 26 +++++++++++++++++---------
 InvenTree/part/test_api.py      | 29 ++++++++++++++++++++---------
 InvenTree/part/test_category.py |  2 --
 requirements-dev.in             |  1 +
 requirements-dev.txt            |  4 ++++
 tasks.py                        |  2 +-
 6 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py
index a2896738e8..1bd14925bf 100644
--- a/InvenTree/InvenTree/settings.py
+++ b/InvenTree/InvenTree/settings.py
@@ -32,15 +32,24 @@ INVENTREE_NEWS_URL = 'https://inventree.org/news/feed.atom'
 # Determine if we are running in "test" mode e.g. "manage.py test"
 TESTING = 'test' in sys.argv
 
-# Note: The following fix is "required" for docker build workflow
-# Note: 2022-12-12 still unsure why...
-if TESTING and os.getenv('INVENTREE_DOCKER'):
-    # Ensure that sys.path includes global python libs
-    site_packages = '/usr/local/lib/python3.9/site-packages'
+if TESTING:
 
-    if site_packages not in sys.path:
-        print("Adding missing site-packages path:", site_packages)
-        sys.path.append(site_packages)
+    # Use a weaker password hasher for testing (improves testing speed)
+    PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher',]
+
+    # Enable slow-test-runner
+    TEST_RUNNER = 'django_slowtests.testrunner.DiscoverSlowestTestsRunner'
+    NUM_SLOW_TESTS = 25
+
+    # Note: The following fix is "required" for docker build workflow
+    # Note: 2022-12-12 still unsure why...
+    if os.getenv('INVENTREE_DOCKER'):
+        # Ensure that sys.path includes global python libs
+        site_packages = '/usr/local/lib/python3.9/site-packages'
+
+        if site_packages not in sys.path:
+            print("Adding missing site-packages path:", site_packages)
+            sys.path.append(site_packages)
 
 # Are environment variables manipulated by tests? Needs to be set by testing code
 TESTING_ENV = False
@@ -901,7 +910,6 @@ CUSTOM_LOGO = get_custom_file('INVENTREE_CUSTOM_LOGO', 'customize.logo', 'custom
 CUSTOM_SPLASH = get_custom_file('INVENTREE_CUSTOM_SPLASH', 'customize.splash', 'custom splash')
 
 CUSTOMIZE = get_setting('INVENTREE_CUSTOMIZE', 'customize', {})
-
 if DEBUG:
     logger.info("InvenTree running with DEBUG enabled")
 
diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py
index 22d689c8d0..e4ab05ae7f 100644
--- a/InvenTree/part/test_api.py
+++ b/InvenTree/part/test_api.py
@@ -2957,17 +2957,28 @@ class PartStocktakeTest(InvenTreeAPITestCase):
 
         total = 0
 
-        # Create some entries
-        for p in Part.objects.all():
+        # Iterate over (up to) 5 parts in the database
+        for p in Part.objects.all()[:5]:
 
-            for n in range(p.pk):
-                PartStocktake.objects.create(
-                    part=p,
-                    quantity=(n + 1) * 100,
+            # Create some entries
+            to_create = []
+
+            n = p.pk % 10
+
+            for idx in range(n):
+                to_create.append(
+                    PartStocktake(
+                        part=p,
+                        quantity=(idx + 1) * 100,
+                    )
                 )
 
-            total += p.pk
+                total += 1
 
+            # Create all entries in a single bulk-create
+            PartStocktake.objects.bulk_create(to_create)
+
+            # Query list endpoint
             response = self.get(
                 url,
                 {
@@ -2976,8 +2987,8 @@ class PartStocktakeTest(InvenTreeAPITestCase):
                 expected_code=200,
             )
 
-            # List by part ID
-            self.assertEqual(len(response.data), p.pk)
+            # Check that the expected number of PartStocktake instances has been created
+            self.assertEqual(len(response.data), n)
 
         # List all entries
         response = self.get(url, {}, expected_code=200)
diff --git a/InvenTree/part/test_category.py b/InvenTree/part/test_category.py
index 7aa94f5535..8044dddf7e 100644
--- a/InvenTree/part/test_category.py
+++ b/InvenTree/part/test_category.py
@@ -256,8 +256,6 @@ class CategoryTest(TestCase):
 
         # At this point, we are confident that the tree is correctly structured
 
-        # Add some parts to category B3
-
         for i in range(10):
             Part.objects.create(
                 name=f'Part {i}',
diff --git a/requirements-dev.in b/requirements-dev.in
index 0aaf254401..1a35909ede 100644
--- a/requirements-dev.in
+++ b/requirements-dev.in
@@ -3,6 +3,7 @@
 coverage                                # Unit test coverage
 coveralls==2.1.2                        # Coveralls linking (for tracking coverage)  # PINNED 2022-06-28 - Old version needed for correct upload
 django-debug-toolbar                    # Debug / profiling toolbar
+django-slowtests                        # Show which unit tests are running slowly
 django-test-migrations                  # Unit testing for database migrations
 flake8                                  # PEP checking
 flake8-docstrings                       # docstring format testing
diff --git a/requirements-dev.txt b/requirements-dev.txt
index cf08515231..0acd96eb33 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -190,10 +190,14 @@ django==3.2.18 \
     # via
     #   -c requirements.txt
     #   django-debug-toolbar
+    #   django-slowtests
 django-debug-toolbar==3.8.1 \
     --hash=sha256:24ef1a7d44d25e60d7951e378454c6509bf536dce7e7d9d36e7c387db499bc27 \
     --hash=sha256:879f8a4672d41621c06a4d322dcffa630fc4df056cada6e417ed01db0e5e0478
     # via -r requirements-dev.in
+django-slowtests==1.1.1 \
+    --hash=sha256:3c6936d420c9df444ac03625b41d97de043c662bbde61fbcd33e4cd407d0c247
+    # via -r requirements-dev.in
 django-test-migrations==1.2.0 \
     --hash=sha256:874884ff4e980583cd9f1c986bb9fbfe72b436e759be23004e4f52c26a15f363 \
     --hash=sha256:9e8b9b4364fef70dde10a5f85c5a75d447ca2189ec648325610fab1268daec97
diff --git a/tasks.py b/tasks.py
index 3788b5f29b..85501e024e 100644
--- a/tasks.py
+++ b/tasks.py
@@ -559,7 +559,7 @@ def test(c, disable_pty=False):
     pty = not disable_pty
 
     # Run coverage tests
-    manage(c, 'test', pty=pty)
+    manage(c, 'test --slowreport', pty=pty)
 
 
 @task(help={'dev': 'Set up development environment at the end'})