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

New unit tests which improve coverage #70 #71

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
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
38 changes: 38 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,13 @@ 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 +647,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 +675,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())
24 changes: 24 additions & 0 deletions tests/test_population.py
Original file line number Diff line number Diff line change
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'])
68 changes: 68 additions & 0 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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
7 changes: 7 additions & 0 deletions tests/testapp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@

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()
Expand Down
1 change: 1 addition & 0 deletions tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ 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
Loading