mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-01 04:56:45 +00:00
Add more test coverage for backend (#8048)
* add test for asset redirect * fully cover spa_bundler * test token api * check without beeing authed * not possible to be reached - no cover * remove unneeded except * fully test group apis * move ignore * add tests for admin site * add full admin testing * use output as ref * ignore admin edge-case * test display name settings * refactor admin test * add more admin testing * fix tests assertation * fix assertations * add test for importer admin * remove old test for function that will not be re-added for now see https://github.com/inventree/InvenTree/pull/8018#discussion_r1734011050 * Add stock detail with wrong pk * add a few stock tests
This commit is contained in:
parent
a102e178d1
commit
79affbe609
@ -10,10 +10,11 @@ from pathlib import Path
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.db import connections
|
||||
from django.db import connections, models
|
||||
from django.http.response import StreamingHttpResponse
|
||||
from django.test import TestCase
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
from django.urls import reverse
|
||||
|
||||
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
|
||||
from rest_framework.test import APITestCase
|
||||
@ -486,3 +487,30 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase):
|
||||
def assertDictContainsSubset(self, a, b):
|
||||
"""Assert that dictionary 'a' is a subset of dictionary 'b'."""
|
||||
self.assertEqual(b, b | a)
|
||||
|
||||
|
||||
class AdminTestCase(InvenTreeAPITestCase):
|
||||
"""Tests for the admin interface integration."""
|
||||
|
||||
superuser = True
|
||||
|
||||
def helper(self, model: type[models.Model], model_kwargs=None):
|
||||
"""Test the admin URL."""
|
||||
if model_kwargs is None:
|
||||
model_kwargs = {}
|
||||
|
||||
# Add object
|
||||
obj = model.objects.create(**model_kwargs)
|
||||
app_app, app_mdl = model._meta.app_label, model._meta.model_name
|
||||
|
||||
# 'Test listing
|
||||
response = self.get(reverse(f'admin:{app_app}_{app_mdl}_changelist'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Test change view
|
||||
response = self.get(
|
||||
reverse(f'admin:{app_app}_{app_mdl}_change', kwargs={'object_id': obj.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
return obj
|
||||
|
@ -4,11 +4,27 @@ import os
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from importer.models import DataImportSession
|
||||
from InvenTree.unit_test import InvenTreeTestCase
|
||||
from importer.models import DataImportRow, DataImportSession
|
||||
from InvenTree.unit_test import AdminTestCase, InvenTreeTestCase
|
||||
|
||||
|
||||
class ImporterTest(InvenTreeTestCase):
|
||||
class ImporterMixin:
|
||||
"""Helpers for import tests."""
|
||||
|
||||
def helper_file(self):
|
||||
"""Return test data."""
|
||||
fn = os.path.join(os.path.dirname(__file__), 'test_data', 'companies.csv')
|
||||
|
||||
with open(fn, encoding='utf-8') as input_file:
|
||||
data = input_file.read()
|
||||
return data
|
||||
|
||||
def helper_content(self):
|
||||
"""Return content file."""
|
||||
return ContentFile(self.helper_file(), 'companies.csv')
|
||||
|
||||
|
||||
class ImporterTest(ImporterMixin, InvenTreeTestCase):
|
||||
"""Basic tests for file imports."""
|
||||
|
||||
def test_import_session(self):
|
||||
@ -17,13 +33,8 @@ class ImporterTest(InvenTreeTestCase):
|
||||
|
||||
n = Company.objects.count()
|
||||
|
||||
fn = os.path.join(os.path.dirname(__file__), 'test_data', 'companies.csv')
|
||||
|
||||
with open(fn, encoding='utf-8') as input_file:
|
||||
data = input_file.read()
|
||||
|
||||
session = DataImportSession.objects.create(
|
||||
data_file=ContentFile(data, 'companies.csv'), model_type='company'
|
||||
data_file=self.helper_content(), model_type='company'
|
||||
)
|
||||
|
||||
session.extract_columns()
|
||||
@ -61,3 +72,15 @@ class ImporterTest(InvenTreeTestCase):
|
||||
|
||||
def test_field_defaults(self):
|
||||
"""Test default field values."""
|
||||
|
||||
|
||||
class AdminTest(ImporterMixin, AdminTestCase):
|
||||
"""Tests for the admin interface integration."""
|
||||
|
||||
def test_admin(self):
|
||||
"""Test the admin URL."""
|
||||
session = self.helper(
|
||||
model=DataImportSession,
|
||||
model_kwargs={'data_file': self.helper_content(), 'model_type': 'company'},
|
||||
)
|
||||
self.helper(model=DataImportRow, model_kwargs={'session_id': session.id})
|
||||
|
@ -9,7 +9,7 @@ from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from InvenTree.unit_test import InvenTreeAPITestCase
|
||||
from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase
|
||||
from machine.machine_type import BaseDriver, BaseMachineType, MachineStatus
|
||||
from machine.machine_types.label_printer import LabelPrinterBaseDriver
|
||||
from machine.models import MachineConfig
|
||||
@ -309,3 +309,11 @@ class TestLabelPrinterMachineType(TestMachineRegistryMixin, InvenTreeAPITestCase
|
||||
},
|
||||
expected_code=400,
|
||||
)
|
||||
|
||||
|
||||
class AdminTest(AdminTestCase):
|
||||
"""Tests for the admin interface integration."""
|
||||
|
||||
def test_admin(self):
|
||||
"""Test the admin URL."""
|
||||
self.helper(model=MachineConfig)
|
||||
|
@ -16,7 +16,7 @@ from PIL import Image
|
||||
import report.models as report_models
|
||||
from build.models import Build
|
||||
from common.models import Attachment, InvenTreeSetting
|
||||
from InvenTree.unit_test import InvenTreeAPITestCase
|
||||
from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase
|
||||
from order.models import ReturnOrder, SalesOrder
|
||||
from plugin.registry import registry
|
||||
from report.models import LabelTemplate, ReportTemplate
|
||||
@ -580,24 +580,6 @@ class TestReportTest(PrintTestMixins, ReportTest):
|
||||
Attachment.objects.filter(model_id=item.pk, model_type='stockitem').exists()
|
||||
)
|
||||
|
||||
return
|
||||
# TODO @matmair - Re-add this test after https://github.com/inventree/InvenTree/pull/7074/files#r1600694356 is resolved
|
||||
# Change the setting, now the test report should be attached automatically
|
||||
InvenTreeSetting.set_setting('REPORT_ATTACH_TEST_REPORT', True, None)
|
||||
|
||||
response = self.post(
|
||||
url, {'template': template.pk, 'items': [item.pk]}, expected_code=201
|
||||
)
|
||||
|
||||
# There should be a link to the generated PDF
|
||||
self.assertEqual(response.data['output'].startswith('/media/report/'), True)
|
||||
|
||||
# Check that a report has been uploaded
|
||||
attachment = Attachment.objects.filter(
|
||||
model_id=item.pk, model_type='stockitem'
|
||||
).first()
|
||||
self.assertIsNotNone(attachment)
|
||||
|
||||
def test_mdl_build(self):
|
||||
"""Test the Build model."""
|
||||
self.run_print_test(Build, 'build', label=False)
|
||||
@ -609,3 +591,11 @@ class TestReportTest(PrintTestMixins, ReportTest):
|
||||
def test_mdl_salesorder(self):
|
||||
"""Test the SalesOrder model."""
|
||||
self.run_print_test(SalesOrder, 'salesorder', label=False)
|
||||
|
||||
|
||||
class AdminTest(AdminTestCase):
|
||||
"""Tests for the admin interface integration."""
|
||||
|
||||
def test_admin(self):
|
||||
"""Test the admin URL."""
|
||||
self.helper(model=ReportTemplate)
|
||||
|
@ -1527,6 +1527,11 @@ class StocktakeTest(StockAPITestCase):
|
||||
|
||||
def test_action(self):
|
||||
"""Test each stocktake action endpoint, for validation."""
|
||||
target = {
|
||||
'api-stock-count': '10.00000',
|
||||
'api-stock-add': '10.00000',
|
||||
'api-stock-remove': '10.00000',
|
||||
}
|
||||
for endpoint in ['api-stock-count', 'api-stock-add', 'api-stock-remove']:
|
||||
url = reverse(endpoint)
|
||||
|
||||
@ -1585,6 +1590,12 @@ class StocktakeTest(StockAPITestCase):
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
# Valid POST
|
||||
data = {'items': [{'pk': 1234, 'quantity': 10}]}
|
||||
response = self.post(url, data, expected_code=201)
|
||||
self.assertEqual(response.data['items'][0]['pk'], 1234)
|
||||
self.assertEqual(response.data['items'][0]['quantity'], target[endpoint])
|
||||
|
||||
def test_transfer(self):
|
||||
"""Test stock transfers."""
|
||||
stock_item = StockItem.objects.get(pk=1234)
|
||||
|
@ -79,6 +79,11 @@ class StockDetailTest(StockViewTestCase):
|
||||
for act in actions:
|
||||
self.assertIn(act, html)
|
||||
|
||||
# Check with a wrong pk
|
||||
response = self.client.get(reverse('stock-item-detail', kwargs={'pk': 99}))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, reverse('stock-index'))
|
||||
|
||||
|
||||
class StockOwnershipTest(StockViewTestCase):
|
||||
"""Tests for stock ownership views."""
|
||||
|
@ -10,7 +10,7 @@ from django.test import override_settings
|
||||
from build.models import Build
|
||||
from common.models import InvenTreeSetting
|
||||
from company.models import Company
|
||||
from InvenTree.unit_test import InvenTreeTestCase
|
||||
from InvenTree.unit_test import AdminTestCase, InvenTreeTestCase
|
||||
from order.models import SalesOrder
|
||||
from part.models import Part, PartTestTemplate
|
||||
from stock.status_codes import StockHistoryCode
|
||||
@ -1347,3 +1347,11 @@ class StockLocationTest(InvenTreeTestCase):
|
||||
loc.location_type = None
|
||||
loc.save()
|
||||
self.assertEqual(loc.icon, '')
|
||||
|
||||
|
||||
class AdminTest(AdminTestCase):
|
||||
"""Tests for the admin interface integration."""
|
||||
|
||||
def test_admin(self):
|
||||
"""Test the admin URL."""
|
||||
self.helper(model=StockLocationType)
|
||||
|
@ -175,13 +175,10 @@ class GroupMixin:
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
"""Return serializer instance for this endpoint."""
|
||||
# Do we wish to include extra detail?
|
||||
try:
|
||||
params = self.request.query_params
|
||||
kwargs['permission_detail'] = InvenTree.helpers.str2bool(
|
||||
params.get('permission_detail', None)
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
params = self.request.query_params
|
||||
kwargs['permission_detail'] = InvenTree.helpers.str2bool(
|
||||
params.get('permission_detail', None)
|
||||
)
|
||||
kwargs['context'] = self.get_serializer_context()
|
||||
return self.serializer_class(*args, **kwargs)
|
||||
|
||||
@ -346,7 +343,7 @@ class GetAuthToken(APIView):
|
||||
return Response(data)
|
||||
|
||||
else:
|
||||
raise exceptions.NotAuthenticated()
|
||||
raise exceptions.NotAuthenticated() # pragma: no cover
|
||||
|
||||
|
||||
class TokenListView(DestroyAPIView, ListAPI):
|
||||
|
@ -164,7 +164,7 @@ class ApiToken(AuthToken, InvenTree.models.MetadataMixin):
|
||||
"""
|
||||
# If the token has not yet been saved, return the raw key
|
||||
if self.pk is None:
|
||||
return self.key
|
||||
return self.key # pragma: no cover
|
||||
|
||||
M = len(self.key) - 20
|
||||
|
||||
|
@ -64,12 +64,21 @@ class UserAPITests(InvenTreeAPITestCase):
|
||||
self.assertEqual(len(response.data), Group.objects.count())
|
||||
|
||||
# Check detail URL
|
||||
pk = response.data[0]['pk']
|
||||
response = self.get(
|
||||
reverse('api-group-detail', kwargs={'pk': response.data[0]['pk']}),
|
||||
reverse('api-group-detail', kwargs={'pk': pk}), expected_code=200
|
||||
)
|
||||
self.assertIn('name', response.data)
|
||||
self.assertNotIn('permissions', response.data)
|
||||
|
||||
# Check more detailed URL
|
||||
response = self.get(
|
||||
reverse('api-group-detail', kwargs={'pk': pk}),
|
||||
data={'permission_detail': True},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
self.assertIn('name', response.data)
|
||||
self.assertIn('permissions', response.data)
|
||||
|
||||
def test_logout(self):
|
||||
"""Test api logout endpoint."""
|
||||
@ -208,3 +217,34 @@ class UserTokenTests(InvenTreeAPITestCase):
|
||||
)
|
||||
self.assertIn('key', response.data)
|
||||
self.assertTrue(response.data['key'].startswith('inv-'))
|
||||
|
||||
def test_token_api(self):
|
||||
"""Test the token API."""
|
||||
url = reverse('api-token-list')
|
||||
response = self.get(url, expected_code=200)
|
||||
self.assertEqual(response.data, [])
|
||||
|
||||
# Get token
|
||||
response = self.get(reverse('api-token'), expected_code=200)
|
||||
self.assertIn('token', response.data)
|
||||
|
||||
# Now there should be one token
|
||||
response = self.get(url, expected_code=200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
self.assertEqual(response.data[0]['active'], True)
|
||||
self.assertEqual(response.data[0]['revoked'], False)
|
||||
self.assertEqual(response.data[0]['in_use'], False)
|
||||
expected_day = str(
|
||||
datetime.datetime.now().date() + datetime.timedelta(days=365)
|
||||
)
|
||||
self.assertEqual(response.data[0]['expiry'], expected_day)
|
||||
|
||||
# Destroy token
|
||||
self.delete(
|
||||
reverse('api-token-detail', kwargs={'pk': response.data[0]['id']}),
|
||||
expected_code=204,
|
||||
)
|
||||
|
||||
# Get token without auth (should fail)
|
||||
self.client.logout()
|
||||
self.get(reverse('api-token'), expected_code=401)
|
||||
|
@ -5,7 +5,8 @@ from django.contrib.auth.models import Group
|
||||
from django.test import TestCase, tag
|
||||
from django.urls import reverse
|
||||
|
||||
from InvenTree.unit_test import InvenTreeAPITestCase, InvenTreeTestCase
|
||||
from common.settings import set_global_setting
|
||||
from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase, InvenTreeTestCase
|
||||
from users.models import ApiToken, Owner, RuleSet
|
||||
|
||||
|
||||
@ -270,6 +271,29 @@ class OwnerModelTest(InvenTreeTestCase):
|
||||
)
|
||||
self.assertEqual(response['username'], self.username)
|
||||
|
||||
def test_display_name(self):
|
||||
"""Test the display name for the owner."""
|
||||
owner = Owner.get_owner(self.user)
|
||||
self.assertEqual(owner.name(), 'testuser')
|
||||
self.assertEqual(str(owner), 'testuser (user)')
|
||||
|
||||
# Change setting
|
||||
set_global_setting('DISPLAY_FULL_NAMES', True)
|
||||
self.user.first_name = 'first'
|
||||
self.user.last_name = 'last'
|
||||
self.user.save()
|
||||
owner = Owner.get_owner(self.user)
|
||||
|
||||
# Now first / last should be used
|
||||
self.assertEqual(owner.name(), 'first last')
|
||||
self.assertEqual(str(owner), 'first last (user)')
|
||||
|
||||
# Reset
|
||||
set_global_setting('DISPLAY_FULL_NAMES', False)
|
||||
self.user.first_name = ''
|
||||
self.user.last_name = ''
|
||||
self.user.save()
|
||||
|
||||
|
||||
class MFALoginTest(InvenTreeAPITestCase):
|
||||
"""Some simplistic tests to ensure that MFA is working."""
|
||||
@ -313,3 +337,15 @@ class MFALoginTest(InvenTreeAPITestCase):
|
||||
# Wrong login should not work
|
||||
auth_data['password'] = 'wrong'
|
||||
self.post(login_url, auth_data, expected_code=401)
|
||||
|
||||
|
||||
class AdminTest(AdminTestCase):
|
||||
"""Tests for the admin interface integration."""
|
||||
|
||||
def test_admin(self):
|
||||
"""Test the admin URL."""
|
||||
my_token = self.helper(
|
||||
model=ApiToken, model_kwargs={'user': self.user, 'name': 'test-token'}
|
||||
)
|
||||
# Additionally test str fnc
|
||||
self.assertEqual(str(my_token), my_token.token)
|
||||
|
@ -37,12 +37,22 @@ class TemplateTagTest(InvenTreeTestCase):
|
||||
|
||||
manifest_file = Path(__file__).parent.joinpath('static/web/.vite/manifest.json')
|
||||
# Try with removed manifest file
|
||||
manifest_file.rename(manifest_file.with_suffix('.json.bak')) # Rename
|
||||
new_name = manifest_file.rename(
|
||||
manifest_file.with_suffix('.json.bak')
|
||||
) # Rename
|
||||
resp = spa_helper.spa_bundle()
|
||||
self.assertIsNone(resp)
|
||||
manifest_file.with_suffix('.json.bak').rename(
|
||||
manifest_file.with_suffix('.json')
|
||||
) # Name back
|
||||
|
||||
# Try with differing name
|
||||
resp = spa_helper.spa_bundle(new_name)
|
||||
self.assertIsNotNone(resp)
|
||||
|
||||
# Broken manifest file
|
||||
manifest_file.write_text('broken')
|
||||
resp = spa_helper.spa_bundle(manifest_file)
|
||||
self.assertIsNone(resp)
|
||||
|
||||
new_name.rename(manifest_file.with_suffix('.json')) # Name back
|
||||
|
||||
def test_spa_settings(self):
|
||||
"""Test the 'spa_settings' template tag."""
|
||||
@ -78,6 +88,11 @@ class TemplateTagTest(InvenTreeTestCase):
|
||||
self.assertNotIn('show_server_selector', rsp)
|
||||
self.assertEqual(rsp['server_list'], ['aa', 'bb'])
|
||||
|
||||
def test_redirects(self):
|
||||
"""Test the redirect helper."""
|
||||
response = self.client.get('/assets/testpath')
|
||||
self.assertEqual(response.url, '/static/web/assets/testpath')
|
||||
|
||||
|
||||
class TestWebHelpers(InvenTreeAPITestCase):
|
||||
"""Tests for the web helpers."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user