From 7b4d3a3c07782ac19c87cf5bf927f17c612c9b23 Mon Sep 17 00:00:00 2001 From: eeintech Date: Tue, 6 Apr 2021 13:16:01 -0400 Subject: [PATCH] Added test units for migration and API --- InvenTree/company/api.py | 3 - .../migrations/0033_supplierpart_update.py | 93 ++++++------ InvenTree/company/test_api.py | 63 ++++++++- InvenTree/company/test_migrations.py | 132 +++++++++++++++++- InvenTree/company/test_views.py | 44 +++++- 5 files changed, 283 insertions(+), 52 deletions(-) diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index ddd5624238..f807ca221c 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -186,9 +186,6 @@ class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView): queryset = ManufacturerPart.objects.all() serializer_class = ManufacturerPartSerializer - read_only_fields = [ - ] - class SupplierPartList(generics.ListCreateAPIView): """ API endpoint for list view of SupplierPart object diff --git a/InvenTree/company/migrations/0033_supplierpart_update.py b/InvenTree/company/migrations/0033_supplierpart_update.py index 16d224269a..de98ff72cb 100644 --- a/InvenTree/company/migrations/0033_supplierpart_update.py +++ b/InvenTree/company/migrations/0033_supplierpart_update.py @@ -7,62 +7,65 @@ def supplierpart_make_manufacturer_parts(apps, schema_editor): Part = apps.get_model('part', 'Part') ManufacturerPart = apps.get_model('company', 'ManufacturerPart') SupplierPart = apps.get_model('company', 'SupplierPart') + + supplier_parts = SupplierPart.objects.all() - print(f'\nCreating Manufacturer parts\n{"-"*10}') - for supplier_part in SupplierPart.objects.all(): - print(f'{supplier_part.supplier.name[:15].ljust(15)} | {supplier_part.SKU[:15].ljust(15)}\t', end='') + if supplier_parts: + print(f'\nCreating Manufacturer parts\n{"-"*10}') + for supplier_part in supplier_parts: + print(f'{supplier_part.supplier.name[:15].ljust(15)} | {supplier_part.SKU[:15].ljust(15)}\t', end='') - if supplier_part.manufacturer_part: - print(f'[ERROR: MANUFACTURER PART ALREADY EXISTS]') - continue + if supplier_part.manufacturer_part: + print(f'[ERROR: MANUFACTURER PART ALREADY EXISTS]') + continue - part = supplier_part.part - if not part: - print(f'[ERROR: SUPPLIER PART IS NOT CONNECTED TO PART]') - continue - - manufacturer = supplier_part.manufacturer - MPN = supplier_part.MPN - link = supplier_part.link - description = supplier_part.description - - if manufacturer or MPN: - print(f' | {part.name[:15].ljust(15)}', end='') + part = supplier_part.part + if not part: + print(f'[ERROR: SUPPLIER PART IS NOT CONNECTED TO PART]') + continue - try: - print(f' | {manufacturer.name[:15].ljust(15)}', end='') - except TypeError: - print(f' | {"EMPTY MANUF".ljust(15)}', end='') + manufacturer = supplier_part.manufacturer + MPN = supplier_part.MPN + link = supplier_part.link + description = supplier_part.description - try: - print(f' | {MPN[:15].ljust(15)}', end='') - except TypeError: - print(f' | {"EMPTY MPN".ljust(15)}', end='') + if manufacturer or MPN: + print(f' | {part.name[:15].ljust(15)}', end='') + + try: + print(f' | {manufacturer.name[:15].ljust(15)}', end='') + except AttributeError: + print(f' | {"EMPTY MANUF".ljust(15)}', end='') - print('\t', end='') + try: + print(f' | {MPN[:15].ljust(15)}', end='') + except TypeError: + print(f' | {"EMPTY MPN".ljust(15)}', end='') - # Create ManufacturerPart - manufacturer_part = ManufacturerPart(part=part, manufacturer=manufacturer, MPN=MPN, description=description, link=link) - created = False - try: - with transaction.atomic(): - manufacturer_part.save() - created = True - except IntegrityError: - manufacturer_part = ManufacturerPart.objects.get(part=part, manufacturer=manufacturer, MPN=MPN) + print('\t', end='') - # Link it to SupplierPart - supplier_part.manufacturer_part = manufacturer_part - supplier_part.save() + # Create ManufacturerPart + manufacturer_part = ManufacturerPart(part=part, manufacturer=manufacturer, MPN=MPN, description=description, link=link) + created = False + try: + with transaction.atomic(): + manufacturer_part.save() + created = True + except IntegrityError: + manufacturer_part = ManufacturerPart.objects.get(part=part, manufacturer=manufacturer, MPN=MPN) - if created: - print(f'[SUCCESS: MANUFACTURER PART CREATED]') + # Link it to SupplierPart + supplier_part.manufacturer_part = manufacturer_part + supplier_part.save() + + if created: + print(f'[SUCCESS: MANUFACTURER PART CREATED]') + else: + print(f'[IGNORED: MANUFACTURER PART ALREADY EXISTS]') else: - print(f'[IGNORED: MANUFACTURER PART ALREADY EXISTS]') - else: - print(f'[IGNORED: MISSING MANUFACTURER DATA]') + print(f'[IGNORED: MISSING MANUFACTURER DATA]') - print(f'{"-"*10}\nDone') + print(f'{"-"*10}\nDone\n') class Migration(migrations.Migration): diff --git a/InvenTree/company/test_api.py b/InvenTree/company/test_api.py index 219a8ac019..0f352b5f4f 100644 --- a/InvenTree/company/test_api.py +++ b/InvenTree/company/test_api.py @@ -27,7 +27,7 @@ class CompanyTest(InvenTreeAPITestCase): def test_company_list(self): url = reverse('api-company-list') - # There should be two companies + # There should be three companies response = self.get(url) self.assertEqual(len(response.data), 3) @@ -62,3 +62,64 @@ class CompanyTest(InvenTreeAPITestCase): data = {'search': 'cup'} response = self.get(url, data) self.assertEqual(len(response.data), 2) + + +class ManufacturerTest(InvenTreeAPITestCase): + """ + Series of tests for the Manufacturer DRF API + """ + + fixtures = [ + 'category', + 'part', + 'location', + 'company', + 'manufacturer_part', + ] + + roles = [ + 'part.change', + ] + + def test_manufacturer_part_list(self): + url = reverse('api-manufacturer-part-list') + + # There should be three manufacturer parts + response = self.get(url) + self.assertEqual(len(response.data), 3) + + # Filter by manufacturer + data = {'company': 7} + response = self.get(url, data) + self.assertEqual(len(response.data), 2) + + # Filter by part + data = {'part': 5} + response = self.get(url, data) + self.assertEqual(len(response.data), 2) + + # Filter by supplier part (should return only one manufacturer part) + data = {'supplier_part': 10} + response = self.get(url, data) + self.assertEqual(len(response.data), 1) + + def test_manufacturer_part_detail(self): + url = reverse('api-manufacturer-part-detail', kwargs={'pk': 1}) + response = self.get(url) + + self.assertEqual(response.data['MPN'], 'MPN123') + + # Change the MPN + data = { + 'MPN': 'MPN-TEST-123', + } + response = self.client.patch(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['MPN'], 'MPN-TEST-123') + + def test_manufacturer_part_search(self): + # Test search functionality in manufacturer list + url = reverse('api-manufacturer-part-list') + data = {'search': 'MPN'} + response = self.get(url, data) + self.assertEqual(len(response.data), 3) diff --git a/InvenTree/company/test_migrations.py b/InvenTree/company/test_migrations.py index 1a51a5a5b0..6b01192092 100644 --- a/InvenTree/company/test_migrations.py +++ b/InvenTree/company/test_migrations.py @@ -79,7 +79,7 @@ class TestManufacturerField(MigratorTestCase): part=part, supplier=supplier, SKU='SCREW.002', - manufacturer_name='Zero Corp' + manufacturer_name='Zero Corp', ) self.assertEqual(Company.objects.count(), 1) @@ -107,6 +107,136 @@ class TestManufacturerField(MigratorTestCase): self.assertEqual(part.manufacturer.name, 'ACME') +class TestManufacturerPart(MigratorTestCase): + """ + Tests for migration 0032 and 0033 which added ManufacturerPart model + """ + + migrate_from = ('company', '0031_auto_20210103_2215') + migrate_to = ('company', '0033_supplierpart_update') + + def prepare(self): + """ + Prepare the database by adding some test data 'before' the change: + + - Part object + - Company object (supplier) + - SupplierPart object + """ + + Part = self.old_state.apps.get_model('part', 'part') + Company = self.old_state.apps.get_model('company', 'company') + SupplierPart = self.old_state.apps.get_model('company', 'supplierpart') + + # Create an initial part + part = Part.objects.create( + name='CAP CER 0.1UF 10V X5R 0402', + description='CAP CER 0.1UF 10V X5R 0402', + purchaseable=True, + level=0, + tree_id=0, + lft=0, + rght=0, + ) + + # Create a manufacturer + manufacturer = Company.objects.create( + name='Murata', + description='Makes capacitors', + is_manufacturer=True, + is_supplier=False, + is_customer=False, + ) + + # Create suppliers + supplier_1 = Company.objects.create( + name='Digi-Key', + description='A supplier of components', + is_manufacturer=False, + is_supplier=True, + is_customer=False, + ) + + supplier_2 = Company.objects.create( + name='Mouser', + description='We sell components', + is_manufacturer=False, + is_supplier=True, + is_customer=False, + ) + + # Add some SupplierPart objects + SupplierPart.objects.create( + part=part, + supplier=supplier_1, + SKU='DK-MUR-CAP-123456-ND', + manufacturer=manufacturer, + MPN='MUR-CAP-123456', + ) + + SupplierPart.objects.create( + part=part, + supplier=supplier_1, + SKU='DK-MUR-CAP-987654-ND', + manufacturer=manufacturer, + MPN='MUR-CAP-987654', + ) + + SupplierPart.objects.create( + part=part, + supplier=supplier_2, + SKU='CAP-CER-01UF', + manufacturer=manufacturer, + MPN='MUR-CAP-123456', + ) + + # No MPN + SupplierPart.objects.create( + part=part, + supplier=supplier_2, + SKU='CAP-CER-01UF-1', + manufacturer=manufacturer, + ) + + # No Manufacturer + SupplierPart.objects.create( + part=part, + supplier=supplier_2, + SKU='CAP-CER-01UF-2', + MPN='MUR-CAP-123456', + ) + + # No Manufacturer data + SupplierPart.objects.create( + part=part, + supplier=supplier_2, + SKU='CAP-CER-01UF-3', + ) + + def test_manufacturer_part_objects(self): + """ + Test that the new companies have been created successfully + """ + + # Check on the SupplierPart objects + SupplierPart = self.new_state.apps.get_model('company', 'supplierpart') + + supplier_parts = SupplierPart.objects.all() + self.assertEqual(supplier_parts.count(), 6) + + supplier_parts = SupplierPart.objects.filter(supplier__name='Mouser') + self.assertEqual(supplier_parts.count(), 4) + + # Check on the ManufacturerPart objects + ManufacturerPart = self.new_state.apps.get_model('company', 'manufacturerpart') + + manufacturer_parts = ManufacturerPart.objects.all() + self.assertEqual(manufacturer_parts.count(), 4) + + manufacturer_part = manufacturer_parts.first() + self.assertEqual(manufacturer_part.MPN, 'MUR-CAP-123456') + + class TestCurrencyMigration(MigratorTestCase): """ Tests for upgrade from basic currency support to django-money diff --git a/InvenTree/company/test_views.py b/InvenTree/company/test_views.py index 1868175648..e6eb54e0bf 100644 --- a/InvenTree/company/test_views.py +++ b/InvenTree/company/test_views.py @@ -220,7 +220,7 @@ class ManufacturerPartViewTests(CompanyViewTestBase): response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) - # How many supplier parts are already in the database? + # How many manufaturer parts are already in the database? n = ManufacturerPart.objects.all().count() data = { @@ -240,6 +240,41 @@ class ManufacturerPartViewTests(CompanyViewTestBase): # Check that the ManufacturerPart was created! self.assertEqual(n + 1, ManufacturerPart.objects.all().count()) + # Try to create duplicate ManufacturerPart + (response, errors) = self.post(url, data, valid=False) + + self.assertIsNotNone(errors.get('__all__', None)) + + # Check that the ManufacturerPart count stayed the same + self.assertEqual(n + 1, ManufacturerPart.objects.all().count()) + + def test_supplier_part_create(self): + """ + Test that the SupplierPartCreate view creates Manufacturer Part. + """ + + url = reverse('supplier-part-create') + + # First check that we can GET the form + response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(response.status_code, 200) + + # How many manufacturer parts are already in the database? + n = ManufacturerPart.objects.all().count() + + data = { + 'part': 1, + 'supplier': 1, + 'SKU': 'SKU_TEST', + 'manufacturer': 6, + 'MPN': 'MPN_TEST', + } + + (response, errors) = self.post(url, data, valid=True) + + # Check that the ManufacturerPart was created! + self.assertEqual(n + 1, ManufacturerPart.objects.all().count()) + def test_manufacturer_part_delete(self): """ Test the ManufacturerPartDelete view @@ -248,11 +283,13 @@ class ManufacturerPartViewTests(CompanyViewTestBase): url = reverse('manufacturer-part-delete') # Get form using 'part' argument - response = self.client.get(url, {'part': '5'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get(url, {'part': '2'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 200) # POST to delete manufacturer part n = ManufacturerPart.objects.count() + m = SupplierPart.objects.count() + response = self.client.post( url, { @@ -263,4 +300,7 @@ class ManufacturerPartViewTests(CompanyViewTestBase): self.assertEqual(response.status_code, 200) + # Check that the ManufacturerPart was deleted self.assertEqual(n - 1, ManufacturerPart.objects.count()) + # Check that the SupplierParts were deleted + self.assertEqual(m - 2, SupplierPart.objects.count())