Skip to content

Commit 5dc55a3

Browse files
Locations V3: add import performance test and autocorrect counts (#14501)
* Locations V3: add import performance test * feat: run performance tests in separate CI job with auto-update - Tag performance test classes with @tag('performance') - Exclude performance tests from main unit test suite - Add keep-alive compose override for performance test CI - Add performance-tests.yml workflow that auto-updates counts and commits - Wire performance tests job into unit-tests.yml * fix: correct artifact name for docker image download * fix: remove manual migration step, update script handles it via --keepdb * fix: set explicit celery broker URL in unit test compose overrides Replace individual DD_CELERY_BROKER_* vars with a single explicit DD_CELERY_BROKER_URL in both unit test compose overrides. This makes the SQLite broker self-contained in the compose file rather than relying on the entrypoint script to unset DD_CELERY_BROKER_URL. Also update performance-tests.yml to reference docker-compose.override.unit_tests_cicd.yml explicitly instead of the docker-compose.override.yml symlink. * perf: run full test class at once in update script instead of per-method * fix: push to head branch by name in detached HEAD state * fix: fetch and rebase before and after performance tests to handle remote ahead * remove rebase * remove rebase * checkout correct ref * chore: update performance test counts [skip ci] * test with wrong counts * chore: update performance test counts [skip ci] * chore: update performance test counts [skip ci] * fix(ci): remove [skip ci] so checks remain visible after auto-update commit * fix(ci): pass head_ref to performance-tests, expand cancel workflow list - Add head_ref input to performance-tests (workflow_call context bug fix) - Pass head_ref from unit-tests so checkout/push use correct PR branch - Fix cancel workflow typo: k8s-testing.yml -> k8s-tests.yml - Add validate_docs_build, test-helm-chart, ruff, shellcheck to cancel list * test wrong counts * fix * chore: update performance test counts * fix(ci): re-trigger unit-tests after auto-update commit, guard against loop GITHUB_TOKEN pushes do not trigger new workflow runs, so after auto-updating performance test counts, dispatch unit-tests.yml manually to ensure the PR gets fresh CI checks on the updated commit. Add a loop guard: if the remote branch's last commit was already an auto-update and counts still differ, abort with an error instead of looping indefinitely. * text * reduce timeout * test incorrect count * chore: update performance test counts * fix(ci): use PAT for auto-update push to trigger pull_request:synchronize GITHUB_TOKEN pushes are silently ignored by GitHub's workflow trigger system, so CI checks never run on the auto-updated commit. Pushing via a PAT (secrets.GH_TOKEN) fires a pull_request:synchronize event which re-triggers unit-tests.yml naturally, without needing workflow_dispatch. * test wrong count * chore: update performance test counts * fix(ci): fail on wrong performance test counts instead of auto-committing Remove the auto-commit logic from the performance tests workflow. If counts are out of date, fail the job and print the diff along with the command to fix it locally. This avoids all the complexity around GITHUB_TOKEN not triggering CI on bot pushes. * fix counts --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 510ddd5 commit 5dc55a3

12 files changed

Lines changed: 479 additions & 156 deletions

.github/workflows/cancel-outdated-workflow-runs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ jobs:
1515
steps:
1616
- uses: styfle/cancel-workflow-action@3155a141048f8f89c06b4cdae32e7853e97536bc # 0.13.0
1717
with:
18-
workflow_id: 'integration-tests.yml,k8s-testing.yml,unit-tests.yml'
18+
workflow_id: 'integration-tests.yml,k8s-tests.yml,unit-tests.yml,validate_docs_build.yml,test-helm-chart.yml,ruff.yml,shellcheck.yml'
1919
access_token: ${{ github.token }}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Performance Tests
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
performance-tests:
8+
name: Performance Tests
9+
runs-on: ubuntu-latest
10+
needs: []
11+
permissions:
12+
contents: read
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
17+
18+
- name: Set-platform
19+
run: |
20+
echo "PLATFORM=linux-amd64" >> $GITHUB_ENV
21+
22+
- name: Load images from artifacts
23+
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
24+
with:
25+
path: built-docker-image
26+
pattern: built-docker-image-django-alpine-linux-amd64
27+
merge-multiple: true
28+
29+
- name: Load docker images
30+
timeout-minutes: 10
31+
run: |
32+
docker load -i built-docker-image/django-alpine-linux-amd64_img
33+
docker images
34+
35+
- name: Set unit-test mode
36+
run: docker/setEnv.sh unit_tests_cicd
37+
38+
- name: Start Postgres and webhook.endpoint
39+
run: docker compose up --no-deps -d postgres webhook.endpoint
40+
41+
- name: Start uwsgi (idle)
42+
timeout-minutes: 5
43+
run: |
44+
docker compose -f docker-compose.yml -f docker-compose.override.unit_tests_cicd.yml \
45+
-f docker/docker-compose.override.performance_tests_cicd.yml \
46+
up -d --no-deps uwsgi
47+
env:
48+
DJANGO_VERSION: alpine
49+
50+
- name: Run performance tests (auto-update counts)
51+
timeout-minutes: 15
52+
run: python3 scripts/update_performance_test_counts.py
53+
54+
- name: Check counts are up to date
55+
run: |
56+
if ! git diff --quiet unittests/test_importers_performance.py; then
57+
echo "Performance test counts are out of date. Fix them by running locally:"
58+
echo ""
59+
echo " python3 scripts/update_performance_test_counts.py"
60+
echo ""
61+
echo "Diff:"
62+
git diff unittests/test_importers_performance.py
63+
exit 1
64+
else
65+
echo "Performance test counts are up to date."
66+
fi
67+
68+
- name: Logs
69+
if: failure()
70+
run: docker compose logs --tail="2500" uwsgi
71+
72+
- name: Shutdown
73+
if: always()
74+
run: docker compose down

.github/workflows/unit-tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ jobs:
2121
with:
2222
platform: ${{ matrix.platform }}
2323

24+
test-performance:
25+
needs: build-docker-containers
26+
uses: ./.github/workflows/performance-tests.yml
27+
secrets: inherit
28+
2429
test-rest-framework:
2530
strategy:
2631
matrix:
@@ -45,3 +50,4 @@ jobs:
4550
needs: build-docker-containers
4651
uses: ./.github/workflows/k8s-tests.yml
4752
secrets: inherit
53+

docker-compose.override.unit_tests.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,7 @@ services:
2121
DD_DATABASE_PORT: ${DD_DATABASE_PORT:-5432}
2222
DD_DATABASE_USER: ${DD_DATABASE_USER:-defectdojo}
2323
DD_DATABASE_PASSWORD: ${DD_DATABASE_PASSWORD:-defectdojo}
24-
DD_CELERY_BROKER_SCHEME: 'sqla+sqlite'
25-
DD_CELERY_BROKER_USER: ''
26-
DD_CELERY_BROKER_PASSWORD: ''
27-
DD_CELERY_BROKER_HOST: ''
28-
DD_CELERY_BROKER_PORT: "-1"
29-
DD_CELERY_BROKER_PATH: '/dojo.celerydb.sqlite'
30-
DD_CELERY_BROKER_PARAMS: ''
24+
DD_CELERY_BROKER_URL: 'sqla+sqlite:///dojo.celerydb.sqlite'
3125
DD_JIRA_EXTRA_ISSUE_TYPES: 'Vulnerability' # Shouldn't trigger a migration error
3226
celerybeat: !reset
3327
celeryworker: !reset

docker-compose.override.unit_tests_cicd.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,7 @@ services:
2020
DD_DATABASE_ENGINE: ${DD_DATABASE_ENGINE:-django.db.backends.postgresql}
2121
DD_DATABASE_HOST: ${DD_DATABASE_HOST:-postgres}
2222
DD_DATABASE_PORT: ${DD_DATABASE_PORT:-5432}
23-
DD_CELERY_BROKER_SCHEME: 'sqla+sqlite'
24-
DD_CELERY_BROKER_USER: ''
25-
DD_CELERY_BROKER_PASSWORD: ''
26-
DD_CELERY_BROKER_HOST: ''
27-
DD_CELERY_BROKER_PORT: "-1"
28-
DD_CELERY_BROKER_PATH: '/dojo.celerydb.sqlite'
29-
DD_CELERY_BROKER_PARAMS: ''
23+
DD_CELERY_BROKER_URL: 'sqla+sqlite:///dojo.celerydb.sqlite'
3024
DD_JIRA_EXTRA_ISSUE_TYPES: 'Vulnerability' # Shouldn't trigger a migration error
3125
DD_V3_FEATURE_LOCATIONS: ${DD_V3_FEATURE_LOCATIONS:-False}
3226
celerybeat: !reset
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
# uwsgi container with no running daemon, used to run performance tests via run-unittest.sh
3+
services:
4+
uwsgi:
5+
entrypoint: ["tail", "-f", "/dev/null"]
6+
command: []

docker/entrypoint-unit-tests-devDocker.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ echo "Unit Tests"
7373
echo "------------------------------------------------------------"
7474

7575
# Removing parallel and shuffle for now to maintain stability
76-
python3 manage.py test unittests -v 3 --keepdb --no-input --exclude-tag="non-parallel" || {
76+
python3 manage.py test unittests -v 3 --keepdb --no-input --exclude-tag="non-parallel" --exclude-tag="performance" || {
7777
exit 1;
7878
}
79-
python3 manage.py test unittests -v 3 --keepdb --no-input --tag="non-parallel" || {
79+
python3 manage.py test unittests -v 3 --keepdb --no-input --tag="non-parallel" --exclude-tag="performance" || {
8080
exit 1;
8181
}
8282

docker/entrypoint-unit-tests.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,16 @@ echo "Unit Tests"
8080
echo "------------------------------------------------------------"
8181

8282
# Removing parallel and shuffle for now to maintain stability
83-
python3 manage.py test unittests --keepdb --no-input --exclude-tag="non-parallel" --exclude-tag="transactional" || {
83+
python3 manage.py test unittests --keepdb --no-input --exclude-tag="non-parallel" --exclude-tag="transactional" --exclude-tag="performance" || {
8484
exit 1;
8585
}
86-
python3 manage.py test unittests --keepdb --no-input --tag="non-parallel" || {
86+
python3 manage.py test unittests --keepdb --no-input --tag="non-parallel" --exclude-tag="performance" || {
8787
exit 1;
8888
}
8989
# Running one unit tests that inherits from TransactionTestCase somehow changes the behaviour of how Django loads fixtures into the database.
9090
# Meaning any test after this one would fail to load our dojo_testdata.json fixture. In a way this makes sense as it contains some data integrity problems.
9191
# I tried to fix these in https://github.com/DefectDojo/django-DefectDojo/pull/13217.
9292
# For now here we run the only TranscationTestCase at the end to avoid the problem.
93-
python3 manage.py test unittests --keepdb --no-input --tag="transactional" || {
93+
python3 manage.py test unittests --keepdb --no-input --tag="transactional" --exclude-tag="performance" || {
9494
exit 1;
9595
}

dojo/base_models/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Meta:
4242

4343
abstract = True
4444

45-
def save(self, *args: list, skip_validation: bool = not settings.V3_FEATURE_LOCATIONS, **kwargs: dict) -> None:
45+
def save(self, *args: list, skip_validation: bool | None = None, **kwargs: dict) -> None:
4646
"""
4747
Override save method to call the `full_clean()` validation function each save.
4848
@@ -53,6 +53,9 @@ def save(self, *args: list, skip_validation: bool = not settings.V3_FEATURE_LOCA
5353
- Validate the field uniqueness - `validate_unique()`
5454
All three steps are performed when you call a model's full_clean() method in the order above
5555
"""
56+
# make sure this is evaluated at runtime, not at class definition time which would be the case if we used it in the parameter default value
57+
if skip_validation is None:
58+
skip_validation = not settings.V3_FEATURE_LOCATIONS
5659
# Run the pre save logic, if enabled
5760
self.pre_save_logic()
5861
# Call the validations

readme-docs/CONTRIBUTING.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ Please use [these test scripts](../tests) to test your changes. These are the sc
5555

5656
For changes that require additional settings, you can now use local_settings.py file. See the logging section below for more information.
5757

58+
## Updating Performance Test Query Counts
59+
60+
The importer performance tests in `unittests/test_importers_performance.py` assert on expected database query and async task counts. If your changes affect import behavior (e.g., adding queries or changing celery task usage), these counts may need to be updated.
61+
62+
Run the update script to refresh expected counts:
63+
64+
```bash
65+
python3 scripts/update_performance_test_counts.py
66+
```
67+
68+
The script runs both `TestDojoImporterPerformanceSmall` (v2 endpoints) and `TestDojoImporterPerformanceSmallLocations` (v3 locations), captures actual counts, and updates the test file when they differ from expectations.
69+
70+
To verify all tests pass after updating:
71+
72+
```bash
73+
python3 scripts/update_performance_test_counts.py --verify
74+
```
75+
5876
## Python3 Version
5977
For compatibility reasons, the code in dev branch should be python3.13 compliant.
6078

0 commit comments

Comments
 (0)