mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +00:00
Merge pull request #1127 from SchrodingersGat/settings-view-unit-test
Extra unit testing for settings forms / views
This commit is contained in:
commit
771efecaa2
@ -82,7 +82,7 @@ settings_urls = [
|
|||||||
url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
|
url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
|
||||||
url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
|
url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/edit/?', SettingEdit.as_view(), name='setting-edit'),
|
url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'),
|
||||||
|
|
||||||
# Catch any other urls
|
# Catch any other urls
|
||||||
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
|
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
|
||||||
|
@ -8,7 +8,8 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
|
from django.db.utils import IntegrityError, OperationalError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
import djmoney.settings
|
import djmoney.settings
|
||||||
@ -16,7 +17,6 @@ from djmoney.models.fields import MoneyField
|
|||||||
from djmoney.contrib.exchange.models import convert_money
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
from djmoney.contrib.exchange.exceptions import MissingRate
|
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||||
|
|
||||||
from django.db.utils import OperationalError
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
@ -230,7 +230,7 @@ class InvenTreeSetting(models.Model):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default_value(cls, key):
|
def get_setting_default(cls, key):
|
||||||
"""
|
"""
|
||||||
Return the default value for a particular setting.
|
Return the default value for a particular setting.
|
||||||
|
|
||||||
@ -281,20 +281,23 @@ class InvenTreeSetting(models.Model):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
setting = InvenTreeSetting.objects.filter(key__iexact=key).first()
|
setting = InvenTreeSetting.objects.filter(key__iexact=key).first()
|
||||||
except OperationalError:
|
|
||||||
# Settings table has not been created yet!
|
|
||||||
return None
|
|
||||||
except (ValueError, InvenTreeSetting.DoesNotExist):
|
except (ValueError, InvenTreeSetting.DoesNotExist):
|
||||||
|
setting = None
|
||||||
|
except (IntegrityError, OperationalError):
|
||||||
|
setting = None
|
||||||
|
|
||||||
|
# Setting does not exist! (Try to create it)
|
||||||
|
if not setting:
|
||||||
|
|
||||||
|
setting = InvenTreeSetting(key=key, value=InvenTreeSetting.get_setting_default(key))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Attempt Create the setting if it does not exist
|
# Wrap this statement in "atomic", so it can be rolled back if it fails
|
||||||
setting = InvenTreeSetting.create(
|
with transaction.atomic():
|
||||||
key=key,
|
setting.save()
|
||||||
value=InvenTreeSetting.get_default_value(key)
|
except (IntegrityError, OperationalError):
|
||||||
)
|
# It might be the case that the database isn't created yet
|
||||||
except OperationalError:
|
pass
|
||||||
# Settings table has not been created yet
|
|
||||||
setting = None
|
|
||||||
|
|
||||||
return setting
|
return setting
|
||||||
|
|
||||||
@ -322,7 +325,7 @@ class InvenTreeSetting(models.Model):
|
|||||||
|
|
||||||
# If no backup value is specified, atttempt to retrieve a "default" value
|
# If no backup value is specified, atttempt to retrieve a "default" value
|
||||||
if backup_value is None:
|
if backup_value is None:
|
||||||
backup_value = cls.get_default_value(key)
|
backup_value = cls.get_setting_default(key)
|
||||||
|
|
||||||
setting = InvenTreeSetting.get_setting_object(key)
|
setting = InvenTreeSetting.get_setting_object(key)
|
||||||
|
|
||||||
@ -380,7 +383,7 @@ class InvenTreeSetting(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def default_value(self):
|
def default_value(self):
|
||||||
return InvenTreeSetting.get_default_value(self.key)
|
return InvenTreeSetting.get_setting_default(self.key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
@ -403,6 +406,9 @@ class InvenTreeSetting(models.Model):
|
|||||||
if validator is not None:
|
if validator is not None:
|
||||||
self.run_validator(validator)
|
self.run_validator(validator)
|
||||||
|
|
||||||
|
if self.is_bool():
|
||||||
|
self.value = InvenTree.helpers.str2bool(self.value)
|
||||||
|
|
||||||
def run_validator(self, validator):
|
def run_validator(self, validator):
|
||||||
"""
|
"""
|
||||||
Run a validator against the 'value' field for this InvenTreeSetting object.
|
Run a validator against the 'value' field for this InvenTreeSetting object.
|
||||||
|
143
InvenTree/common/test_views.py
Normal file
143
InvenTree/common/test_views.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for the views associated with the 'common' app
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from common.models import InvenTreeSetting
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsViewTest(TestCase):
|
||||||
|
"""
|
||||||
|
Tests for the settings management views
|
||||||
|
"""
|
||||||
|
|
||||||
|
fixtures = [
|
||||||
|
'settings',
|
||||||
|
]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
# Create a user (required to access the views / forms)
|
||||||
|
self.user = get_user_model().objects.create_user(
|
||||||
|
username='username',
|
||||||
|
email='me@email.com',
|
||||||
|
password='password',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client.login(username='username', password='password')
|
||||||
|
|
||||||
|
def get_url(self, pk):
|
||||||
|
return reverse('setting-edit', args=(pk,))
|
||||||
|
|
||||||
|
def get_setting(self, title):
|
||||||
|
|
||||||
|
return InvenTreeSetting.get_setting_object(title)
|
||||||
|
|
||||||
|
def get(self, url, status=200):
|
||||||
|
|
||||||
|
response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status)
|
||||||
|
|
||||||
|
data = json.loads(response.content)
|
||||||
|
|
||||||
|
return response, data
|
||||||
|
|
||||||
|
def post(self, url, data, valid=None):
|
||||||
|
|
||||||
|
response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
|
|
||||||
|
json_data = json.loads(response.content)
|
||||||
|
|
||||||
|
# If a particular status code is required
|
||||||
|
if valid is not None:
|
||||||
|
if valid:
|
||||||
|
self.assertEqual(json_data['form_valid'], True)
|
||||||
|
else:
|
||||||
|
self.assertEqual(json_data['form_valid'], False)
|
||||||
|
|
||||||
|
form_errors = json.loads(json_data['form_errors'])
|
||||||
|
|
||||||
|
return json_data, form_errors
|
||||||
|
|
||||||
|
def test_instance_name(self):
|
||||||
|
"""
|
||||||
|
Test that we can get the settings view for particular setting objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start with something basic - load the settings view for INVENTREE_INSTANCE
|
||||||
|
setting = self.get_setting('INVENTREE_INSTANCE')
|
||||||
|
|
||||||
|
self.assertIsNotNone(setting)
|
||||||
|
self.assertEqual(setting.value, 'My very first InvenTree Instance')
|
||||||
|
|
||||||
|
url = self.get_url(setting.pk)
|
||||||
|
|
||||||
|
self.get(url)
|
||||||
|
|
||||||
|
new_name = 'A new instance name!'
|
||||||
|
|
||||||
|
# Change the instance name via the form
|
||||||
|
data, errors = self.post(url, {'value': new_name}, valid=True)
|
||||||
|
|
||||||
|
name = InvenTreeSetting.get_setting('INVENTREE_INSTANCE')
|
||||||
|
|
||||||
|
self.assertEqual(name, new_name)
|
||||||
|
|
||||||
|
def test_choices(self):
|
||||||
|
"""
|
||||||
|
Tests for a setting which has choices
|
||||||
|
"""
|
||||||
|
|
||||||
|
setting = InvenTreeSetting.get_setting_object('INVENTREE_DEFAULT_CURRENCY')
|
||||||
|
|
||||||
|
# Default value!
|
||||||
|
self.assertEqual(setting.value, 'USD')
|
||||||
|
|
||||||
|
url = self.get_url(setting.pk)
|
||||||
|
|
||||||
|
# Try posting an invalid currency option
|
||||||
|
data, errors = self.post(url, {'value': 'XPQaaa'}, valid=False)
|
||||||
|
|
||||||
|
self.assertIsNotNone(errors.get('value'), None)
|
||||||
|
|
||||||
|
# Try posting a valid currency option
|
||||||
|
data, errors = self.post(url, {'value': 'AUD'}, valid=True)
|
||||||
|
|
||||||
|
def test_binary_values(self):
|
||||||
|
"""
|
||||||
|
Test for binary value
|
||||||
|
"""
|
||||||
|
|
||||||
|
setting = InvenTreeSetting.get_setting_object('PART_COMPONENT')
|
||||||
|
|
||||||
|
self.assertTrue(setting.as_bool())
|
||||||
|
|
||||||
|
url = self.get_url(setting.pk)
|
||||||
|
|
||||||
|
setting.value = True
|
||||||
|
setting.save()
|
||||||
|
|
||||||
|
# Try posting some invalid values
|
||||||
|
# The value should be "cleaned" and stay the same
|
||||||
|
for value in ['', 'abc', 'cat', 'TRUETRUETRUE']:
|
||||||
|
self.post(url, {'value': value}, valid=True)
|
||||||
|
|
||||||
|
# Try posting some valid (True) values
|
||||||
|
for value in [True, 'True', '1', 'yes']:
|
||||||
|
self.post(url, {'value': value}, valid=True)
|
||||||
|
self.assertTrue(InvenTreeSetting.get_setting('PART_COMPONENT'))
|
||||||
|
|
||||||
|
# Try posting some valid (False) values
|
||||||
|
for value in [False, 'False']:
|
||||||
|
self.post(url, {'value': value}, valid=True)
|
||||||
|
self.assertFalse(InvenTreeSetting.get_setting('PART_COMPONENT'))
|
@ -70,7 +70,7 @@ class SettingsTest(TestCase):
|
|||||||
|
|
||||||
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
|
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
|
||||||
|
|
||||||
value = InvenTreeSetting.get_default_value(key)
|
value = InvenTreeSetting.get_setting_default(key)
|
||||||
|
|
||||||
InvenTreeSetting.set_setting(key, value, self.user)
|
InvenTreeSetting.set_setting(key, value, self.user)
|
||||||
|
|
||||||
|
@ -72,3 +72,32 @@ class SettingEdit(AjaxUpdateView):
|
|||||||
form.fields['value'].help_text = description
|
form.fields['value'].help_text = description
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
def validate(self, setting, form):
|
||||||
|
"""
|
||||||
|
Perform custom validation checks on the form data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = form.cleaned_data
|
||||||
|
|
||||||
|
value = data.get('value', None)
|
||||||
|
|
||||||
|
if setting.choices():
|
||||||
|
"""
|
||||||
|
If a set of choices are provided for a given setting,
|
||||||
|
the provided value must be one of those choices.
|
||||||
|
"""
|
||||||
|
|
||||||
|
choices = [choice[0] for choice in setting.choices()]
|
||||||
|
|
||||||
|
if value not in choices:
|
||||||
|
form.add_error('value', _('Supplied value is not allowed'))
|
||||||
|
|
||||||
|
if setting.is_bool():
|
||||||
|
"""
|
||||||
|
If a setting is defined as a boolean setting,
|
||||||
|
the provided value must look somewhat like a boolean value!
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not str2bool(value, test=True) and not str2bool(value, test=False):
|
||||||
|
form.add_error('value', _('Supplied value must be a boolean'))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user