mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-14 21:22:20 +00:00
refactor(backend): reduce duplication in tests (#10579)
* refactor(backend): reduce duplication for tests * fix tests * fix text * adjust last test
This commit is contained in:
@@ -8,7 +8,7 @@ import re
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Callable, Optional, Union
|
||||
from unittest import mock
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
@@ -728,6 +728,63 @@ class InvenTreeAPITestCase(
|
||||
"""Assert that dictionary 'a' is a subset of dictionary 'b'."""
|
||||
self.assertEqual(b, b | a)
|
||||
|
||||
def run_output_test(
|
||||
self,
|
||||
url: str,
|
||||
test_cases: list[Union[tuple[str, str], str]],
|
||||
additional_params: Optional[dict] = None,
|
||||
assert_subset: bool = False,
|
||||
assert_fnc: Optional[Callable] = None,
|
||||
):
|
||||
"""Run a series of tests against the provided URL.
|
||||
|
||||
Arguments:
|
||||
url: The URL to test
|
||||
test_cases: A list of tuples of the form (parameter_name, response_field_name)
|
||||
additional_params: Additional request parameters to include in the request
|
||||
assert_subset: If True, make the assertion against the first item in the response rather than the entire response
|
||||
assert_fnc: If provided, call this function with the response data and make the assertion against the return value
|
||||
"""
|
||||
|
||||
def get_response(response):
|
||||
if assert_subset:
|
||||
return response.data[0]
|
||||
if assert_fnc:
|
||||
return assert_fnc(response)
|
||||
return response.data
|
||||
|
||||
for case in test_cases:
|
||||
if isinstance(case, str):
|
||||
param = case
|
||||
field = case
|
||||
else:
|
||||
param, field = case
|
||||
# Test with parameter set to 'true'
|
||||
response = self.get(
|
||||
url,
|
||||
{param: 'true', **(additional_params or {})},
|
||||
expected_code=200,
|
||||
msg=f'Testing {param}=true returns anything but 200',
|
||||
)
|
||||
self.assertIn(
|
||||
field,
|
||||
get_response(response),
|
||||
f"Field '{field}' should be present when {param}=true",
|
||||
)
|
||||
|
||||
# Test with parameter set to 'false'
|
||||
response = self.get(
|
||||
url,
|
||||
{param: 'false', **(additional_params or {})},
|
||||
expected_code=200,
|
||||
msg=f'Testing {param}=false returns anything but 200',
|
||||
)
|
||||
self.assertNotIn(
|
||||
field,
|
||||
get_response(response),
|
||||
f"Field '{field}' should NOT be present when {param}=false",
|
||||
)
|
||||
|
||||
|
||||
@override_settings(
|
||||
SITE_URL='http://testserver', CSRF_TRUSTED_ORIGINS=['http://testserver']
|
||||
|
@@ -1447,43 +1447,16 @@ class BuildLineTests(BuildAPITest):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test output options for the BuildLine endpoint."""
|
||||
url = reverse('api-build-line-detail', kwargs={'pk': 2})
|
||||
|
||||
# Test cases: (parameter_name, response_field_name)
|
||||
test_cases = [
|
||||
('bom_item_detail', 'bom_item_detail'),
|
||||
('assembly_detail', 'assembly_detail'),
|
||||
('part_detail', 'part_detail'),
|
||||
('build_detail', 'build_detail'),
|
||||
('allocations', 'allocations'),
|
||||
]
|
||||
|
||||
for param, field in test_cases:
|
||||
# Test with parameter set to 'true'
|
||||
response = self.get(
|
||||
url,
|
||||
{param: 'true'},
|
||||
expected_code=200,
|
||||
msg=f'Testing {param}=true returns anything but 200',
|
||||
)
|
||||
self.assertIn(
|
||||
field,
|
||||
response.data,
|
||||
f"Field '{field}' should be present when {param}=true",
|
||||
)
|
||||
|
||||
# Test with parameter set to 'false'
|
||||
response = self.get(
|
||||
url,
|
||||
{param: 'false'},
|
||||
expected_code=200,
|
||||
msg=f'Testing {param}=false returns anything but 200',
|
||||
)
|
||||
self.assertNotIn(
|
||||
field,
|
||||
response.data,
|
||||
f"Field '{field}' should NOT be present when {param}=false",
|
||||
)
|
||||
self.run_output_test(
|
||||
reverse('api-build-line-detail', kwargs={'pk': 2}),
|
||||
[
|
||||
'bom_item_detail',
|
||||
'assembly_detail',
|
||||
'part_detail',
|
||||
'build_detail',
|
||||
'allocations',
|
||||
],
|
||||
)
|
||||
|
||||
def test_filter_consumed(self):
|
||||
"""Filter for the 'consumed' status."""
|
||||
|
@@ -538,31 +538,11 @@ class ManufacturerTest(InvenTreeAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the output options for SupplierPart detail."""
|
||||
url = reverse('api-manufacturer-part-list')
|
||||
|
||||
# Test cases: (parameter_name, response_field_name)
|
||||
test_cases = [
|
||||
('part_detail', 'part_detail'),
|
||||
('manufacturer_detail', 'manufacturer_detail'),
|
||||
('pretty', 'pretty_name'),
|
||||
]
|
||||
|
||||
for param, field in test_cases:
|
||||
# Test with parameter set to 'true'
|
||||
response = self.get(url, {param: 'true', 'limit': 1}, expected_code=200)
|
||||
self.assertIn(
|
||||
field,
|
||||
response.data['results'][0],
|
||||
f"Field '{field}' should be present when {param}='true'",
|
||||
)
|
||||
|
||||
# Test with parameter set to 'false'
|
||||
response = self.get(url, {param: 'false', 'limit': 1}, expected_code=200)
|
||||
self.assertNotIn(
|
||||
field,
|
||||
response.data['results'][0],
|
||||
f"Field '{field}' should not be present when {param}='false'",
|
||||
)
|
||||
self.run_output_test(
|
||||
reverse('api-manufacturer-part-list'),
|
||||
['part_detail', 'manufacturer_detail', ('pretty', 'pretty_name')],
|
||||
assert_subset=True,
|
||||
)
|
||||
|
||||
|
||||
class SupplierPartTest(InvenTreeAPITestCase):
|
||||
@@ -603,32 +583,15 @@ class SupplierPartTest(InvenTreeAPITestCase):
|
||||
def test_output_options(self):
|
||||
"""Test the output options for SupplierPart detail."""
|
||||
sp = SupplierPart.objects.all().first()
|
||||
url = reverse('api-supplier-part-detail', kwargs={'pk': sp.pk})
|
||||
|
||||
# Test cases: (parameter_name, response_field_name)
|
||||
test_cases = [
|
||||
('part_detail', 'part_detail'),
|
||||
('supplier_detail', 'supplier_detail'),
|
||||
('manufacturer_detail', 'manufacturer_detail'),
|
||||
('pretty', 'pretty_name'),
|
||||
]
|
||||
|
||||
for param, field in test_cases:
|
||||
# Test with parameter set to 'true'
|
||||
response = self.get(url, {param: 'true'}, expected_code=200)
|
||||
self.assertIn(
|
||||
field,
|
||||
response.data,
|
||||
f"Field '{field}' should be present when {param}='true'",
|
||||
)
|
||||
|
||||
# Test with parameter set to 'false'
|
||||
response = self.get(url, {param: 'false'}, expected_code=200)
|
||||
self.assertNotIn(
|
||||
field,
|
||||
response.data,
|
||||
f"Field '{field}' should not be present when {param}='false'",
|
||||
)
|
||||
self.run_output_test(
|
||||
reverse('api-supplier-part-detail', kwargs={'pk': sp.pk}),
|
||||
[
|
||||
'part_detail',
|
||||
'supplier_detail',
|
||||
'manufacturer_detail',
|
||||
('pretty', 'pretty_name'),
|
||||
],
|
||||
)
|
||||
|
||||
def test_available(self):
|
||||
"""Tests for updating the 'available' field."""
|
||||
@@ -789,28 +752,12 @@ class SupplierPriceBreakAPITest(InvenTreeAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the output options for SupplierPart price break list."""
|
||||
url = reverse('api-part-supplier-price-list')
|
||||
test_cases = [
|
||||
('part_detail', 'part_detail'),
|
||||
('supplier_detail', 'supplier_detail'),
|
||||
]
|
||||
|
||||
for param, field in test_cases:
|
||||
# Test with parameter set to 'true'
|
||||
response = self.get(url, {param: 'true', 'limit': 1}, expected_code=200)
|
||||
self.assertIn(
|
||||
field,
|
||||
response.data['results'][0],
|
||||
f"Field '{field}' should be present when {param}='true'",
|
||||
)
|
||||
|
||||
# Test with parameter set to 'false'
|
||||
response = self.get(url, {param: 'false', 'limit': 1}, expected_code=200)
|
||||
self.assertNotIn(
|
||||
field,
|
||||
response.data['results'][0],
|
||||
f"Field '{field}' should not be present when {param}='false'",
|
||||
)
|
||||
self.run_output_test(
|
||||
reverse('api-part-supplier-price-list'),
|
||||
['part_detail', 'supplier_detail'],
|
||||
additional_params={'limit': 1},
|
||||
assert_fnc=lambda x: x.data['results'][0],
|
||||
)
|
||||
|
||||
def test_supplier_price_break_list(self):
|
||||
"""Test the SupplierPriceBreak API list functionality."""
|
||||
|
@@ -315,12 +315,9 @@ class PurchaseOrderTest(OrderTest):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the various output options for the PurchaseOrder detail endpoint."""
|
||||
url = reverse('api-po-detail', kwargs={'pk': 1})
|
||||
response = self.get(url, {'supplier_detail': 'true'}, expected_code=200)
|
||||
self.assertIn('supplier_detail', response.data)
|
||||
|
||||
response = self.get(url, {'supplier_detail': 'false'}, expected_code=200)
|
||||
self.assertNotIn('supplier_detail', response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-po-detail', kwargs={'pk': 1}), ['supplier_detail']
|
||||
)
|
||||
|
||||
def test_po_operations(self):
|
||||
"""Test that we can create / edit and delete a PurchaseOrder via the API."""
|
||||
@@ -863,17 +860,10 @@ class PurchaseOrderLineItemTest(OrderTest):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test PurchaseOrderLineItem output option endpoint."""
|
||||
url = reverse('api-po-line-detail', kwargs={'pk': 1})
|
||||
|
||||
response = self.get(url, {'part_detail': 'true'}, expected_code=200)
|
||||
self.assertIn('part_detail', response.data)
|
||||
response = self.get(url, {'part_detail': 'false'}, expected_code=200)
|
||||
self.assertNotIn('part_detail', response.data)
|
||||
|
||||
response = self.get(url, {'order_detail': 'true'}, expected_code=200)
|
||||
self.assertIn('order_detail', response.data)
|
||||
response = self.get(url, {'order_detail': 'false'}, expected_code=200)
|
||||
self.assertNotIn('order_detail', response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-po-line-detail', kwargs={'pk': 1}),
|
||||
['part_detail', 'order_detail'],
|
||||
)
|
||||
|
||||
|
||||
class PurchaseOrderDownloadTest(OrderTest):
|
||||
@@ -1774,11 +1764,9 @@ class SalesOrderTest(OrderTest):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the output options for the SalesOrder detail endpoint."""
|
||||
url = reverse('api-so-detail', kwargs={'pk': 1})
|
||||
response = self.get(url, {'customer_detail': True}, expected_code=200)
|
||||
self.assertIn('customer_detail', response.data)
|
||||
response = self.get(url, {'customer_detail': False}, expected_code=200)
|
||||
self.assertNotIn('customer_detail', response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-so-detail', kwargs={'pk': 1}), ['customer_detail']
|
||||
)
|
||||
|
||||
|
||||
class SalesOrderLineItemTest(OrderTest):
|
||||
@@ -1943,14 +1931,10 @@ class SalesOrderLineItemTest(OrderTest):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the various output options for the SalesOrderLineItem detail endpoint."""
|
||||
url = reverse('api-so-line-detail', kwargs={'pk': 1})
|
||||
|
||||
options = ['part_detail', 'order_detail', 'customer_detail']
|
||||
for option in options:
|
||||
response = self.get(url, {f'{option}': True}, expected_code=200)
|
||||
self.assertIn(option, response.data)
|
||||
response = self.get(url, {f'{option}': False}, expected_code=200)
|
||||
self.assertNotIn(option, response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-so-line-detail', kwargs={'pk': 1}),
|
||||
['part_detail', 'order_detail', 'customer_detail'],
|
||||
)
|
||||
|
||||
|
||||
class SalesOrderDownloadTest(OrderTest):
|
||||
@@ -2296,20 +2280,17 @@ class SalesOrderAllocateTest(OrderTest):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the various output options for the SalesOrderAllocation detail endpoint."""
|
||||
url = reverse('api-so-allocation-list')
|
||||
|
||||
options = [
|
||||
'part_detail',
|
||||
'item_detail',
|
||||
'order_detail',
|
||||
'location_detail',
|
||||
'customer_detail',
|
||||
]
|
||||
for option in options:
|
||||
response = self.get(url, {f'{option}': True}, expected_code=200)
|
||||
self.assertIn(option, response.data[0])
|
||||
response = self.get(url, {f'{option}': False}, expected_code=200)
|
||||
self.assertNotIn(option, response.data[0])
|
||||
self.run_output_test(
|
||||
reverse('api-so-allocation-list'),
|
||||
[
|
||||
'part_detail',
|
||||
'item_detail',
|
||||
'order_detail',
|
||||
'location_detail',
|
||||
'customer_detail',
|
||||
],
|
||||
assert_subset=True,
|
||||
)
|
||||
|
||||
|
||||
class ReturnOrderTests(InvenTreeAPITestCase):
|
||||
@@ -2679,14 +2660,9 @@ class ReturnOrderTests(InvenTreeAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the various output options for the ReturnOrder detail endpoint."""
|
||||
url = reverse('api-return-order-detail', kwargs={'pk': 1})
|
||||
|
||||
options = ['customer_detail']
|
||||
for option in options:
|
||||
response = self.get(url, {f'{option}': True}, expected_code=200)
|
||||
self.assertIn(option, response.data)
|
||||
response = self.get(url, {f'{option}': False}, expected_code=200)
|
||||
self.assertNotIn(option, response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-return-order-detail', kwargs={'pk': 1}), ['customer_detail']
|
||||
)
|
||||
|
||||
|
||||
class ReturnOrderLineItemTests(InvenTreeAPITestCase):
|
||||
@@ -2749,17 +2725,10 @@ class ReturnOrderLineItemTests(InvenTreeAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test output options for detail endpoint."""
|
||||
url = reverse('api-return-order-line-detail', kwargs={'pk': 1})
|
||||
options = ['part_detail', 'item_detail', 'order_detail']
|
||||
|
||||
for option in options:
|
||||
# Test with option enabled
|
||||
response = self.get(url, {option: True}, expected_code=200)
|
||||
self.assertIn(option, response.data)
|
||||
|
||||
# Test with option disabled
|
||||
response = self.get(url, {option: False}, expected_code=200)
|
||||
self.assertNotIn(option, response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-return-order-line-detail', kwargs={'pk': 1}),
|
||||
['part_detail', 'item_detail', 'order_detail'],
|
||||
)
|
||||
|
||||
def test_update(self):
|
||||
"""Test updating ReturnOrderLineItem."""
|
||||
|
@@ -2666,13 +2666,10 @@ class BomItemTest(InvenTreeAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test that various output options work as expected."""
|
||||
url = reverse('api-bom-item-detail', kwargs={'pk': 3})
|
||||
options = ['can_build', 'part_detail', 'sub_part_detail']
|
||||
for option in options:
|
||||
response = self.get(url, {f'{option}': True}, expected_code=200)
|
||||
self.assertIn(option, response.data)
|
||||
response = self.get(url, {f'{option}': False}, expected_code=200)
|
||||
self.assertNotIn(option, response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-bom-item-detail', kwargs={'pk': 3}),
|
||||
['can_build', 'part_detail', 'sub_part_detail'],
|
||||
)
|
||||
|
||||
def test_add_bom_item(self):
|
||||
"""Test that we can create a new BomItem via the API."""
|
||||
|
@@ -265,13 +265,9 @@ class StockLocationTest(StockAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test output options."""
|
||||
url = reverse('api-location-detail', kwargs={'pk': 1})
|
||||
|
||||
response = self.get(url, {'path_detail': 'true'}, expected_code=200)
|
||||
self.assertIn('path', response.data)
|
||||
|
||||
response = self.get(url, {'path_detail': 'false'}, expected_code=200)
|
||||
self.assertNotIn('path', response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-location-detail', kwargs={'pk': 1}), [('path_detail', 'path')]
|
||||
)
|
||||
|
||||
def test_stock_location_structural(self):
|
||||
"""Test the effectiveness of structural stock locations.
|
||||
@@ -1529,33 +1525,16 @@ class StockItemTest(StockAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test the output options for StockItemt detail."""
|
||||
url = reverse('api-stock-detail', kwargs={'pk': 1})
|
||||
|
||||
# Test cases: (parameter_name, response_field_name)
|
||||
test_cases = [
|
||||
('part_detail', 'part_detail'),
|
||||
('path_detail', 'location_path'),
|
||||
('supplier_part_detail', 'supplier_part_detail'),
|
||||
('location_detail', 'location_detail'),
|
||||
('tests', 'tests'),
|
||||
]
|
||||
|
||||
for param, field in test_cases:
|
||||
# Test with parameter set to 'true'
|
||||
response = self.get(url, {param: 'true'}, expected_code=200)
|
||||
self.assertIn(
|
||||
field,
|
||||
response.data,
|
||||
f"Field '{field}' should be present when {param}='true'",
|
||||
)
|
||||
|
||||
# Test with parameter set to 'false'
|
||||
response = self.get(url, {param: 'false'}, expected_code=200)
|
||||
self.assertNotIn(
|
||||
field,
|
||||
response.data,
|
||||
f"Field '{field}' should not be present when {param}='false'",
|
||||
)
|
||||
self.run_output_test(
|
||||
reverse('api-stock-detail', kwargs={'pk': 1}),
|
||||
[
|
||||
'part_detail',
|
||||
('path_detail', 'location_path'),
|
||||
'supplier_part_detail',
|
||||
'location_detail',
|
||||
'tests',
|
||||
],
|
||||
)
|
||||
|
||||
def test_install(self):
|
||||
"""Test that stock item can be installed into another item, via the API."""
|
||||
@@ -2223,19 +2202,10 @@ class StockTestResultTest(StockAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test output options for single item retrieval."""
|
||||
url = reverse('api-stock-test-result-detail', kwargs={'pk': 1})
|
||||
|
||||
response = self.get(url, {'user_detail': 'true'}, expected_code=200)
|
||||
self.assertIn('user_detail', response.data)
|
||||
|
||||
response = self.get(url, {'user_detail': 'false'}, expected_code=200)
|
||||
self.assertNotIn('user_detail', response.data)
|
||||
|
||||
response = self.get(url, {'template_detail': 'true'}, expected_code=200)
|
||||
self.assertIn('template_detail', response.data)
|
||||
|
||||
response = self.get(url, {'template_detail': 'false'}, expected_code=200)
|
||||
self.assertNotIn('template_detail', response.data)
|
||||
self.run_output_test(
|
||||
reverse('api-stock-test-result-detail', kwargs={'pk': 1}),
|
||||
['user_detail', 'template_detail'],
|
||||
)
|
||||
|
||||
|
||||
class StockTrackingTest(StockAPITestCase):
|
||||
@@ -2334,23 +2304,13 @@ class StockTrackingTest(StockAPITestCase):
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test output options."""
|
||||
url = self.get_url()
|
||||
response = self.client.get(
|
||||
url, {'item_detail': True, 'user_detail': True, 'limit': 2}
|
||||
self.run_output_test(
|
||||
self.get_url(),
|
||||
['item_detail', 'user_detail'],
|
||||
additional_params={'limit': 2},
|
||||
assert_fnc=lambda x: x.data['results'][0],
|
||||
)
|
||||
|
||||
for item in response.data['results']:
|
||||
self.assertIn('item_detail', item)
|
||||
self.assertIn('user_detail', item)
|
||||
|
||||
response = self.client.get(
|
||||
url, {'item_detail': False, 'user_detail': False, 'limit': 2}
|
||||
)
|
||||
|
||||
for item in response.data['results']:
|
||||
self.assertNotIn('item_detail', item)
|
||||
self.assertNotIn('user_detail', item)
|
||||
|
||||
|
||||
class StockAssignTest(StockAPITestCase):
|
||||
"""Unit tests for the stock assignment API endpoint, where stock items are manually assigned to a customer."""
|
||||
|
Reference in New Issue
Block a user