Skip to content

Commit 2684481

Browse files
perf1: test cases: fix caching of system settings (#12861)
* test cases: fix caching of system settings * fix tests * fix caching for github * fix caching for github * simplify cache loading * fix test cases * set tags on (re)import
1 parent c70e5a1 commit 2684481

5 files changed

Lines changed: 67 additions & 50 deletions

File tree

dojo/middleware.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ def __init__(self, get_response):
8383

8484
def __call__(self, request):
8585
self.load()
86-
response = self.get_response(request)
87-
self.cleanup()
88-
return response
86+
try:
87+
return self.get_response(request)
88+
finally:
89+
# ensure cleanup happens even if an exception occurs
90+
self.cleanup()
8991

9092
def process_exception(self, request, exception):
9193
self.cleanup()
@@ -94,7 +96,6 @@ def process_exception(self, request, exception):
9496
def get_system_settings(cls):
9597
if hasattr(cls._thread_local, "system_settings"):
9698
return cls._thread_local.system_settings
97-
9899
return None
99100

100101
@classmethod
@@ -104,6 +105,8 @@ def cleanup(cls, *args, **kwargs): # noqa: ARG003
104105

105106
@classmethod
106107
def load(cls):
108+
# cleanup any existing settings first to ensure fresh state
109+
cls.cleanup()
107110
from dojo.models import System_Settings
108111
system_settings = System_Settings.objects.get(no_cache=True)
109112
cls._thread_local.system_settings = system_settings

run-unittest.sh

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env bash
22
unset TEST_CASE
3+
EXTRA_ARGS=()
34

45
bash ./docker/docker-compose-check.sh
56
if [[ $? -eq 1 ]]; then exit 1; fi
@@ -13,9 +14,11 @@ usage() {
1314
echo " --help -h - prints this dialogue."
1415
echo
1516
echo "You must specify a test case (arg)!"
17+
echo "Any additional arguments will be passed to the test command."
1618
echo
17-
echo "Example command:"
19+
echo "Example commands:"
1820
echo "./run-unittest.sh --test-case unittests.tools.test_stackhawk_parser.TestStackHawkParser"
21+
echo "./run-unittest.sh --test-case unittests.tools.test_stackhawk_parser.TestStackHawkParser -v3 --failfast"
1922
}
2023

2124
while [[ $# -gt 0 ]]; do
@@ -29,19 +32,13 @@ while [[ $# -gt 0 ]]; do
2932
usage
3033
exit 0
3134
;;
32-
-*)
33-
echo "Unknown option $1"
34-
usage
35-
exit 1
36-
;;
3735
*)
38-
POSITIONAL_ARGS+=("$1") # save positional arg
36+
EXTRA_ARGS+=("$1") # save extra arg
3937
shift # past argument
4038
;;
4139
esac
4240
done
4341

44-
4542
if [ -z "$TEST_CASE" ]
4643
then
4744
echo "No test case supplied."
@@ -50,7 +47,8 @@ then
5047
fi
5148

5249
echo "Running docker compose unit tests with test case $TEST_CASE ..."
53-
# Compose V2 integrates compose functions into the Docker platform, continuing to support
54-
# most of the previous docker-compose features and flags. You can run Compose V2 by
55-
# replacing the hyphen (-) with a space, using docker compose, instead of docker-compose.
56-
docker compose exec uwsgi bash -c "python manage.py test $TEST_CASE -v2 --keepdb"
50+
if [ ${#EXTRA_ARGS[@]} -gt 0 ]; then
51+
echo "Additional arguments: ${EXTRA_ARGS[*]}"
52+
fi
53+
54+
docker compose exec uwsgi bash -c "python manage.py test $TEST_CASE -v2 ${EXTRA_ARGS[*]} --keepdb"

unittests/dojo_test_case.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from dojo.jira_link import helper as jira_helper
1818
from dojo.jira_link.views import get_custom_field
19+
from dojo.middleware import DojoSytemSettingsMiddleware
1920
from dojo.models import (
2021
SEVERITIES,
2122
DojoMeta,
@@ -54,12 +55,15 @@ def decorator(test_func):
5455
def wrapper(*args, **kwargs):
5556
# Set the flag to the specified value
5657
System_Settings.objects.update(**{flag_name: value})
58+
# Reinitialize middleware with updated settings as this doesn't happen automatically during django tests
59+
DojoSytemSettingsMiddleware.load()
5760
try:
5861
return test_func(*args, **kwargs)
5962
finally:
6063
# Reset the flag to its original state after the test
6164
System_Settings.objects.update(**{flag_name: not value})
62-
65+
# Reinitialize middleware with updated settings as this doesn't happen automatically during django tests
66+
DojoSytemSettingsMiddleware.load()
6367
return wrapper
6468

6569
return decorator
@@ -74,11 +78,15 @@ def wrapper(*args, **kwargs):
7478
old_value = getattr(System_Settings.objects.get(), field)
7579
# Set the flag to the specified value
7680
System_Settings.objects.update(**{field: value})
81+
# Reinitialize middleware with updated settings as this doesn't happen automatically during django tests
82+
DojoSytemSettingsMiddleware.load()
7783
try:
7884
return test_func(*args, **kwargs)
7985
finally:
8086
# Reset the flag to its original state after the test
8187
System_Settings.objects.update(**{field: old_value})
88+
# Reinitialize middleware with updated settings as this doesn't happen automatically during django tests
89+
DojoSytemSettingsMiddleware.load()
8290

8391
return wrapper
8492

@@ -90,28 +98,13 @@ class DojoTestUtilsMixin:
9098
def get_test_admin(self, *args, **kwargs):
9199
return User.objects.get(username="admin")
92100

93-
def system_settings(
94-
self,
95-
*,
96-
enable_jira=False,
97-
enable_jira_web_hook=False,
98-
disable_jira_webhook_secret=False,
99-
jira_webhook_secret=None,
100-
enable_github=False,
101-
enable_product_tag_inehritance=False,
102-
enable_product_grade=True,
103-
enable_webhooks_notifications=True,
104-
):
101+
def system_settings(self, **kwargs):
105102
ss = System_Settings.objects.get()
106-
ss.enable_jira = enable_jira
107-
ss.enable_jira_web_hook = enable_jira_web_hook
108-
ss.disable_jira_webhook_secret = disable_jira_webhook_secret
109-
ss.jira_webhook_secret = jira_webhook_secret
110-
ss.enable_github = enable_github
111-
ss.enable_product_tag_inheritance = enable_product_tag_inehritance
112-
ss.enable_product_grade = enable_product_grade
113-
ss.enable_webhooks_notifications = enable_webhooks_notifications
103+
for key, value in kwargs.items():
104+
setattr(ss, key, value)
114105
ss.save()
106+
# Refresh the cache with the new settings
107+
DojoSytemSettingsMiddleware.load()
115108

116109
def create_product_type(self, name, *args, description="dummy description", **kwargs):
117110
product_type = Product_Type(name=name, description=description)
@@ -489,6 +482,11 @@ class DojoTestCase(TestCase, DojoTestUtilsMixin):
489482
def __init__(self, *args, **kwargs):
490483
TestCase.__init__(self, *args, **kwargs)
491484

485+
def setUp(self):
486+
super().setUp()
487+
# Initialize middleware with fresh settings from db
488+
DojoSytemSettingsMiddleware.load()
489+
492490
def common_check_finding(self, finding):
493491
self.assertIn(finding.severity, SEVERITIES)
494492
finding.clean()

unittests/test_importers_performance.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,22 @@ class TestDojoImporterPerformance(DojoTestCase):
3838

3939
def setUp(self):
4040
super().setUp()
41+
4142
self.system_settings(enable_webhooks_notifications=False)
4243
self.system_settings(enable_product_grade=False)
4344
self.system_settings(enable_github=False)
45+
# from dojo.models import System_Settings
46+
47+
# # Configure system settings directly
48+
# from dojo.middleware import DojoSytemSettingsMiddleware
49+
# from dojo.models import System_Settings
50+
# system_settings = System_Settings.objects.get()
51+
# system_settings.enable_product_tag_inheritance = True
52+
# system_settings.save()
53+
54+
# Initialize middleware with modified settings
55+
# DojoSytemSettingsMiddleware.initialize_for_testing(System_Settings.objects.get())
56+
4457
# Warm up ContentType cache for relevant models. This is needed if we want to be able to run the test in isolation
4558
# As part of the test suite the ContentTYpe ids will already be cached and won't affect the query count.
4659
# But if we run the test in isolation, the ContentType ids will not be cached and will result in more queries.
@@ -106,6 +119,8 @@ def import_reimport_performance(self, expected_num_queries1, expected_num_async_
106119
"sync": True,
107120
"scan_type": STACK_HAWK_SCAN_TYPE,
108121
"engagement": engagement,
122+
"tags": ["performance-test", "tag-in-param", "go-faster"],
123+
"apply_tags_to_findings": True,
109124
}
110125
importer = DefaultImporter(**import_options)
111126
test, _, _len_new_findings, _len_closed_findings, _, _, _ = importer.process_scan(scan)
@@ -127,6 +142,8 @@ def import_reimport_performance(self, expected_num_queries1, expected_num_async_
127142
"verified": True,
128143
"sync": True,
129144
"scan_type": STACK_HAWK_SCAN_TYPE,
145+
"tags": ["performance-test-reimport", "reimport-tag-in-param", "reimport-go-faster"],
146+
"apply_tags_to_findings": True,
130147
}
131148
reimporter = DefaultReImporter(**reimport_options)
132149
test, _, _len_new_findings, _len_closed_findings, _, _, _ = reimporter.process_scan(scan)
@@ -154,11 +171,11 @@ def import_reimport_performance(self, expected_num_queries1, expected_num_async_
154171

155172
def test_import_reimport_reimport_performance(self):
156173
self.import_reimport_performance(
157-
expected_num_queries1=603,
174+
expected_num_queries1=712,
158175
expected_num_async_tasks1=15,
159-
expected_num_queries2=489,
176+
expected_num_queries2=656,
160177
expected_num_async_tasks2=23,
161-
expected_num_queries3=347,
178+
expected_num_queries3=332,
162179
expected_num_async_tasks3=20,
163180
)
164181

@@ -172,12 +189,12 @@ def test_import_reimport_reimport_performance_no_async(self, mock):
172189
so we patch the we_want_async decorator to always return False.
173190
"""
174191
self.import_reimport_performance(
175-
expected_num_queries1=673,
176-
expected_num_async_tasks1=25,
177-
expected_num_queries2=544,
178-
expected_num_async_tasks2=30,
179-
expected_num_queries3=387,
180-
expected_num_async_tasks3=25,
192+
expected_num_queries1=712,
193+
expected_num_async_tasks1=15,
194+
expected_num_queries2=656,
195+
expected_num_async_tasks2=23,
196+
expected_num_queries3=332,
197+
expected_num_async_tasks3=20,
181198
)
182199

183200
@patch("dojo.decorators.we_want_async", return_value=False)
@@ -190,11 +207,12 @@ def test_import_reimport_reimport_performance_no_async_with_product_grading(self
190207
so we patch the we_want_async decorator to always return False.
191208
"""
192209
self.system_settings(enable_product_grade=True)
210+
193211
self.import_reimport_performance(
194-
expected_num_queries1=673,
212+
expected_num_queries1=752,
195213
expected_num_async_tasks1=25,
196-
expected_num_queries2=544,
214+
expected_num_queries2=690,
197215
expected_num_async_tasks2=30,
198-
expected_num_queries3=387,
216+
expected_num_queries3=357,
199217
expected_num_async_tasks3=25,
200218
)

unittests/test_tags.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ class InheritedTagsTests(DojoAPITestCase):
292292
def setUp(self, *args, **kwargs):
293293
super().setUp()
294294
self.login_as_admin()
295-
self.system_settings(enable_product_tag_inehritance=True)
295+
self.system_settings(enable_product_tag_inheritance=True)
296296
self.product = self.create_product("Inherited Tags Test", tags=["inherit", "these", "tags"])
297297
self.scans_path = get_unit_tests_scans_path("zap")
298298
self.zap_sample5_filename = self.scans_path / "5_zap_sample_one.xml"

0 commit comments

Comments
 (0)