Add tests for report API (#6214)

* Add tests for report api

* Add test for delete endpoint

* Use pk in reverse call

* Allow kwargs in APITestCase methods

* Data is in response.data

* Fix comment

* Use expected_code argument instead of separate check

* Test with superuser

* Negative indexing not allowed, try with this alternative

* Looking for what went wrong..

* APITestCase does not work like Python requests - change POST calls

* Upload success is code 201

* In PATCH method, change test file and description as well

* Add test for get_api_url method

* Change format parameter for file upload

* Copy tests for report API over to label API

* Add test model for BuildLineLabel

* Update tests

* Make example template valid

* Add methods for testing label printing
This commit is contained in:
miggland 2024-01-16 20:55:55 +01:00 committed by GitHub
parent 21ff17332d
commit 5c7d3af150
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 411 additions and 35 deletions

View File

@ -287,60 +287,60 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase):
return actions return actions
def get(self, url, data=None, expected_code=200, format='json'): def get(self, url, data=None, expected_code=200, format='json', **kwargs):
"""Issue a GET request.""" """Issue a GET request."""
# Set default - see B006 # Set default - see B006
if data is None: if data is None:
data = {} data = {}
response = self.client.get(url, data, format=format) response = self.client.get(url, data, format=format, **kwargs)
self.checkResponse(url, 'GET', expected_code, response) self.checkResponse(url, 'GET', expected_code, response)
return response return response
def post(self, url, data=None, expected_code=None, format='json'): def post(self, url, data=None, expected_code=None, format='json', **kwargs):
"""Issue a POST request.""" """Issue a POST request."""
# Set default value - see B006 # Set default value - see B006
if data is None: if data is None:
data = {} data = {}
response = self.client.post(url, data=data, format=format) response = self.client.post(url, data=data, format=format, **kwargs)
self.checkResponse(url, 'POST', expected_code, response) self.checkResponse(url, 'POST', expected_code, response)
return response return response
def delete(self, url, data=None, expected_code=None, format='json'): def delete(self, url, data=None, expected_code=None, format='json', **kwargs):
"""Issue a DELETE request.""" """Issue a DELETE request."""
if data is None: if data is None:
data = {} data = {}
response = self.client.delete(url, data=data, format=format) response = self.client.delete(url, data=data, format=format, **kwargs)
self.checkResponse(url, 'DELETE', expected_code, response) self.checkResponse(url, 'DELETE', expected_code, response)
return response return response
def patch(self, url, data, expected_code=None, format='json'): def patch(self, url, data, expected_code=None, format='json', **kwargs):
"""Issue a PATCH request.""" """Issue a PATCH request."""
response = self.client.patch(url, data=data, format=format) response = self.client.patch(url, data=data, format=format, **kwargs)
self.checkResponse(url, 'PATCH', expected_code, response) self.checkResponse(url, 'PATCH', expected_code, response)
return response return response
def put(self, url, data, expected_code=None, format='json'): def put(self, url, data, expected_code=None, format='json', **kwargs):
"""Issue a PUT request.""" """Issue a PUT request."""
response = self.client.put(url, data=data, format=format) response = self.client.put(url, data=data, format=format, **kwargs)
self.checkResponse(url, 'PUT', expected_code, response) self.checkResponse(url, 'PUT', expected_code, response)
return response return response
def options(self, url, expected_code=None): def options(self, url, expected_code=None, **kwargs):
"""Issue an OPTIONS request.""" """Issue an OPTIONS request."""
response = self.client.options(url, format='json') response = self.client.options(url, format='json', **kwargs)
self.checkResponse(url, 'OPTIONS', expected_code, response) self.checkResponse(url, 'OPTIONS', expected_code, response)

View File

