Skip to content

Commit

Permalink
New unit tests which improve coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Serbel97 committed Oct 12, 2024
1 parent 508f3be commit d360baa
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.0.0-rc.12 : 12.10.2024

- **Added**: New unit tests which improve coverage.

## 1.0.0-rc.11 : 16.08.2024

- **Fixed**: Proper manipulation with `BaseStrategy` instances during population
Expand Down
2 changes: 1 addition & 1 deletion django_api_forms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def to_python(self, value) -> typing.Optional[File]:
image.verify()
f.image = Image.open(file) # Image have to be reopened after Image.verify() call
f.content_type = Image.MIME.get(image.format)
except Exception:
except Exception as e:
raise ValidationError(
self.error_messages['invalid_image'],
code='invalid_image'
Expand Down
39 changes: 39 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ def test_booleanfield_required_false(self):
log_input(truthy_val)
self.assertTrue(bool_field.clean(truthy_val))

def test_booleanfield_has_changed(self):
bool_field = BooleanField(disabled=True)
self.assertFalse(bool_field.has_changed(True, False))

bool_field = BooleanField()
self.assertTrue(bool_field.has_changed(True, False))
self.assertFalse(bool_field.has_changed(True, True))


class FieldListTests(SimpleTestCase):
def test_fieldlist_init(self):
Expand Down Expand Up @@ -541,6 +549,14 @@ def test_simple(self):
self.assertIsInstance(django_file, File)
self.assertEqual(django_file.size, 12412)

def test_non_value(self):
file_field = FileField()

expected_error = "'This field is required.'"
with self.assertRaisesMessage(ValidationError, expected_error):
file_field.clean(None)


def test_mime(self):
file_field = FileField(mime=('image/jpeg',))
django_file = file_field.clean(self._payload)
Expand Down Expand Up @@ -632,6 +648,13 @@ def setUp(self) -> None:
self._payload = f.read().strip('\n')
pass

def test_non_value(self):
file_field = ImageField()

expected_error = "'This field is required.'"
with self.assertRaisesMessage(ValidationError, expected_error):
file_field.clean(None)

def test_simple(self):
image_field = ImageField()
django_image = image_field.clean(self._payload)
Expand All @@ -653,6 +676,22 @@ def test_mime_mismatch(self):
log_input(kitten)
file_field.clean(kitten)

def test_invalid_image(self):
image_field = ImageField()

invalid_base64_image = "data:image/jpeg;base64,SGVsbG8gd29ybGQh"

expected_error_message = image_field.default_error_messages['invalid_image']

with self.assertRaises(ValidationError) as context:
image_field.to_python(invalid_base64_image)

# Retrieving the error messages list from the ValidationError
error_messages = context.exception.messages

# Verifying that the appropriate message is included
self.assertIn(expected_error_message, error_messages)

def test_invalid(self):
file_field = ImageField()

Expand Down
33 changes: 33 additions & 0 deletions tests/test_form_iterators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

from django.forms import fields
from django.test import TestCase
from django_api_forms import Form


class FormIteratorTests(TestCase):
def test_iter_fields(self):
class TestForm(Form):
field1 = fields.CharField(label='field1')
field2 = fields.IntegerField(label='field2')

form = TestForm()
field_labels = [field.label for field in form]

# Check that fields can be iterated over and contain the expected field names
self.assertEqual(sorted(field_labels), ['field1', 'field2'])

def test_getitem_field(self):
class TestForm(Form):
field1 = fields.CharField()
field2 = fields.IntegerField()

form = TestForm()

field1 = form['field1']
self.assertIsInstance(field1, fields.CharField)

field2 = form['field2']
self.assertIsInstance(field2, fields.IntegerField)

with self.assertRaises(KeyError):
form['nonexistent_field']
74 changes: 35 additions & 39 deletions tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
from typing import Optional

import msgpack
from django.core.exceptions import ValidationError
from django.forms import fields
from django.test import TestCase
from django.test.client import RequestFactory
from django_api_forms import Form, BooleanField
from django_api_forms.exceptions import UnsupportedMediaType
from tests.testapp.forms import BandForm
from tests.testapp.models import Band


Expand Down Expand Up @@ -90,13 +90,6 @@ class FunnyForm(Form):
def _normalize_url(cls, url: str) -> Optional[str]:
if not url:
return None
if url.startswith('http://'):
url = url.replace('http://', '')

if not url.startswith('https://'):
url = f"https://{url}"

return url

def clean_url(self):
return self._normalize_url(self.cleaned_data['url'])
Expand Down Expand Up @@ -134,13 +127,6 @@ class Meta:
def _normalize_url(cls, url: str) -> Optional[str]:
if not url:
return None
if url.startswith('http://'):
url = url.replace('http://', '')

if not url.startswith('https://'):
url = f"https://{url}"

return url

def clean_url(self):
return self._normalize_url(self.cleaned_data['url'])
Expand Down Expand Up @@ -181,21 +167,6 @@ class Meta:
formed = fields.IntegerField()
has_award = BooleanField()

@classmethod
def _normalize_url(cls, url: str) -> Optional[str]:
if not url:
return None
if url.startswith('http://'):
url = url.replace('http://', '')

if not url.startswith('https://'):
url = f"https://{url}"

return url

def clean_url(self):
return self._normalize_url(self.cleaned_data['url'])

request_factory = RequestFactory()
request = request_factory.post(
'/test/',
Expand Down Expand Up @@ -265,13 +236,6 @@ class FunnyForm(Form):
def _normalize_url(cls, url: str) -> Optional[str]:
if not url:
return None
if url.startswith('http://'):
url = url.replace('http://', '')

if not url.startswith('https://'):
url = f"https://{url}"

return url

def clean_url(self):
return self._normalize_url(self.cleaned_data['url'])
Expand All @@ -285,8 +249,6 @@ def clean(self):
if 'param1' in self.extras and 'param2' in self.extras:
self.extras['param2'] = 'param4'
return self.cleaned_data
else:
raise ValidationError("Missing params!", code='missing-params')

request_factory = RequestFactory()
request = request_factory.post(
Expand All @@ -305,3 +267,37 @@ def clean(self):
self.assertTrue(len(form.cleaned_data.keys()) == 3)
self.assertIsNone(form.cleaned_data['url'])
self.assertEqual(form.extras, valid_test_extras)

def test_exclude_field(self):
# Create form from request
request_factory = RequestFactory()
request = request_factory.post(
'/test/',
data={
'emails': {'Joy Division': '[email protected]'},
'name': 'Queen',
'formed': '1870',
'has_award': False,
},
content_type='application/json'
)
form = BandForm.create_from_request(request)
self.assertTrue(form.is_valid())

# Populate form
band = Band()
form.populate(band, exclude=['emails'])

self.assertEqual(band.name, form.cleaned_data['name'])
self.assertEqual(band.formed, 2000)
self.assertEqual(band.has_award, True)

def test_empty_request_body(self):
# Create form from request
request_factory = RequestFactory()
request = request_factory.get(
'/test/',
content_type='application/json'
)
form = BandForm.create_from_request(request)
self.assertFalse(form.is_valid())
26 changes: 25 additions & 1 deletion tests/test_population.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django_api_forms import Form, EnumField, FormField
from django_api_forms.exceptions import ApiFormException
from tests import settings
from tests.testapp.forms import AlbumForm, BandForm, ArtistForm
from tests.testapp.forms import AlbumForm, BandForm, ArtistForm, ConcertForm
from tests.testapp.models import Album, Artist, Band


Expand Down Expand Up @@ -123,3 +123,27 @@ def populate_artist(self, obj, value: dict) -> Artist:
self.assertIsInstance(my_model.artist, Artist)
self.assertEqual(my_model.year, 2020)
self.assertEqual(my_model.artist.name, 'Punk Pineapples')

def test_alias_strategy(self):
request_factory = RequestFactory()
data = {
'name': "Frank",
'genres': ["Rock", "Alternative"],
'members': 4,
'has_label': True
}

request = request_factory.post(
'/test/',
data=data,
content_type='application/json'
)

artist = Artist()
form = ArtistForm.create_from_request(request)
self.assertTrue(form.is_valid())

form.populate(artist)
self.assertIsInstance(artist, Artist)
self.assertEqual(artist.has_own_label, data['has_label'])
self.assertEqual(artist.name, data['name'])
69 changes: 69 additions & 0 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime

from django.conf import settings
from django.forms import ValidationError
from django.test import RequestFactory, TestCase

from tests.testapp.forms import AlbumForm
Expand Down Expand Up @@ -62,6 +63,74 @@ def test_invalid(self):
}
self.assertEqual(error, expected)

def test_raise_invalid(self):
rf = RequestFactory()

data = {
"title": "Unknown Pleasures",
"type": "vinyl",
"artist": {
"name": "Joy Division",
"genres": [
"rock",
"punk"
],
"members": 4
},
"year": 1992,
"songs": [
{
"title": "Disorder",
"duration": "3:29"
},
{
"title": "Day of the Lords",
"duration": "4:48",
"metadata": {
"_section": {
"type": "ID3v2",
"offset": 0,
"byteLength": 2048
},
"header": {
"majorVersion": 3,
"minorRevision": 0,
"flagsOctet": 0,
"unsynchronisationFlag": False,
"extendedHeaderFlag": False,
"experimentalIndicatorFlag": False,
"size": 2038
}
}
}
],
"metadata": {
"created_at": "2019-10-21T18:57:03+0100",
"updated_at": "2019-10-21T18:57:03+0100"
}
}
expected = {
"errors": [
{
"code": "forbidden-value",
"message": "Year 1992 is forbidden!",
"path": [
"year"
]
},
]
}

request = rf.post('/foo/bar', data=data, content_type='application/json')

form = AlbumForm.create_from_request(request)

self.assertFalse(form.is_valid())
error = {
'errors': [item.to_dict() for item in form._errors]
}
self.assertEqual(error, expected)

def test_valid(self):
rf = RequestFactory()
expected = {
Expand Down
8 changes: 7 additions & 1 deletion tests/testapp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@

from django_api_forms import Form, FieldList, AnyField, FormField, FormFieldList, EnumField, DictionaryField, \
ModelForm, BooleanField
from django_api_forms.population_strategies import AliasStrategy
from tests.testapp.models import Album, Artist


class ArtistForm(Form):
class Meta:
field_strategy = {
'has_label': AliasStrategy(property_name='has_own_label'),
}

has_label = BooleanField(required=False)
name = fields.CharField(required=True, max_length=100)
genres = FieldList(field=fields.CharField(max_length=30))
members = fields.IntegerField()


class SongForm(Form):
title = fields.CharField(required=True, max_length=100)
duration = fields.DurationField(required=True)
Expand Down
2 changes: 2 additions & 0 deletions tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class Meta:
name = models.CharField(max_length=100, unique=True)
genres = models.JSONField()
members = models.PositiveIntegerField()
has_own_label = models.BooleanField(default=False)



class Album(models.Model):
Expand Down

0 comments on commit d360baa

Please sign in to comment.