mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1700 from SchrodingersGat/api-default-values
Unit testing for default values
This commit is contained in:
commit
9e720ea620
@ -18,6 +18,7 @@ class InvenTreeAPITestCase(APITestCase):
|
|||||||
email = 'test@testing.com'
|
email = 'test@testing.com'
|
||||||
|
|
||||||
superuser = False
|
superuser = False
|
||||||
|
is_staff = True
|
||||||
auto_login = True
|
auto_login = True
|
||||||
|
|
||||||
# Set list of roles automatically associated with the user
|
# Set list of roles automatically associated with the user
|
||||||
@ -40,7 +41,11 @@ class InvenTreeAPITestCase(APITestCase):
|
|||||||
|
|
||||||
if self.superuser:
|
if self.superuser:
|
||||||
self.user.is_superuser = True
|
self.user.is_superuser = True
|
||||||
self.user.save()
|
|
||||||
|
if self.is_staff:
|
||||||
|
self.user.is_staff = True
|
||||||
|
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
for role in self.roles:
|
for role in self.roles:
|
||||||
self.assignRole(role)
|
self.assignRole(role)
|
||||||
|
@ -2,17 +2,19 @@
|
|||||||
Serializers used in various InvenTree apps
|
Serializers used in various InvenTree apps
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from rest_framework.utils import model_meta
|
||||||
from rest_framework.fields import empty
|
from rest_framework.fields import empty
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
@ -42,6 +44,72 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
|||||||
but also ensures that the underlying model class data are checked on validation.
|
but also ensures that the underlying model class data are checked on validation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, instance=None, data=empty, **kwargs):
|
||||||
|
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
# If instance is None, we are creating a new instance
|
||||||
|
if instance is None:
|
||||||
|
|
||||||
|
if data is empty:
|
||||||
|
data = OrderedDict()
|
||||||
|
else:
|
||||||
|
# Required to side-step immutability of a QueryDict
|
||||||
|
data = data.copy()
|
||||||
|
|
||||||
|
# Add missing fields which have default values
|
||||||
|
ModelClass = self.Meta.model
|
||||||
|
|
||||||
|
fields = model_meta.get_field_info(ModelClass)
|
||||||
|
|
||||||
|
for field_name, field in fields.fields.items():
|
||||||
|
|
||||||
|
if field.has_default() and field_name not in data:
|
||||||
|
|
||||||
|
value = field.default
|
||||||
|
|
||||||
|
# Account for callable functions
|
||||||
|
if callable(value):
|
||||||
|
try:
|
||||||
|
value = value()
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data[field_name] = value
|
||||||
|
|
||||||
|
super().__init__(instance, data, **kwargs)
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
"""
|
||||||
|
Construct initial data for the serializer.
|
||||||
|
Use the 'default' values specified by the django model definition
|
||||||
|
"""
|
||||||
|
|
||||||
|
initials = super().get_initial()
|
||||||
|
|
||||||
|
# Are we creating a new instance?
|
||||||
|
if self.instance is None:
|
||||||
|
ModelClass = self.Meta.model
|
||||||
|
|
||||||
|
fields = model_meta.get_field_info(ModelClass)
|
||||||
|
|
||||||
|
for field_name, field in fields.fields.items():
|
||||||
|
|
||||||
|
if field.has_default() and field_name not in initials:
|
||||||
|
|
||||||
|
value = field.default
|
||||||
|
|
||||||
|
# Account for callable functions
|
||||||
|
if callable(value):
|
||||||
|
try:
|
||||||
|
value = value()
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
initials[field_name] = value
|
||||||
|
|
||||||
|
return initials
|
||||||
|
|
||||||
def run_validation(self, data=empty):
|
def run_validation(self, data=empty):
|
||||||
""" Perform serializer validation.
|
""" Perform serializer validation.
|
||||||
In addition to running validators on the serializer fields,
|
In addition to running validators on the serializer fields,
|
||||||
|
@ -13,6 +13,7 @@ from InvenTree.status_codes import StockStatus
|
|||||||
from part.models import Part, PartCategory
|
from part.models import Part, PartCategory
|
||||||
from stock.models import StockItem
|
from stock.models import StockItem
|
||||||
from company.models import Company
|
from company.models import Company
|
||||||
|
from common.models import InvenTreeSetting
|
||||||
|
|
||||||
|
|
||||||
class PartAPITest(InvenTreeAPITestCase):
|
class PartAPITest(InvenTreeAPITestCase):
|
||||||
@ -311,6 +312,59 @@ class PartAPITest(InvenTreeAPITestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(data['results']), n)
|
self.assertEqual(len(data['results']), n)
|
||||||
|
|
||||||
|
def test_default_values(self):
|
||||||
|
"""
|
||||||
|
Tests for 'default' values:
|
||||||
|
|
||||||
|
Ensure that unspecified fields revert to "default" values
|
||||||
|
(as specified in the model field definition)
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = reverse('api-part-list')
|
||||||
|
|
||||||
|
response = self.client.post(url, {
|
||||||
|
'name': 'all defaults',
|
||||||
|
'description': 'my test part',
|
||||||
|
'category': 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
data = response.data
|
||||||
|
|
||||||
|
# Check that the un-specified fields have used correct default values
|
||||||
|
self.assertTrue(data['active'])
|
||||||
|
self.assertFalse(data['virtual'])
|
||||||
|
|
||||||
|
# By default, parts are not purchaseable
|
||||||
|
self.assertFalse(data['purchaseable'])
|
||||||
|
|
||||||
|
# Set the default 'purchaseable' status to True
|
||||||
|
InvenTreeSetting.set_setting(
|
||||||
|
'PART_PURCHASEABLE',
|
||||||
|
True,
|
||||||
|
self.user
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.post(url, {
|
||||||
|
'name': 'all defaults',
|
||||||
|
'description': 'my test part 2',
|
||||||
|
'category': 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Part should now be purchaseable by default
|
||||||
|
self.assertTrue(response.data['purchaseable'])
|
||||||
|
|
||||||
|
# "default" values should not be used if the value is specified
|
||||||
|
response = self.client.post(url, {
|
||||||
|
'name': 'all defaults',
|
||||||
|
'description': 'my test part 2',
|
||||||
|
'category': 1,
|
||||||
|
'active': False,
|
||||||
|
'purchaseable': False,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertFalse(response.data['active'])
|
||||||
|
self.assertFalse(response.data['purchaseable'])
|
||||||
|
|
||||||
|
|
||||||
class PartDetailTests(InvenTreeAPITestCase):
|
class PartDetailTests(InvenTreeAPITestCase):
|
||||||
"""
|
"""
|
||||||
@ -392,6 +446,13 @@ class PartDetailTests(InvenTreeAPITestCase):
|
|||||||
# Try to remove the part
|
# Try to remove the part
|
||||||
response = self.client.delete(url)
|
response = self.client.delete(url)
|
||||||
|
|
||||||
|
# As the part is 'active' we cannot delete it
|
||||||
|
self.assertEqual(response.status_code, 405)
|
||||||
|
|
||||||
|
# So, let's make it not active
|
||||||
|
response = self.patch(url, {'active': False}, expected_code=200)
|
||||||
|
|
||||||
|
response = self.client.delete(url)
|
||||||
self.assertEqual(response.status_code, 204)
|
self.assertEqual(response.status_code, 204)
|
||||||
|
|
||||||
# Part count should have reduced
|
# Part count should have reduced
|
||||||
@ -542,8 +603,6 @@ class PartDetailTests(InvenTreeAPITestCase):
|
|||||||
# And now check that the image has been set
|
# And now check that the image has been set
|
||||||
p = Part.objects.get(pk=pk)
|
p = Part.objects.get(pk=pk)
|
||||||
|
|
||||||
print("Image:", p.image.file)
|
|
||||||
|
|
||||||
|
|
||||||
class PartAPIAggregationTest(InvenTreeAPITestCase):
|
class PartAPIAggregationTest(InvenTreeAPITestCase):
|
||||||
"""
|
"""
|
||||||
|
@ -354,15 +354,17 @@ class StockItemTest(StockAPITestCase):
|
|||||||
self.assertContains(response, 'does not exist', status_code=status.HTTP_400_BAD_REQUEST)
|
self.assertContains(response, 'does not exist', status_code=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# POST without quantity
|
# POST without quantity
|
||||||
response = self.client.post(
|
response = self.post(
|
||||||
self.list_url,
|
self.list_url,
|
||||||
data={
|
{
|
||||||
'part': 1,
|
'part': 1,
|
||||||
'location': 1,
|
'location': 1,
|
||||||
}
|
},
|
||||||
|
expected_code=201,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, 'This field is required', status_code=status.HTTP_400_BAD_REQUEST)
|
# Item should have been created with default quantity
|
||||||
|
self.assertEqual(response.data['quantity'], 1)
|
||||||
|
|
||||||
# POST with quantity and part and location
|
# POST with quantity and part and location
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
|
Loading…
Reference in New Issue
Block a user