@ -1,43 +1,306 @@
"""Unit tests for label API.""" """Unit tests for label API."""
import json
from io import StringIO
from django.core.cache import cache
from django.urls import reverse from django.urls import reverse
import label.models as label_models
from build.models import BuildLine
from InvenTree.unit_test import InvenTreeAPITestCase from InvenTree.unit_test import InvenTreeAPITestCase
from part.models import Part
from stock.models import StockItem, StockLocation
class TestReportTests(InvenTreeAPITestCase): class LabelTest(InvenTreeAPITestCase):
"""Tests for the StockItem TestReport templates.""" """Base class for unit testing label model API endpoints."""
fixtures = ['category', 'part', 'location', 'stock'] fixtures = ['category', 'part', 'location', 'stock', 'bom', 'build']
roles = ['stock.view', 'stock_location.view'] superuser = True
list_url = reverse('api-stockitem-testreport-list') model = None
list_url = None
detail_url = None
metadata_url = None
def do_list(self, filters=None): print_url = None
"""Helper function to request list of labels with provided filters.""" print_itemname = None
# Set default - see B006 print_itemmodel = None
if filters is None:
filters = {}
response = self.client.get(self.list_url, filters, format='json') def setUp(self):
"""Ensure cache is cleared as part of test setup."""
cache.clear()
return super().setUp()
def test_api_url(self):
"""Test returned API Url against URL tag defined in this file."""
if not self.list_url:
return
self.assertEqual(reverse(self.list_url), self.model.get_api_url())
def test_list_endpoint(self):
"""Test that the LIST endpoint works for each model."""
if not self.list_url:
return
url = reverse(self.list_url)
response = self.get(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
return response.data labels = self.model.objects.all()
n = len(labels)
def test_list(self): # API endpoint must return correct number of reports
"""Test the API list endpoint.""" self.assertEqual(len(response.data), n)
response = self.do_list()
# TODO - Add some report templates to the fixtures # Filter by "enabled" status
self.assertEqual(len(response), 0) response = self.get(url, {'enabled': True})
self.assertEqual(len(response.data), n)
# TODO - Add some tests to this response response = self.get(url, {'enabled': False})
response = self.do_list({'item': 10}) self.assertEqual(len(response.data), 0)
# TODO - Add some tests to this response # Disable each report
response = self.do_list({'item': 100000}) for label in labels:
label.enabled = False
label.save()
# TODO - Add some tests to this response # Filter by "enabled" status
response = self.do_list({'items': [10, 11, 12]}) response = self.get(url, {'enabled': True})
self.assertEqual(len(response.data), 0)
response = self.get(url, {'enabled': False})
self.assertEqual(len(response.data), n)
def test_create_endpoint(self):
"""Test that creating a new report works for each label."""
if not self.list_url:
return
url = reverse(self.list_url)
# Create a new label
# Django REST API "APITestCase" does not work like requests - to send a file without it existing on disk,
# create it as a StringIO object, and upload it under parameter template
filestr = StringIO(
'{% extends "label/label_base.html" %}{% block content %}<pre>TEST LABEL</pre>{% endblock content %}'
)
filestr.name = 'ExampleTemplate.html'
response = self.post(
url,
data={
'name': 'New label',
'description': 'A fancy new label created through API test',
'label': filestr,
},
format=None,
expected_code=201,
)
# Make sure the expected keys are in the response
self.assertIn('pk', response.data)
self.assertIn('name', response.data)
self.assertIn('description', response.data)
self.assertIn('label', response.data)
self.assertIn('filters', response.data)
self.assertIn('enabled', response.data)
self.assertEqual(response.data['name'], 'New label')
self.assertEqual(
response.data['description'], 'A fancy new label created through API test'
)
self.assertEqual(response.data['label'].count('ExampleTemplate'), 1)
def test_detail_endpoint(self):
"""Test that the DETAIL endpoint works for each label."""
if not self.detail_url:
return
# Create an item first
self.test_create_endpoint()
labels = self.model.objects.all()
n = len(labels)
# Make sure at least one report defined
self.assertGreaterEqual(n, 1)
# Check detail page for first report
response = self.get(
reverse(self.detail_url, kwargs={'pk': labels[0].pk}), expected_code=200
)
# Make sure the expected keys are in the response
self.assertIn('pk', response.data)
self.assertIn('name', response.data)
self.assertIn('description', response.data)
self.assertIn('label', response.data)
self.assertIn('filters', response.data)
self.assertIn('enabled', response.data)
filestr = StringIO(
'{% extends "label/label_base.html" %}{% block content %}<pre>TEST LABEL</pre>{% endblock content %}'
)
filestr.name = 'ExampleTemplate_Updated.html'
# Check PATCH method
response = self.patch(
reverse(self.detail_url, kwargs={'pk': labels[0].pk}),
{
'name': 'Changed name during test',
'description': 'New version of the template',
'label': filestr,
},
format=None,
expected_code=200,
)
# Make sure the expected keys are in the response
self.assertIn('pk', response.data)
self.assertIn('name', response.data)
self.assertIn('description', response.data)
self.assertIn('label', response.data)
self.assertIn('filters', response.data)
self.assertIn('enabled', response.data)
self.assertEqual(response.data['name'], 'Changed name during test')
self.assertEqual(response.data['description'], 'New version of the template')
self.assertEqual(response.data['label'].count('ExampleTemplate_Updated'), 1)
def test_delete(self):
"""Test deleting, after other test are done."""
if not self.detail_url:
return
# Create an item first
self.test_create_endpoint()
labels = self.model.objects.all()
n = len(labels)
# Make sure at least one label defined
self.assertGreaterEqual(n, 1)
# Delete the last report
response = self.delete(
reverse(self.detail_url, kwargs={'pk': labels[n - 1].pk}), expected_code=204
)
def test_print_label(self):
"""Test printing a label."""
if not self.print_url:
return
# Create an item first
self.test_create_endpoint()
labels = self.model.objects.all()
n = len(labels)
# Make sure at least one label defined
self.assertGreaterEqual(n, 1)
url = reverse(self.print_url, kwargs={'pk': labels[0].pk})
# Try to print without providing a valid item
response = self.get(url, expected_code=400)
# Try to print with an invalid item
response = self.get(url, {self.print_itemname: 9999}, expected_code=400)
# Now print with a valid item
print(f'{self.print_itemmodel = }')
print(f'{self.print_itemmodel.objects.all() = }')
item = self.print_itemmodel.objects.first()
self.assertIsNotNone(item)
response = self.get(url, {self.print_itemname: item.pk}, expected_code=200)
response_json = json.loads(response.content.decode('utf-8'))
self.assertIn('file', response_json)
self.assertIn('success', response_json)
self.assertIn('message', response_json)
self.assertTrue(response_json['success'])
def test_metadata_endpoint(self):
"""Unit tests for the metadata field."""
if not self.metadata_url:
return
# Create an item first
self.test_create_endpoint()
labels = self.model.objects.all()
n = len(labels)
# Make sure at least one label defined
self.assertGreaterEqual(n, 1)
# Test getting metadata
response = self.get(
reverse(self.metadata_url, kwargs={'pk': labels[0].pk}), expected_code=200
)
self.assertEqual(response.data, {'metadata': {}})
class TestStockItemLabel(LabelTest):
"""Unit testing class for the StockItemLabel model."""
model = label_models.StockItemLabel
list_url = 'api-stockitem-label-list'
detail_url = 'api-stockitem-label-detail'
metadata_url = 'api-stockitem-label-metadata'
print_url = 'api-stockitem-label-print'
print_itemname = 'item'
print_itemmodel = StockItem
class TestStockLocationLabel(LabelTest):
"""Unit testing class for the StockLocationLabel model."""
model = label_models.StockLocationLabel
list_url = 'api-stocklocation-label-list'
detail_url = 'api-stocklocation-label-detail'
metadata_url = 'api-stocklocation-label-metadata'
print_url = 'api-stocklocation-label-print'
print_itemname = 'location'
print_itemmodel = StockLocation
class TestPartLabel(LabelTest):
"""Unit testing class for the PartLabel model."""
model = label_models.PartLabel
list_url = 'api-part-label-list'
detail_url = 'api-part-label-detail'
metadata_url = 'api-part-label-metadata'
print_url = 'api-part-label-print'
print_itemname = 'part'
print_itemmodel = Part
class TestBuildLineLabel(LabelTest):
"""Unit testing class for the BuildLine model."""
model = label_models.BuildLineLabel
list_url = 'api-buildline-label-list'
detail_url = 'api-buildline-label-detail'
metadata_url = 'api-buildline-label-metadata'
print_url = 'api-buildline-label-print'
print_itemname = 'line'
print_itemmodel = BuildLine

View File

@ -2,6 +2,7 @@
import os import os
import shutil import shutil
from io import StringIO
from pathlib import Path from pathlib import Path
from django.conf import settings from django.conf import settings
@ -200,6 +201,8 @@ class ReportTest(InvenTreeAPITestCase):
'build', 'build',
] ]
superuser = True
model = None model = None
list_url = None list_url = None
detail_url = None detail_url = None
@ -238,6 +241,13 @@ class ReportTest(InvenTreeAPITestCase):
enabled=True, enabled=True,
) )
def test_api_url(self):
"""Test returned API Url against URL tag defined in this file."""
if not self.list_url:
return
self.assertEqual(reverse(self.list_url), self.model.get_api_url())
def test_list_endpoint(self): def test_list_endpoint(self):
"""Test that the LIST endpoint works for each report.""" """Test that the LIST endpoint works for each report."""
if not self.list_url: if not self.list_url:
@ -273,6 +283,109 @@ class ReportTest(InvenTreeAPITestCase):
response = self.get(url, {'enabled': False}) response = self.get(url, {'enabled': False})
self.assertEqual(len(response.data), n) self.assertEqual(len(response.data), n)
def test_create_endpoint(self):
"""Test that creating a new report works for each report."""
if not self.list_url:
return
url = reverse(self.list_url)
# Create a new report
# Django REST API "APITestCase" does not work like requests - to send a file without it existing on disk,
# create it as a StringIO object, and upload it under parameter template
filestr = StringIO(
'{% extends "label/report_base.html" %}{% block content %}<pre>TEST REPORT</pre>{% endblock content %}'
)
filestr.name = 'ExampleTemplate.html'
response = self.post(
url,
data={
'name': 'New report',
'description': 'A fancy new report created through API test',
'template': filestr,
},
format=None,
expected_code=201,
)
# Make sure the expected keys are in the response
self.assertIn('pk', response.data)
self.assertIn('name', response.data)
self.assertIn('description', response.data)
self.assertIn('template', response.data)
self.assertIn('filters', response.data)
self.assertIn('enabled', response.data)
self.assertEqual(response.data['name'], 'New report')
self.assertEqual(
response.data['description'], 'A fancy new report created through API test'
)
self.assertTrue(response.data['template'].endswith('ExampleTemplate.html'))
def test_detail_endpoint(self):
"""Test that the DETAIL endpoint works for each report."""
if not self.detail_url:
return
reports = self.model.objects.all()
n = len(reports)
# Make sure at least one report defined
self.assertGreaterEqual(n, 1)
# Check detail page for first report
response = self.get(
reverse(self.detail_url, kwargs={'pk': reports[0].pk}), expected_code=200
)
# Make sure the expected keys are in the response
self.assertIn('pk', response.data)
self.assertIn('name', response.data)
self.assertIn('description', response.data)
self.assertIn('template', response.data)
self.assertIn('filters', response.data)
self.assertIn('enabled', response.data)
filestr = StringIO(
'{% extends "label/report_base.html" %}{% block content %}<pre>TEST REPORT VERSION 2</pre>{% endblock content %}'
)
filestr.name = 'ExampleTemplate_Updated.html'
# Check PATCH method
response = self.patch(
reverse(self.detail_url, kwargs={'pk': reports[0].pk}),
{
'name': 'Changed name during test',
'description': 'New version of the template',
'template': filestr,
},
format=None,
expected_code=200,
)
# Make sure the expected keys are in the response
self.assertIn('pk', response.data)
self.assertIn('name', response.data)
self.assertIn('description', response.data)
self.assertIn('template', response.data)
self.assertIn('filters', response.data)
self.assertIn('enabled', response.data)
self.assertEqual(response.data['name'], 'Changed name during test')
self.assertEqual(response.data['description'], 'New version of the template')
self.assertTrue(
response.data['template'].endswith('ExampleTemplate_Updated.html')
)
# Delete the last report
response = self.delete(
reverse(self.detail_url, kwargs={'pk': reports[n - 1].pk}),
expected_code=204,
)
def test_metadata(self): def test_metadata(self):
"""Unit tests for the metadata field.""" """Unit tests for the metadata field."""
if self.model is not None: if self.model is not None: