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
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."""
# Set default - see B006
if data is None:
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)
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."""
# Set default value - see B006
if data is None:
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)
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."""
if data is None:
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)
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."""
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)
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."""
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)
return response
def options(self, url, expected_code=None):
def options(self, url, expected_code=None, **kwargs):
"""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)

View File

@ -1,43 +1,306 @@
"""Unit tests for label API."""
import json
from io import StringIO
from django.core.cache import cache
from django.urls import reverse
import label.models as label_models
from build.models import BuildLine
from InvenTree.unit_test import InvenTreeAPITestCase
from part.models import Part
from stock.models import StockItem, StockLocation
class TestReportTests(InvenTreeAPITestCase):
"""Tests for the StockItem TestReport templates."""
class LabelTest(InvenTreeAPITestCase):
"""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):
"""Helper function to request list of labels with provided filters."""
# Set default - see B006
if filters is None:
filters = {}
print_url = None
print_itemname = None
print_itemmodel = None
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)
return response.data
labels = self.model.objects.all()
n = len(labels)
def test_list(self):
"""Test the API list endpoint."""
response = self.do_list()
# API endpoint must return correct number of reports
self.assertEqual(len(response.data), n)
# TODO - Add some report templates to the fixtures
self.assertEqual(len(response), 0)
# Filter by "enabled" status
response = self.get(url, {'enabled': True})
self.assertEqual(len(response.data), n)
# TODO - Add some tests to this response
response = self.do_list({'item': 10})
response = self.get(url, {'enabled': False})
self.assertEqual(len(response.data), 0)
# TODO - Add some tests to this response
response = self.do_list({'item': 100000})
# Disable each report
for label in labels:
label.enabled = False
label.save()
# TODO - Add some tests to this response
response = self.do_list({'items': [10, 11, 12]})
# Filter by "enabled" status
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 shutil
from io import StringIO
from pathlib import Path
from django.conf import settings
@ -200,6 +201,8 @@ class ReportTest(InvenTreeAPITestCase):
'build',
]
superuser = True
model = None
list_url = None
detail_url = None
@ -238,6 +241,13 @@ class ReportTest(InvenTreeAPITestCase):
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):
"""Test that the LIST endpoint works for each report."""
if not self.list_url:
@ -273,6 +283,109 @@ class ReportTest(InvenTreeAPITestCase):
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 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):
"""Unit tests for the metadata field."""
if self.model is not None: