Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup exception handler from drf-standardized-errors, adjust unit tests to changes, add some extra exceptions codes, clean up tests #57

Merged
merged 4 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ drf-spectacular = "==0.27.2"
django-storages = "==1.14.2"
boto3 = "==1.34.93"
django-countries = "==7.6.1"
drf-standardized-errors = "==0.13.0"

[dev-packages]
coverage = "==7.5.0"
Expand Down
304 changes: 157 additions & 147 deletions backend/Pipfile.lock

Large diffs are not rendered by default.

40 changes: 27 additions & 13 deletions backend/beers/tests/test_beer_styles_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from beers.models import BeerStyle
from beers.serializers import BeerStyleListSerializer, BeerStyleDetailSerializer
from core.shared.factories import BeerStyleFactory
from core.shared.unit_tests import APITestCase


Expand All @@ -17,16 +18,19 @@ class BeerStylesAPIViewsTests(APITestCase):
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
cls.style_ipa = BeerStyle.objects.create(name='India Pale Ale')
cls.style_apa = BeerStyle.objects.create(name='American Pale Ale')
cls.beer_style_to_delete = BeerStyle.objects.create(name='DDH APA')
cls.style_ipa = BeerStyleFactory(name='IPA')
cls.style_apa = BeerStyleFactory(name='APA')
cls.beer_style_to_delete = BeerStyleFactory(name='DDH APA')

def test_list_beer_styles(self):
queryset = BeerStyle.objects.order_by('-id')

response = self.client.get(self.styles_url)
response_json = response.json()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
first=response.json()['results'],
second=BeerStyleListSerializer(BeerStyle.objects.order_by('-id'), many=True).data
response_json['results'],
BeerStyleListSerializer(queryset, many=True).data
)

@unittest.skip('Currently disabled')
Expand All @@ -35,10 +39,12 @@ def test_create_beer_style(self):
response = self.client.post(self.styles_url, {
'name': 'DDH Hazy IPA'
})
response_json = response.json()
beer_style = BeerStyle.objects.get(name='DDH Hazy IPA')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(
first=response.json(),
second=BeerStyleListSerializer(BeerStyle.objects.get(name='DDH Hazy IPA')).data
response_json,
BeerStyleListSerializer(beer_style).data
)

@unittest.skip('Currently disabled')
Expand All @@ -64,24 +70,32 @@ def test_get_update_delete_beer_style_not_exists(self):
# self.assertEqual(response_delete.status_code, status.HTTP_404_NOT_FOUND)

def test_get_beer_style_by_id(self):
response = self.client.get(f'/api/styles/{self.style_apa.id}/')
response = self.client.get(
reverse_lazy('styles-detail', args=(self.style_apa.id,))
)
response_json = response.json()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
first=response.json(),
second=BeerStyleDetailSerializer(self.style_apa).data
response_json,
BeerStyleDetailSerializer(self.style_apa).data
)

@unittest.skip('Currently disabled')
def test_update_beer_style_by_id(self):
self._require_login_and_auth()
response = self.client.put(f'/api/styles/{self.style_apa.id}/', data={
payload = {
'name': 'APA',
'description': 'Very nice beer style',
})
}
response = self.client.put(
reverse_lazy('styles-detail', args=(self.style_apa.id,)),
data=payload
)
response_json = response.json()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.style_apa.refresh_from_db()
self.assertEqual(
response.json(),
response_json,
BeerStyleDetailSerializer(self.style_apa).data
)

Expand Down
124 changes: 77 additions & 47 deletions backend/beers/tests/test_beers_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from string import ascii_letters

from django.core.exceptions import ObjectDoesNotExist
from drf_standardized_errors.openapi_serializers import ValidationErrorEnum, ClientErrorEnum, ErrorCode404Enum
from rest_framework import status
from rest_framework.reverse import reverse_lazy

from beers.models import (
Beer, BeerStyle,
Expand All @@ -13,10 +15,11 @@
BeerDetailedSerializer,
BeerSerializer,
)
from core.shared.unit_tests import APITestCase
from core.shared.unit_tests import APITestCase, ExceptionResponse


class BeersAPIViewsTest(APITestCase):
list_url = reverse_lazy('beers-list')

@classmethod
def setUpTestData(cls) -> None:
Expand Down Expand Up @@ -51,99 +54,126 @@ def setUpTestData(cls) -> None:
)

def test_list_beers(self):
response = self.client.get('/api/beers/')
json_response = response.json()
beers = Beer.objects.order_by('-id')

response = self.client.get(self.list_url)
response_json = response.json()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(json_response['count'], beers.count())
self.assertEqual(response_json['count'], beers.count())
self.assertEqual(
first=BeerDetailedSerializer(beers, many=True).data,
second=json_response['results']
response_json['results'],
BeerDetailedSerializer(beers, many=True).data
)

