15 Commits

14 changed files with 383 additions and 129 deletions

View File

@@ -11,6 +11,7 @@ pipenv = "*"
wheel = "*" wheel = "*"
twine = "*" twine = "*"
pandoc ="*" pandoc ="*"
pylint-django ="*"
[packages] [packages]
django = "*" django = "*"
@@ -18,6 +19,7 @@ easy-thumbnails = "*"
image = "*" image = "*"
django-widget-tweaks = "*" django-widget-tweaks = "*"
django-taggit = "*" django-taggit = "*"
# Commented out to not explicitly specify Python 3 subversion. # Commented out to not explicitly specify Python 3 subversion.
# [requires] [requires]
# python_version = "3.8" python_version = "3.7"

View File

@@ -6,15 +6,15 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from .models import * from lostplaces_app.models import *
from .forms import ExplorerCreationForm, ExplorerChangeForm from lostplaces_app.forms import ExplorerCreationForm, ExplorerChangeForm
# Register your models here. # Register your models here.
class VoucherAdmin(admin.ModelAdmin): class VoucherAdmin(admin.ModelAdmin):
fields = ['code', 'expires', 'created'] fields = ['code', 'expires_when', 'created_when']
readonly_fields = ['created'] readonly_fields = ['created_when']
admin.site.register(Explorer) admin.site.register(Explorer)
admin.site.register(Voucher, VoucherAdmin) admin.site.register(Voucher, VoucherAdmin)

View File

@@ -33,7 +33,7 @@ class Explorer(models.Model):
) )
def __str__(self): def __str__(self):
return self.user.name return self.user.username
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
@@ -54,11 +54,11 @@ class Voucher(models.Model):
""" """
code = models.CharField(unique=True, max_length=30) code = models.CharField(unique=True, max_length=30)
created = models.DateTimeField(auto_now_add=True) created_when = models.DateTimeField(auto_now_add=True)
expires = models.DateField() expires_when = models.DateTimeField()
def __str__(self): def __str__(self):
return "Voucher " + str(self.pk) return "Voucher " + str(self.code)
class Place (models.Model): class Place (models.Model):

View File

@@ -1,13 +1,11 @@
from django.db import models as django_models from django.test import TestCase
from django.contrib.auth.models import User from django.contrib.auth.models import User
class BaseData(TestCase):
def mock_user(): @classmethod
explorer_list = User.objects.all() def setUpTestData(cls):
if len(explorer_list) <= 0: User.objects.create_user(
return User.objects.create_user(
username='testpeter', username='testpeter',
password='Develop123' password='Develop123'
) ).save()
else:
return explorer_list[0]

View File

@@ -1,50 +1,98 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User
from django.core.exceptions import FieldDoesNotExist
from django.test import TestCase
class TestModel: # Creating a test user
class ModelTestCase:
''' '''
Base class for Lostplaces models Base class for Lostplaces models
''' '''
model = None
model_name = None model_name = None
def _test_field(self, field_name, field_class): def setUp(self):
self.object = self.model.objects.get(id=1)
self.model_name = type(self.model).__name__
def _test_field(self, field_name, field_class, must_have={}, must_not_have={}):
''' '''
Tests if a field exists under the given name and Tests if a field exists under the given name and
if the field is of the right type if the field is of the right type.
Also checks if the field has the given must_have attributes
and does not have any of the must_not_have attributes. If you
dont care about the value of the attribute you can just set it to
something that fullfills value == False (i.e. '' or 0)
''' '''
try:
field = self.object._meta.get_field(field_name) field = self.object._meta.get_field(field_name)
self.assertTrue( except FieldDoesNotExist:
field, self.fail(
msg="%s has no field named '%s'" % ( 'Expecting %s to have a field named \'%s\'' % (
self.model_name, self.model_name,
field_name field_name
) )
) )
self.assertEqual( self.assertEqual(
type(field), field_class, type(field), field_class,
msg='%s.%s name field is no CharField' % ( msg='Expecting type of %s.%s to be %s' % (
self.model_name, self.model_name,
field_name field_name,
field_class.__name__
) )
) )
for key, value in must_have.items():
if value:
self.assertEqual(getattr(field, key), value,
msg='Expeting %s.%s.%s to be \'%s\'' % (
self.model_name,
field_name,
key,
value
)
)
else:
self.assertTrue(hasattr(field, key),
msg='Expeting %s.%s to have \'%s\'' % (
self.model_name,
field_name,
key
)
)
for key, value in must_not_have.items():
if value:
self.assertTrue(getattr(field, key) != value,
msg='Expeting %s.%s.%s to not be \'%s\'' % (
self.model_name,
field_name,
key,
value
)
)
else:
self.assertFalse(hasattr(field, value),
msg='Expeting %s.%s to not have \'%s\'' % (
self.model_name,
field_name,
key
)
)
return field return field
def _test_char_field(self, field_name, min_length, max_length, must_have={}, must_hot_have={}):
def _test_char_field(self, field_name, min_length, max_length):
''' '''
Tests if the given field is a char field and if its max_length Tests if the given field is a char field and if its max_length
is in min_length and max_legth is in min_length and max_legth
''' '''
field = self._test_field(field_name, models.CharField) field = self._test_field(
self.assertEqual( field_name, models.CharField, must_have, must_hot_have)
type(field), models.CharField,
msg='%s.%s name field is no CharField' % (
self.model_name,
field_name
)
)
self.assertTrue( self.assertTrue(
field.max_length in range(min_length, max_length), field.max_length in range(min_length, max_length),
msg='%s.%s field max_length not in range of %d and %d' % ( msg='Expeting %s.%s field max_length to be in the range of %d and %d' % (
self.model_name, self.model_name,
field_name, field_name,
min_length, min_length,
@@ -52,7 +100,7 @@ class TestModel:
) )
) )
def _test_float_field(self, field_name, min_value=None, max_value=None): def _test_float_field(self, field_name, min_value=None, max_value=None, must_have={}, must_hot_have={}):
''' '''
Tests if the field is a floatfield. If min_value and/or max_value are passed, Tests if the field is a floatfield. If min_value and/or max_value are passed,
the validators of the field are also checked. The validator list of the field should the validators of the field are also checked. The validator list of the field should
@@ -61,11 +109,12 @@ class TestModel:
[MinValueValidator] if only min_value is passed, [MinValueValidator] if only min_value is passed,
[MaxValueValidator] if only max_value is passed [MaxValueValidator] if only max_value is passed
''' '''
field = self._test_field(field_name, models.FloatField) field = self._test_field(
field_name, models.FloatField, must_have, must_hot_have)
if min_value: if min_value:
self.assertTrue( self.assertTrue(
len(field.validators) >= 1, len(field.validators) >= 1,
msg='%s.%s first validator should check minimum' % ( msg='Expecting the first valiator of %s.%s to check the minimum' % (
self.model_name, self.model_name,
field_name field_name
) )
@@ -73,9 +122,10 @@ class TestModel:
self.assertEqual( self.assertEqual(
field.validators[0].limit_value, field.validators[0].limit_value,
min_value, min_value,
msg='%s.%s min value missmatch' % ( msg='Expecting the min value of %s.%s min to be at least %d' % (
self.model_name, self.model_name,
field_name field_name,
min_value
) )
) )
if max_value: if max_value:
@@ -84,7 +134,7 @@ class TestModel:
index += 1 index += 1
self.assertTrue( self.assertTrue(
len(field.validators) >= index+1, len(field.validators) >= index+1,
msg='%s.%s second validator should check maximum' % ( msg='Expecting the second valiator of %s.%s to check the maximum' % (
self.model_name, self.model_name,
field_name field_name
) )
@@ -92,30 +142,33 @@ class TestModel:
self.assertEqual( self.assertEqual(
field.validators[1].limit_value, field.validators[1].limit_value,
max_value, max_value,
msg='%s.%s max value missmatch' % ( msg='Expecting the max value of %s.%s min to be at most %d' % (
self.model_name, self.model_name,
field_name field_name,
max_value
) )
) )
class TestSubmittable(TestModel):
class SubmittableTestCase(ModelTestCase):
model_name = '<Class>' model_name = '<Class>'
related_name = None related_name = None
nullable = False nullable = False
def test_submitted_when(self): def test_submitted_when(self):
submitted_when = self._test_field('submitted_when', models.DateTimeField) submitted_when = self._test_field(
self.assertTrue(submitted_when.auto_now_add, 'submitted_when',
msg='%s.submitted_when should be auto_now_add' % ( models.DateTimeField,
self.model_name must_have={'auto_now_add': True}
)
) )
def test_submitted_by(self): def test_submitted_by(self):
submitted_by = self._test_field('submitted_by', models.ForeignKey) submitted_by = self._test_field('submitted_by', models.ForeignKey)
if self.related_name: if self.related_name:
self.assertEqual(submitted_by.remote_field.related_name, self.related_name) self.assertEqual(
submitted_by.remote_field.related_name, self.related_name)
if self.nullable: if self.nullable:
self.assertTrue(submitted_by.null,) self.assertTrue(submitted_by.null,)
self.assertTrue(submitted_by.blank) self.assertTrue(submitted_by.blank)
self.assertEqual(submitted_by.remote_field.on_delete, models.SET_NULL) self.assertEqual(
submitted_by.remote_field.on_delete, models.SET_NULL)

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -0,0 +1,58 @@
from django.test import TestCase
from django.db import models
from django.contrib.auth.models import User
from lostplaces_app.models import Explorer
class ExplorerTestCase(TestCase):
@classmethod
def setUpTestData(self):
User.objects.create_user(
username='testpeter',
password='Develop123'
)
def test_epxlorer_creation(self):
'''
Tests if the explorer profile will be automticly
created when a user is created
'''
user = User.objects.get(id=1)
explorer_list = Explorer.objects.all()
self.assertTrue(len(explorer_list) > 0,
msg='Expecting at least one Exlorer object, none found'
)
self.assertTrue(hasattr(user, 'explorer'),
msg='''Expecting the User instance to have an \'explorer\' attribute.
Check the Explorer model and the related name.'''
)
explorer = Explorer.objects.get(id=1)
self.assertEqual(explorer, user.explorer,
msg='''The Explorer object of the User did not match.
Expecting User with id 1 to have Explorer with id 1'''
)
explorer = Explorer.objects.get(id=1)
self.assertEqual(explorer.user, user,
msg='''The User object of the Explorer did not match.
Expecting Explorer with id 1 to have User with id 1'''
)
def test_explorer_deletion(self):
'''
Tests if the Explorer objects get's deleted when the User instance is deleted
'''
user = User.objects.get(username='testpeter')
explorer_id = user.explorer.id
user.delete()
with self.assertRaises(models.ObjectDoesNotExist,
msg='Expecting explorer objec to be deleted when the corresponding User object is deleted'
):
Explorer.objects.get(id=explorer_id)

View File

@@ -1,32 +1,56 @@
import datetime import datetime
import os
import shutil
from unittest import mock from unittest import mock
from django.test import TestCase from django.test import TestCase
from django.db import models from django.db import models
from django.core.files import File from django.core.files import File
from django.conf import settings
from django.contrib.auth.models import User
from lostplaces_app.models import PlaceImage from lostplaces_app.models import PlaceImage, Place
from lostplaces_app.tests.models import TestSubmittable from lostplaces_app.tests.models import SubmittableTestCase
from lostplaces_app.tests import mock_user from lostplaces_app.tests.models.test_place_model import PlaceTestCase
from lostplaces_app.tests.models import TestModel
from lostplaces_app.tests.models.test_place_model import mock_place
from easy_thumbnails.fields import ThumbnailerImageField from easy_thumbnails.fields import ThumbnailerImageField
def mock_place_image(): class TestPlaceImage(SubmittableTestCase, TestCase):
return PlaceImage( model = PlaceImage
description='Im a description',
filename=mock.MagicMock(spec=File, name='FileMock'), @classmethod
place=mock_place(), def setUpTestData(cls):
submitted_when=datetime.datetime.now(), user = User.objects.create_user(
submitted_by=mock_user().explorer username='testpeter',
password='Develop123'
) )
class TestPlaceImage(TestSubmittable, TestCase): place = Place.objects.create(
model_name = 'PlaceImage' name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=User.objects.get(username='testpeter').explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def setUp(self): current_dir = os.path.dirname(os.path.abspath(__file__))
self.object = mock_place_image() if not os.path.isfile(os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg')):
shutil.copyfile(
os.path.join(current_dir, 'im_a_image.jpeg'),
os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg')
)
PlaceImage.objects.create(
description='Im a description',
filename=os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg'),
place=place,
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer
)
def test_description(self): def test_description(self):
self._test_field('description', models.TextField) self._test_field('description', models.TextField)
@@ -51,5 +75,18 @@ class TestPlaceImage(TestSubmittable, TestCase):
) )
def test_str(self): def test_str(self):
place_image = mock_place_image() place_image = self.object
self.assertEqual(str(place_image), ' '.join([place_image.place.name, str(place_image.pk)])) self.assertTrue(place_image.place.name.lower() in str(place_image).lower(),
msg='Expecting %s.__str__ to contain the name of the place' % (
self.model_name
)
)
def test_deletion(self):
# TODO
path = self.object.filename.path
self.object.delete()
self.assertFalse(
os.path.isfile(path),
msg='Expecting the file of an place_image to be deleteed when an place_image is deleted'
)

View File

@@ -3,35 +3,38 @@ import datetime
from django.test import TestCase from django.test import TestCase
from django.db import models from django.db import models
from django.contrib.auth.models import User
from lostplaces_app.models import Place from lostplaces_app.models import Place
from lostplaces_app.tests.models import TestSubmittable from lostplaces_app.tests import BaseData
from lostplaces_app.tests import mock_user from lostplaces_app.tests.models import SubmittableTestCase
from lostplaces_app.tests.models import TestModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
def mock_place(): class PlaceTestCase(SubmittableTestCase, TestCase):
model = Place
related_name = 'places'
nullable = True
@classmethod
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
place = Place.objects.create( place = Place.objects.create(
name='Im a place', name='Im a place',
submitted_when=datetime.datetime.now(), submitted_when=datetime.datetime.now(),
submitted_by=mock_user().explorer, submitted_by=user.explorer,
location='Testtown', location='Testtown',
latitude=50.5, latitude=50.5,
longitude=7.0, longitude=7.0,
description='This is just a test, do not worry' description='This is just a test, do not worry'
) )
place.tags.add('I a tag', 'testlocation') place.tags.add('I a tag', 'testlocation')
place.save()
return place
class PlaceTestCase(TestSubmittable, TestCase):
model_name = 'Place'
related_name = 'places'
nullable = True
def setUp(self):
self.place = mock_place()
self.object = self.place
def test_name_field(self): def test_name_field(self):
self._test_char_field( self._test_char_field(
@@ -74,9 +77,11 @@ class PlaceTestCase(TestSubmittable, TestCase):
''' '''
place_list = [] place_list = []
for i in range(10): for i in range(10):
place = mock_place() place = Place.objects.get(id=1)
place.id = None
place.latitude = i+1 place.latitude = i+1
place.longitude = i+10 place.longitude = i+10
place.save()
place_list.append(place) place_list.append(place)
avg_latlon = Place.average_latlon(place_list) avg_latlon = Place.average_latlon(place_list)
@@ -96,7 +101,7 @@ class PlaceTestCase(TestSubmittable, TestCase):
Tests the average latitude/longitude calculation of a list Tests the average latitude/longitude calculation of a list
of one place of one place
''' '''
place = mock_place() place = Place.objects.get(id=1)
avg_latlon = Place.average_latlon([place]) avg_latlon = Place.average_latlon([place])
self.assertEqual(avg_latlon[0], place.latitude, self.assertEqual(avg_latlon[0], place.latitude,
msg='%s:(one place) average latitude missmatch' % ( msg='%s:(one place) average latitude missmatch' % (
@@ -127,10 +132,9 @@ class PlaceTestCase(TestSubmittable, TestCase):
) )
def test_str(self): def test_str(self):
place = mock_place() place = self.object
self.assertEqual(str(place), place.name, self.assertTrue(place.name.lower() in str(place).lower(),
msg='%s __str__ should return the name' % ( msg='Expecting %s.__str__ to contain the name' % (
self.model_name self.model_name
) )
) )

View File

@@ -0,0 +1,49 @@
import datetime
from django.test import TestCase
from django.db import models
from django.utils import timezone
from lostplaces_app.models import Voucher
from lostplaces_app.tests.models import ModelTestCase
class VoucheTestCase(ModelTestCase, TestCase):
model = Voucher
@classmethod
def setUpTestData(cls):
Voucher.objects.create(
code='ayDraJCCwfhcFiYmSR5GrcjcchDfcahv',
expires_when=timezone.now() + datetime.timedelta(days=1)
)
def test_voucher_code(self):
self._test_char_field(
'code',
10,
100,
must_have={'unique': True}
)
def test_voucher_created(self):
self._test_field(
'created_when',
models.DateTimeField,
must_have={'auto_now_add': True}
)
def test_voucher_expires(self):
self._test_field(
'expires_when',
models.DateTimeField,
must_not_have={'auto_now_add': True}
)
def test_str(self):
voucher = self.object
self.assertTrue(voucher.code.lower() in str(voucher).lower(),
msg='Expecting %s.__str__ to contain the voucher code' % (
self.model_name
)
)

View File

@@ -1,17 +1,36 @@
import datetime
from django.test import TestCase, Client from django.test import TestCase, Client
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth.models import User
from lostplaces_app.models import Place from lostplaces_app.models import Place
from django.contrib.auth.models import User from django.contrib.auth.models import User
from lostplaces_app.tests.models.test_place_model import mock_place
from lostplaces_app.tests import mock_user
class TestIsAuthenticated(TestCase): class TestIsAuthenticated(TestCase):
@classmethod
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=User.objects.get(username='testpeter').explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def setUp(self): def setUp(self):
self. client = Client() self. client = Client()
mock_place()
mock_user()
def test_logged_in(self): def test_logged_in(self):
self.client.login(username='testpeter', password='Develop123') self.client.login(username='testpeter', password='Develop123')
@@ -36,10 +55,27 @@ class TestIsAuthenticated(TestCase):
class TestIsPlaceSubmitter(TestCase): class TestIsPlaceSubmitter(TestCase):
@classmethod
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def setUp(self): def setUp(self):
self. client = Client() self. client = Client()
mock_place()
mock_user()
def test_is_submitter(self): def test_is_submitter(self):
self.client.login(username='testpeter', password='Develop123') self.client.login(username='testpeter', password='Develop123')

View File

@@ -1,17 +1,34 @@
import datetime
from django.test import TestCase, Client from django.test import TestCase, Client
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth.models import User
from lostplaces_app.models import Place from lostplaces_app.models import Place
from lostplaces_app.tests.models.test_place_model import mock_place
from lostplaces_app.tests import mock_user
class TestPlaceCreateView(TestCase): class TestPlaceCreateView(TestCase):
@classmethod
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def setUp(self): def setUp(self):
self. client = Client() self. client = Client()
mock_place()
mock_user()
def test_url_logged_in(self): def test_url_logged_in(self):
self.client.login(username='testpeter', password='Develop123') self.client.login(username='testpeter', password='Develop123')

View File

@@ -1,5 +1,5 @@
from django.urls import path from django.urls import path
from .views import ( from lostplaces_app.views import (
HomeView, HomeView,
PlaceDetailView, PlaceDetailView,
PlaceListView, PlaceListView,