mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-10 02:47:49 +00:00
[bug] Settings fix (#13156)
* Fix error when generating default settings - Prevent database error on conflicting rows - Can be caused by concurrent user requests * ignore conflicts for TipResult
This commit is contained in:
1 parent
84fa789caf
commit
4fb0226ecc
4 files changed
+54
-4
No files matched your search
@@ -292,6 +292,8 @@ class BaseInvenTreeSetting(UpdatedUserMixin, models.Model):
|
||||
if not key.startswith('_')
|
||||
],
|
||||
batch_size=250,
|
||||
# Concurrent requests may race to create the same keys
|
||||
ignore_conflicts=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
|
||||
@@ -866,6 +866,32 @@ class UserSettingsApiTest(InvenTreeAPITestCase):
|
||||
|
||||
self.get(url, expected_code=200)
|
||||
|
||||
def test_build_default_values_race(self):
|
||||
"""Test that building default values tolerates keys created concurrently."""
|
||||
key = 'DISPLAY_HISTORY_TAB'
|
||||
|
||||
InvenTreeUserSetting.objects.create(key=key, value='True', user=self.user)
|
||||
|
||||
# Simulate another request creating the key after the existing keys are checked
|
||||
mock_qs = mock.Mock()
|
||||
mock_qs.values_list.return_value = []
|
||||
|
||||
with (
|
||||
mock.patch.object(
|
||||
InvenTreeUserSetting.objects, 'filter', return_value=mock_qs
|
||||
),
|
||||
self.assertNoLogs(logger='inventree', level='ERROR'),
|
||||
):
|
||||
InvenTreeUserSetting.build_default_values(user=self.user)
|
||||
|
||||
settings = InvenTreeUserSetting.objects.filter(user=self.user)
|
||||
|
||||
self.assertEqual(settings.filter(key=key).count(), 1)
|
||||
self.assertEqual(
|
||||
set(settings.values_list('key', flat=True)),
|
||||
{k for k in InvenTreeUserSetting.SETTINGS if not k.startswith('_')},
|
||||
)
|
||||
|
||||
def test_user_setting_invalid(self):
|
||||
"""Test a user setting with an invalid key."""
|
||||
url = reverse('api-user-setting-detail', kwargs={'key': 'DONKEY'})
|
||||
|
||||
@@ -878,9 +878,11 @@ class TipResult(InvenTree.models.MetadataMixin):
|
||||
# bulk create
|
||||
missing_ids = set(known_ids) - existing_ids
|
||||
if missing_ids:
|
||||
cls.objects.bulk_create([
|
||||
cls(user=user, tip_id=tip_id) for tip_id in missing_ids
|
||||
])
|
||||
cls.objects.bulk_create(
|
||||
[cls(user=user, tip_id=tip_id) for tip_id in missing_ids],
|
||||
# Concurrent requests may race to create the same records
|
||||
ignore_conflicts=True,
|
||||
)
|
||||
|
||||
|
||||
# Validate groups
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Unit tests for the 'users' app."""
|
||||
|
||||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth.models import Group
|
||||
@@ -10,7 +11,7 @@ from django.urls import reverse
|
||||
from common.settings import set_global_setting
|
||||
from InvenTree.helpers_mfa import get_codes
|
||||
from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase, InvenTreeTestCase
|
||||
from users.models import ApiToken, Owner
|
||||
from users.models import ApiToken, Owner, TipResult
|
||||
from users.oauth2_scopes import _roles
|
||||
from users.ruleset import (
|
||||
RULESET_CHOICES,
|
||||
@@ -18,6 +19,7 @@ from users.ruleset import (
|
||||
get_ruleset_ignore,
|
||||
get_ruleset_models,
|
||||
)
|
||||
from web.tips import KNOWN_IDS
|
||||
|
||||
G_RULESETS = get_ruleset_models()
|
||||
G_RULESETS_IG = get_ruleset_ignore()
|
||||
@@ -697,6 +699,24 @@ class UserProfileTest(InvenTreeAPITestCase):
|
||||
profile.refresh_from_db()
|
||||
self.assertIsNone(profile.primary_group)
|
||||
|
||||
def test_tip_backfill_race(self):
|
||||
"""Test that backfilling tip results tolerates records created concurrently."""
|
||||
tip_id = KNOWN_IDS[0]
|
||||
|
||||
TipResult.objects.create(user=self.user, tip_id=tip_id)
|
||||
|
||||
# Simulate another request creating the record after the existing IDs are checked
|
||||
mock_qs = mock.Mock()
|
||||
mock_qs.values_list.return_value = []
|
||||
|
||||
with mock.patch.object(TipResult.objects, 'filter', return_value=mock_qs):
|
||||
TipResult.backfill(self.user)
|
||||
|
||||
results = TipResult.objects.filter(user=self.user)
|
||||
|
||||
self.assertEqual(results.filter(tip_id=tip_id).count(), 1)
|
||||
self.assertEqual(set(results.values_list('tip_id', flat=True)), set(KNOWN_IDS))
|
||||
|
||||
|
||||
class UserProfileMetadataPermissionTests(InvenTreeAPITestCase):
|
||||
"""Tests for the generic metadata endpoint against the UserProfile model."""
|
||||
|
||||
Reference in new issue
Block a user