def test_create_beer(self):
self._require_login_and_auth()
response = self.client.post('/api/beers/', data={
payload = {
'name': "a'la Grodziskie",
'percentage': 5,
'volume_ml': 500,
})
}
self._require_login_and_auth()
response = self.client.post(self.list_url, payload)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
beer = Beer.objects.get(name=payload['name'])
self.assertEqual(
response.json(),
BeerSerializer(Beer.objects.get(name="a'la Grodziskie")).data
BeerSerializer(beer).data
)

def test_create_beer_missing_data(self):
self._require_login_and_auth()
response = self.client.post('/api/beers/', data={
payload = {
'name': "Random name",
'volume_ml': 750,
})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('percentage', response.json())
}
self._require_login_and_auth()
response = self.client.post(self.list_url, payload)
res = ExceptionResponse.from_response(response)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(res.type, ValidationErrorEnum.VALIDATION_ERROR)
err = res.get_error_by_attr('percentage')
self.assertEqual(err.code, 'required')

def test_create_beer_negative_percentage(self):
self._require_login_and_auth()
response = self.client.post('/api/beers/', data={
payload = {
'name': "Negative",
'percentage': -1,
'volume_ml': 500,
})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('percentage', response.json())
}
self._require_login_and_auth()
response = self.client.post(self.list_url, payload)
res = ExceptionResponse.from_response(response)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(res.type, ValidationErrorEnum.VALIDATION_ERROR)
err = res.get_error_by_attr('percentage')
self.assertEqual(err.code, 'min_value')

# todo: more create beer tests (including base64 image upload)

def test_retrieve_beer(self):
beer = Beer.objects.create(name='Kwas Theta', percentage=10.2, volume_ml=500)
response = self.client.get(f'/api/beers/{beer.id}/')
response = self.client.get(
reverse_lazy('beers-detail', args=(beer.id,))
)
response_json = response.json()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json(), BeerSerializer(beer).data)
self.assertEqual(
response_json,
BeerSerializer(beer).data
)

# todo: more retrieve beer tests

def test_get_update_delete_beer_not_exists(self):
response_get = self.client.get('/api/beers/200/')
self.assertEqual(response_get.status_code, status.HTTP_404_NOT_FOUND)

# self._require_login_and_auth()
# response_put = self.client.put('/api/beers/200/', {})
# self.assertEqual(response_put.status_code, status.HTTP_404_NOT_FOUND)
#
# response_patch = self.client.patch('/api/beers/200/', {})
# self.assertEqual(response_patch.status_code, status.HTTP_404_NOT_FOUND)
#
# response_delete = self.client.delete('/api/beers/200/')
# self.assertEqual(response_delete.status_code, status.HTTP_404_NOT_FOUND)
def test_get_beer_does_not_exists(self):
beer_id = 200
response = self.client.get(
reverse_lazy('beers-detail', args=(beer_id,))
)
res = ExceptionResponse.from_response(response)
self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(res.type, ClientErrorEnum.CLIENT_ERROR)
self.assertIn(ErrorCode404Enum.NOT_FOUND, res.codes)

@unittest.skip('Currently disabled')
def test_update_beer_by_id(self):
self._require_login_and_auth()
self.assertIsNone(self.beer_to_update.description)
response = self.client.patch(f"/api/beers/{self.beer_to_update.id}/", {
"description": 'Very nice beer',
})
payload = {
'description': 'Very nice beer',
}
response = self.client.patch(
reverse_lazy('beers-detail', args=(self.beer_to_update.id,)),
payload
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
beer = Beer.objects.get(name='PanIIPAni')
self.assertEqual(beer.description, "Very nice beer")
self.beer_to_update.refresh_from_db()
self.assertEqual(self.beer_to_update.description, payload['description'])

@unittest.skip('Currently disabled')
def test_update_beer_too_long_data(self):
self._require_login_and_auth()
response = self.client.patch(f"/api/beers/{self.beer_to_update.id}/", {
"name": ''.join(choice(ascii_letters) for _ in range(101)),
})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
payload = {
'name': ''.join(choice(ascii_letters) for _ in range(101)),
}
response = self.client.patch(
reverse_lazy('beers-detail', args=(self.beer_to_update.id,)),
payload
)
res = ExceptionResponse.from_response(response)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(res.type, ValidationErrorEnum.VALIDATION_ERROR)
err = res.get_error_by_attr('name')
self.assertEqual(err.code, 'max_length')

@unittest.skip('Currently disabled')
def test_delete_beer_by_id(self):
self._require_login_and_auth()
qs_len_before = Beer.objects.all().count()
qs_len_before = Beer.objects.count()
lookup_id = self.beer_to_delete.id
response = self.client.delete(f"/api/beers/{lookup_id}/")
response = self.client.delete(
reverse_lazy('beers-detail', args=(lookup_id,))
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(qs_len_before - 1, Beer.objects.all().count())
self.assertEqual(qs_len_before - 1, Beer.objects.count())
with self.assertRaises(ObjectDoesNotExist):
Beer.objects.get(id=lookup_id)

Expand Down
Loading
Loading