From e1ef7174f96dd2363b6dea79c73336a89c55336e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 7 Sep 2019 22:30:46 +1000 Subject: [PATCH 1/6] Install package for fast string matching - But really, mostly to supress a warning! --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a07aa8265f..cf45d83248 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,5 @@ django-qr-code==1.0.0 # Generate QR codes flake8==3.3.0 # PEP checking coverage>=4.5.3 # Unit test coverage python-coveralls==2.9.1 # Coveralls linking (for Travis) -fuzzywuzzy>=0.17.0 # Fuzzy string matching \ No newline at end of file +fuzzywuzzy>=0.17.0 # Fuzzy string matching +python-Levenshtein>=0.12.0 # Required for fuzzywuzzy \ No newline at end of file From 654fbc3847c17658ef08d9d81dd7e819ef0a2cde Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 7 Sep 2019 22:41:57 +1000 Subject: [PATCH 2/6] Ensure migrations are always called from the correct directory --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index aaf795144e..7ea3f33b03 100644 --- a/Makefile +++ b/Makefile @@ -15,10 +15,11 @@ migrate: python3 InvenTree/manage.py makemigrations stock python3 InvenTree/manage.py makemigrations build python3 InvenTree/manage.py makemigrations order - python3 InvenTree/manage.py migrate - python3 InvenTree/manage.py migrate --run-syncdb + python3 InvenTree/manage.py makemigrations + cd InvenTree && python3 manage.py migrate + cd InvenTree && python3 manage.py migrate --run-syncdb python3 InvenTree/manage.py check - python3 InvenTree/manage.py collectstatic + cd InvenTree && python3 manage.py collectstatic # Install all required packages install: From f24496c5a2dd5e0f808b92da2de848b8ab557593 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 7 Sep 2019 22:42:08 +1000 Subject: [PATCH 3/6] Enforce at least one base currency to be selected --- InvenTree/common/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index de3b9bab2c..61bb9ec138 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -69,7 +69,7 @@ class Currency(models.Model): cur.save() # If there are no currencies set as the base currency, set this as base - if not Currency.objects.filter(base=True).exists(): + if not Currency.objects.exclude(pk=self.pk).filter(base=True).exists(): self.base = True # If this is the base currency, ensure value is set to unity From 32f606627ddb9471a805394dbe182ad532cc5e3e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 7 Sep 2019 22:43:39 +1000 Subject: [PATCH 4/6] Special display case for base currecny --- InvenTree/templates/InvenTree/settings/currency.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/InvenTree/templates/InvenTree/settings/currency.html b/InvenTree/templates/InvenTree/settings/currency.html index 2c7b3dbccb..712cd1700c 100644 --- a/InvenTree/templates/InvenTree/settings/currency.html +++ b/InvenTree/templates/InvenTree/settings/currency.html @@ -60,6 +60,13 @@ field: 'value', title: 'Value', sortable: true, + formatter: function(value, row, index, field) { + if (row.base) { + return "Base Currency"; + } else { + return value; + } + } }, { formatter: function(value, row, index, field) { From 576226ad30ea3cdedb9d99e632b0f429ec144a7e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 7 Sep 2019 23:41:15 +1000 Subject: [PATCH 5/6] Tests for retrieving user auth tokens --- InvenTree/InvenTree/test_api.py | 45 +++++++++++++++++++++++++++++++ InvenTree/InvenTree/test_views.py | 7 +++-- InvenTree/users/urls.py | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 InvenTree/InvenTree/test_api.py diff --git a/InvenTree/InvenTree/test_api.py b/InvenTree/InvenTree/test_api.py new file mode 100644 index 0000000000..4ec173bf7d --- /dev/null +++ b/InvenTree/InvenTree/test_api.py @@ -0,0 +1,45 @@ +""" Low level tests for the InvenTree API """ + +from rest_framework.test import APITestCase +from rest_framework import status + +from django.urls import reverse + +from django.contrib.auth import get_user_model + + +class APITests(APITestCase): + """ Tests for the InvenTree API """ + + username = 'test_user' + password = 'test_pass' + + def setUp(self): + + # Create a user (but do not log in!) + User = get_user_model() + User.objects.create_user(self.username, 'user@email.com', self.password) + + def test_get_token_fail(self): + """ Ensure that an invalid user cannot get a token """ + + token_url = reverse('api-token') + + response = self.client.post(token_url, format='json', data={'username': 'bad', 'password': 'also_bad'}) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse('token' in response.data) + + def test_get_token_pass(self): + """ Ensure that a valid user can request an API token """ + + token_url = reverse('api-token') + + # POST to retreive a token + response = self.client.post(token_url, format='json', data={'username': self.username, 'password': self.password}) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue('token' in response.data) + self.assertTrue('pk' in response.data) + self.assertTrue(len(response.data['token']) > 0) + diff --git a/InvenTree/InvenTree/test_views.py b/InvenTree/InvenTree/test_views.py index 150a6a4f30..171dcbb05f 100644 --- a/InvenTree/InvenTree/test_views.py +++ b/InvenTree/InvenTree/test_views.py @@ -10,13 +10,16 @@ import os class ViewTests(TestCase): """ Tests for various top-level views """ + username = 'test_user' + password = 'test_pass' + def setUp(self): # Create a user User = get_user_model() - User.objects.create_user('username', 'user@email.com', 'password') + User.objects.create_user(self.username, 'user@email.com', self.password) - self.client.login(username='username', password='password') + self.client.login(username=self.username, password=self.password) def test_api_doc(self): """ Test that the api-doc view works """ diff --git a/InvenTree/users/urls.py b/InvenTree/users/urls.py index 6082ef14df..312789b55b 100644 --- a/InvenTree/users/urls.py +++ b/InvenTree/users/urls.py @@ -5,7 +5,7 @@ from . import views user_urls = [ url(r'^(?P[0-9]+)/?$', views.UserDetail.as_view(), name='user-detail'), - url(r'token', views.GetAuthToken.as_view()), + url(r'token', views.GetAuthToken.as_view(), name='api-token'), url(r'^$', views.UserList.as_view()), ] From baf096b3e72a98f981cb49f5a5ecdb75331b3355 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 8 Sep 2019 00:28:12 +1000 Subject: [PATCH 6/6] Ensure token validation is working correctly --- InvenTree/InvenTree/test_api.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/test_api.py b/InvenTree/InvenTree/test_api.py index 4ec173bf7d..0bb36db59f 100644 --- a/InvenTree/InvenTree/test_api.py +++ b/InvenTree/InvenTree/test_api.py @@ -11,6 +11,13 @@ from django.contrib.auth import get_user_model class APITests(APITestCase): """ Tests for the InvenTree API """ + fixtures = [ + 'location', + 'stock', + 'part', + 'category', + ] + username = 'test_user' password = 'test_pass' @@ -29,7 +36,7 @@ class APITests(APITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertFalse('token' in response.data) - + def test_get_token_pass(self): """ Ensure that a valid user can request an API token """ @@ -43,3 +50,18 @@ class APITests(APITestCase): self.assertTrue('pk' in response.data) self.assertTrue(len(response.data['token']) > 0) + # Now, use the token to access other data + token = response.data['token'] + + part_url = reverse('api-part-list') + + # Try to access without a token + response = self.client.get(part_url, format='json') + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + # Now, with the token + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.get(part_url, format='json') + + self.assertEqual(response.status_code, status.HTTP_200_OK)