Merge pull request #1700 from SchrodingersGat/api-default-values

Unit testing for default values
This commit is contained in:
Oliver 2021-06-27 09:01:01 +10:00 committed by GitHub
commit 9e720ea620
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 144 additions and 10 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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):
""" """

View File

@ -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(