From 21819ebc78cd4a1110b7cf3fa07d89ab10dae235 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 8 Aug 2026 20:27:50 +1000 Subject: [PATCH] Fix flaky test on sqlite (#12582) --- .../plugin/samples/integration/test_scheduled_task.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py b/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py index bacedb1c99..3252931771 100644 --- a/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py +++ b/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py @@ -5,7 +5,7 @@ from unittest import mock from django.db import connection from django.db.models.query import QuerySet -from django.test import TestCase, TransactionTestCase +from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature from plugin import InvenTreePlugin from plugin.helpers import MixinImplementationError @@ -97,6 +97,7 @@ class ExampleScheduledTaskPluginTests(TestCase): call_plugin_function('does_not_exist', 'member_func'), None +@skipUnlessDBFeature('has_select_for_update') class ScheduleMixinConcurrencyTest(TransactionTestCase): """Genuine cross-transaction regression test for ScheduleMixin.register_tasks(). @@ -108,6 +109,14 @@ class ScheduleMixinConcurrencyTest(TransactionTestCase): register_tasks() now locks the plugin's PluginConfig row (select_for_update) for the duration of task registration, so only one of two concurrent calls may proceed through the check-then-write at a time. + + This relies on genuine database-level row locking, which SQLite does not + provide - select_for_update() is silently a no-op there (Django's sqlite + backend reports has_select_for_update=False), so both threads can race + through the check-then-write and a "database is locked" error from one + thread can roll back an otherwise-successful transaction, making the test + flaky under sqlite for reasons unrelated to the code under test. Only run + it against backends where the lock is real (e.g. postgres, mysql). """ def test_concurrent_register_tasks_does_not_duplicate(self):