Compare commits
17 Commits
main
...
ci-test-co
Author | SHA1 | Date | |
---|---|---|---|
c9f46d7547 | |||
9f23f5768c | |||
19210f90cd | |||
462bb8f485 | |||
ea4d15b99a | |||
de30dfcb8b | |||
36a979954c | |||
71ef17dc97 | |||
206cd282e6 | |||
e399346c3e | |||
929c6dfff0 | |||
841b57fea2 | |||
9e5446ff1d | |||
3b79809b8c | |||
53e6db3655 | |||
424f91e919 | |||
84ce5f54b2 |
4
.coveragerc
Normal file
4
.coveragerc
Normal file
@ -0,0 +1,4 @@
|
||||
[run]
|
||||
omit =
|
||||
*/migrations/*
|
||||
*/tests/*
|
@ -6,6 +6,9 @@ steps:
|
||||
commands:
|
||||
- cd docs && make html
|
||||
|
||||
when:
|
||||
event: [ tag, push ]
|
||||
|
||||
deploy:
|
||||
image: appleboy/drone-scp
|
||||
settings:
|
||||
@ -19,6 +22,8 @@ steps:
|
||||
source: docs/_build/html/
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
when:
|
||||
event: [ tag, push ]
|
||||
|
||||
|
||||
|
14
.woodpecker/test.yml
Normal file
14
.woodpecker/test.yml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
|
||||
steps:
|
||||
test:
|
||||
image: python
|
||||
commands:
|
||||
- python -m pip install '.[develop]'
|
||||
- coverage run --source='.' src/manage.py test src && coverage html
|
||||
- coverage html
|
||||
- cat htmlcov/index.html
|
||||
when:
|
||||
event: [tag, push]
|
||||
|
||||
|
20
README.md
20
README.md
@ -77,6 +77,26 @@ docker push moanos/notfellchen:latest
|
||||
docker run -p8000:7345 moanos/notfellchen:latest
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Tests can be run with
|
||||
|
||||
```zsh
|
||||
nf test src
|
||||
```
|
||||
|
||||
If you want to report on code coverage run
|
||||
|
||||
```zsh
|
||||
coverage run --source='.' src/manage.py test src
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```
|
||||
coverage report
|
||||
```
|
||||
|
||||
## Geocoding
|
||||
|
||||
Geocoding services (search map data by name, address or postcode) are provided via the
|
||||
|
@ -36,6 +36,11 @@ An application can then send this token in the request header for authorization.
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
All Endpoints are documented at https://notfellchen.org/api/schema/swagger-ui/ or at https://notfellchen.org/api/schema/redoc/ if you prefer redoc.
|
||||
The OpenAI schema can be downloaded at https://notfellchen.org/api/schema/
|
||||
|
||||
Examples are documented here.
|
||||
|
||||
Get Adoption Notices
|
||||
++++++++++++++++++++
|
||||
|
||||
|
@ -5,7 +5,7 @@ Report a bug
|
||||
^^^^^^^^^^^^
|
||||
|
||||
To report a bug, file an issue on `Github
|
||||
<https://codeberg.org/moanos/notfellchen/issues>`_
|
||||
<https://github.com/moan0s/notfellchen/issues>`_
|
||||
|
||||
Try to include the following information:
|
||||
|
||||
@ -29,7 +29,7 @@ To contribute simply clone the directory, make your changes and file a
|
||||
pull request.
|
||||
|
||||
If you want to know what can be done, have a look at the current `Github
|
||||
<https://codeberg.org/moanos/notfellchen/issues>`_.
|
||||
<https://github.com/moan0s/notfellchen/issues>`_.
|
||||
|
||||
Get in touch!
|
||||
^^^^^^^^^^^^^
|
||||
|
@ -5,8 +5,7 @@ What qualifies as release?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A new release should be announced when a significant number functions, bugfixes or other improvements to the software
|
||||
is made. Usually this indicates a minor release.
|
||||
Major releases are yet to be determined.
|
||||
is made. Notfellchen follows `Semantic Versioning <https://semver.org/>`_.
|
||||
|
||||
What should be done before a release?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -14,7 +13,7 @@ What should be done before a release?
|
||||
Tested basic functions
|
||||
######################
|
||||
|
||||
Run :command:`pytest`
|
||||
Run :command:`nf test src`
|
||||
|
||||
Test upgrade on a copy of a production database
|
||||
###############################################
|
||||
@ -38,4 +37,4 @@ Do a final commit on this change, and tag the commit as release with appropriate
|
||||
git tag -a v1.0.0 -m "Releasing version v1.0.0"
|
||||
git push origin v1.0.0
|
||||
|
||||
Make sure the tag is visible on Codeberg and celebrate 🥳
|
||||
Make sure the tag is visible on GitHub/Codeberg and celebrate 🥳
|
||||
|
@ -8,13 +8,13 @@ build-backend = "setuptools.build_meta"
|
||||
name = "notfellchen"
|
||||
description = "A tool to help."
|
||||
authors = [
|
||||
{name = "moanos", email = "julian-samuel@gebuehr.net"},
|
||||
{ name = "moanos", email = "julian-samuel@gebuehr.net" },
|
||||
]
|
||||
maintainers = [
|
||||
{name = "moanos", email = "julian-samuel@gebuehr.net"},
|
||||
{ name = "moanos", email = "julian-samuel@gebuehr.net" },
|
||||
]
|
||||
keywords = ["animal", "adoption", "django", "rescue", ]
|
||||
license = {text = "AGPL-3.0-or-later"}
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
classifiers = [
|
||||
"Environment :: Web",
|
||||
"License :: OSI Approved :: GNU Affero General Public License v3",
|
||||
@ -24,14 +24,12 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"Django",
|
||||
"coverage",
|
||||
"codecov",
|
||||
"sphinx",
|
||||
"sphinx-rtd-theme",
|
||||
"gunicorn",
|
||||
"fontawesomefree",
|
||||
"whitenoise",
|
||||
"model_bakery",
|
||||
"markdown",
|
||||
"Pillow",
|
||||
"django-registration",
|
||||
@ -48,6 +46,8 @@ dynamic = ["version", "readme"]
|
||||
[project.optional-dependencies]
|
||||
develop = [
|
||||
"pytest",
|
||||
"coverage",
|
||||
"model_bakery",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@ -62,6 +62,6 @@ nf = 'notfellchen.main:main'
|
||||
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "notfellchen.__version__"}
|
||||
readme = {file = "README.md"}
|
||||
version = { attr = "notfellchen.__version__" }
|
||||
readme = { file = "README.md" }
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.4 on 2025-01-14 06:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0035_alter_image_alt_text_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='basenotification',
|
||||
name='read_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Gelesen am'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.4 on 2025-01-14 06:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fellchensammlung', '0036_basenotification_read_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='basenotification',
|
||||
name='title',
|
||||
field=models.CharField(max_length=100, verbose_name='Titel'),
|
||||
),
|
||||
]
|
@ -818,7 +818,8 @@ class Comment(models.Model):
|
||||
class BaseNotification(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
title = models.CharField(max_length=100)
|
||||
read_at = models.DateTimeField(blank=True, null=True, verbose_name=_("Gelesen am"))
|
||||
title = models.CharField(max_length=100, verbose_name=_("Titel"))
|
||||
text = models.TextField(verbose_name="Inhalt")
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
||||
read = models.BooleanField(default=False)
|
||||
@ -829,6 +830,11 @@ class BaseNotification(models.Model):
|
||||
def get_absolute_url(self):
|
||||
self.user.get_notifications_url()
|
||||
|
||||
def mark_read(self):
|
||||
self.read = True
|
||||
self.read_at = timezone.now()
|
||||
self.save()
|
||||
|
||||
|
||||
class CommentNotification(BaseNotification):
|
||||
comment = models.ForeignKey(Comment, on_delete=models.CASCADE, verbose_name=_('Antwort'))
|
||||
|
@ -259,6 +259,11 @@ a.btn, a.btn2, a.nav-link {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
font-size: medium;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
@ -7,7 +7,7 @@ from .mail import send_notification_email
|
||||
from .tools.admin import clean_locations, deactivate_unchecked_adoption_notices, deactivate_404_adoption_notices
|
||||
from .tools.misc import healthcheck_ok
|
||||
from .models import Location, AdoptionNotice, Timestamp
|
||||
from .tools.notifications import notify_moderators_of_AN_to_be_checked
|
||||
from .tools.notifications import notify_of_AN_to_be_checked
|
||||
from .tools.search import notify_search_subscribers
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ def post_adoption_notice_save(pk):
|
||||
logging.info(f"Location was added to Adoption notice {pk}")
|
||||
|
||||
notify_search_subscribers(instance, only_if_active=True)
|
||||
notify_moderators_of_AN_to_be_checked(instance)
|
||||
notify_of_AN_to_be_checked(instance)
|
||||
|
||||
@celery_app.task(name="tools.healthcheck")
|
||||
def task_healthcheck():
|
||||
|
@ -137,7 +137,6 @@ class GeoAPI:
|
||||
result = self.requests.get(self.api_url,
|
||||
{"q": location_string, "lang": language},
|
||||
headers=self.headers).json()
|
||||
logging.warning(result)
|
||||
geofeatures = GeoFeature.geofeatures_from_photon_result(result)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
@ -1,11 +1,13 @@
|
||||
from fellchensammlung.models import User, AdoptionNoticeNotification, TrustLevel
|
||||
|
||||
|
||||
def notify_moderators_of_AN_to_be_checked(adoption_notice):
|
||||
def notify_of_AN_to_be_checked(adoption_notice):
|
||||
if adoption_notice.is_disabled_unchecked:
|
||||
for moderator in User.objects.filter(trust_level__gt=TrustLevel.MODERATOR):
|
||||
users_to_notify = set(User.objects.filter(trust_level__gt=TrustLevel.MODERATOR))
|
||||
users_to_notify.add(adoption_notice.owner)
|
||||
for user in users_to_notify:
|
||||
AdoptionNoticeNotification.objects.create(adoption_notice=adoption_notice,
|
||||
user=moderator,
|
||||
user=user,
|
||||
title=f" Prüfe Vermittlung {adoption_notice}",
|
||||
text=f"{adoption_notice} muss geprüft werden bevor sie veröffentlicht wird.",
|
||||
)
|
@ -130,6 +130,8 @@ def adoption_notice_detail(request, adoption_notice_id):
|
||||
if action == "unsubscribe":
|
||||
subscription.delete()
|
||||
is_subscribed = False
|
||||
elif action == "subscribe":
|
||||
return redirect_to_login(next=request.path)
|
||||
else:
|
||||
raise PermissionDenied
|
||||
else:
|
||||
@ -481,13 +483,11 @@ def my_profile(request):
|
||||
notification = CommentNotification.objects.get(pk=notification_id)
|
||||
except CommentNotification.DoesNotExist:
|
||||
notification = BaseNotification.objects.get(pk=notification_id)
|
||||
notification.read = True
|
||||
notification.save()
|
||||
notification.mark_read()
|
||||
elif action == "notification_mark_all_read":
|
||||
notifications = CommentNotification.objects.filter(user=request.user, mark_read=False)
|
||||
for notification in notifications:
|
||||
notification.read = True
|
||||
notification.save()
|
||||
notification.mark_read()
|
||||
elif action == "search_subscription_delete":
|
||||
search_subscription_id = request.POST.get("search_subscription_id")
|
||||
SearchSubscription.objects.get(pk=search_subscription_id).delete()
|
||||
|
@ -7,21 +7,28 @@
|
||||
<p>{% translate "Dein Username oder Passwort ist falsch." %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
{% if user.is_authenticated %}
|
||||
<p>{% translate "Du bist bereits eingeloggt." %}</p>
|
||||
{% else %} {% if next %}
|
||||
<p>{% translate "Bitte log dich ein um diese Seite sehen zu können." %}</p>
|
||||
<p class="card">{% translate "Bitte log dich ein um diese Seite sehen zu können." %}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if not user.is_authenticated %}
|
||||
<form class="card" method="post" action="{% url 'login' %}">
|
||||
{% if not user.is_authenticated %}
|
||||
<div class="card">
|
||||
<div class="container-edit-buttons">
|
||||
<form method="post" action="{% url 'login' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input class="btn" type="submit" value={% translate "Einloggen" %} />
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
<input class="btn" type="submit" value="{% translate 'Einloggen' %}"/>
|
||||
<input type="hidden" name="next" value="{{ next }}"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p><a class="btn2" href="{% url 'password_reset' %}">{% translate "Passwort vergessen?" %}</a></p>
|
||||
{% endif %}
|
||||
<div class="container-edit-buttons">
|
||||
<a class="btn btn-small" href="{% url 'password_reset' %}">{% translate "Passwort vergessen?" %}</a>
|
||||
<a class="btn btn-small" href="{% url 'django_registration_register' %}">{% translate "Registrieren" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -27,7 +27,6 @@ class DistanceTest(TestCase):
|
||||
l_stuttgart = LocationProxy("Stuttgart")
|
||||
l_tue = LocationProxy("Tübingen")
|
||||
# Should be 30km
|
||||
print(f"{l_stuttgart.position} -> {l_tue.position}")
|
||||
distance_tue_stuttgart = calculate_distance_between_coordinates(l_stuttgart.position, l_tue.position)
|
||||
self.assertLess(distance_tue_stuttgart, 50)
|
||||
self.assertGreater(distance_tue_stuttgart, 20)
|
||||
|
@ -4,7 +4,7 @@ from django.utils import timezone
|
||||
from django.test import TestCase
|
||||
from model_bakery import baker
|
||||
|
||||
from fellchensammlung.models import Announcement, Language, User, TrustLevel
|
||||
from fellchensammlung.models import Announcement, Language, User, TrustLevel, BaseNotification
|
||||
|
||||
|
||||
class UserTest(TestCase):
|
||||
@ -77,3 +77,21 @@ class AnnouncementTest(TestCase):
|
||||
self.assertTrue(self.announcement2 not in active_announcements)
|
||||
self.assertTrue(self.announcement4 not in active_announcements)
|
||||
self.assertTrue(self.announcement5 in active_announcements)
|
||||
|
||||
|
||||
class TestNotifications(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.test_user_1 = User.objects.create(username="Testuser1", password="SUPERSECRET", email="test@example.org")
|
||||
|
||||
def test_mark_read(self):
|
||||
not1 = BaseNotification.objects.create(user=self.test_user_1, text="New rats to adopt", title="🔔 New Rat alert")
|
||||
not2 = BaseNotification.objects.create(user=self.test_user_1,
|
||||
text="New wombat to adopt", title="🔔 New Wombat alert")
|
||||
not1.mark_read()
|
||||
|
||||
self.assertTrue(not1.read)
|
||||
self.assertFalse(not2.read)
|
||||
self.assertTrue((timezone.now() - timedelta(hours=1)) < not1.read_at < timezone.now())
|
||||
self.assertIsNone(not2.read_at)
|
||||
|
||||
|
34
src/tests/test_tools_notifications.py
Normal file
34
src/tests/test_tools_notifications.py
Normal file
@ -0,0 +1,34 @@
|
||||
from django.test import TestCase
|
||||
from model_bakery import baker
|
||||
|
||||
from fellchensammlung.models import User, TrustLevel, Species, Location, AdoptionNotice, AdoptionNoticeNotification
|
||||
from fellchensammlung.tools.notifications import notify_of_AN_to_be_checked
|
||||
|
||||
|
||||
class TestNotifications(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.test_user0 = User.objects.create_user(username='testuser0',
|
||||
first_name="Admin",
|
||||
last_name="BOFH",
|
||||
password='12345')
|
||||
|
||||
cls.test_user1 = User.objects.create_user(username='testuser1',
|
||||
first_name="Max",
|
||||
last_name="Müller",
|
||||
password='12345')
|
||||
cls.test_user2 = User.objects.create_user(username='testuser2',
|
||||
first_name="Miriam",
|
||||
last_name="Müller",
|
||||
password='12345')
|
||||
cls.test_user0.trust_level = TrustLevel.ADMIN
|
||||
cls.test_user0.save()
|
||||
|
||||
cls.adoption1 = baker.make(AdoptionNotice, name="TestAdoption1", owner=cls.test_user1,)
|
||||
cls.adoption1.set_unchecked() # Could also emit notification
|
||||
|
||||
def test_notify_of_AN_to_be_checked(self):
|
||||
notify_of_AN_to_be_checked(self.adoption1)
|
||||
self.assertTrue(AdoptionNoticeNotification.objects.filter(user=self.test_user0).exists())
|
||||
self.assertTrue(AdoptionNoticeNotification.objects.filter(user=self.test_user1).exists())
|
||||
self.assertFalse(AdoptionNoticeNotification.objects.filter(user=self.test_user2).exists())
|
0
src/tests/test_views/__init__.py
Normal file
0
src/tests/test_views/__init__.py
Normal file
@ -4,7 +4,8 @@ from django.urls import reverse
|
||||
|
||||
from model_bakery import baker
|
||||
|
||||
from fellchensammlung.models import Animal, Species, AdoptionNotice, User, Location, AdoptionNoticeStatus, TrustLevel
|
||||
from fellchensammlung.models import Animal, Species, AdoptionNotice, User, Location, AdoptionNoticeStatus, TrustLevel, \
|
||||
Animal, Subscriptions
|
||||
from fellchensammlung.views import add_adoption_notice
|
||||
|
||||
|
||||
@ -70,7 +71,33 @@ class AnimalAndAdoptionTest(TestCase):
|
||||
|
||||
self.assertTrue(response.status_code < 400)
|
||||
self.assertTrue(AdoptionNotice.objects.get(name="TestAdoption4").is_active)
|
||||
an = AdoptionNotice.objects.get(name="TestAdoption4")
|
||||
animals = Animal.objects.filter(adoption_notice=an)
|
||||
self.assertTrue(len(animals) == 2)
|
||||
|
||||
def test_creating_AN_as_user(self):
|
||||
self.client.login(username='testuser1', password='12345')
|
||||
|
||||
form_data = {"name": "TestAdoption5",
|
||||
"species": Species.objects.first().pk,
|
||||
"num_animals": "3",
|
||||
"date_of_birth": "2024-12-04",
|
||||
"sex": "M",
|
||||
"group_only": "on",
|
||||
"searching_since": "2024-11-10",
|
||||
"location_string": "München",
|
||||
"description": "Blaaaa",
|
||||
"further_information": "https://notfellchen.org/",
|
||||
"save-and-add-another-animal": "Speichern"}
|
||||
|
||||
response = self.client.post(reverse('add-adoption'), data=form_data)
|
||||
|
||||
self.assertTrue(response.status_code < 400)
|
||||
self.assertFalse(AdoptionNotice.objects.get(name="TestAdoption5").is_active)
|
||||
an = AdoptionNotice.objects.get(name="TestAdoption5")
|
||||
animals = Animal.objects.filter(adoption_notice=an)
|
||||
self.assertTrue(len(animals) == 3)
|
||||
self.assertTrue(an.sexes == set("M", ))
|
||||
|
||||
|
||||
class SearchTest(TestCase):
|
||||
@ -196,3 +223,64 @@ class UpdateQueueTest(TestCase):
|
||||
self.assertFalse(self.adoption3.is_active)
|
||||
self.assertEqual(self.adoption3.adoptionnoticestatus.major_status, AdoptionNoticeStatus.CLOSED)
|
||||
|
||||
|
||||
class AdoptionDetailTest(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
test_user0 = User.objects.create_user(username='testuser0',
|
||||
first_name="Admin",
|
||||
last_name="BOFH",
|
||||
password='12345')
|
||||
test_user0.save()
|
||||
|
||||
test_user1 = User.objects.create_user(username='testuser1',
|
||||
first_name="Max",
|
||||
last_name="Müller",
|
||||
password='12345')
|
||||
test_user0.trust_level = TrustLevel.ADMIN
|
||||
test_user0.save()
|
||||
|
||||
adoption1 = baker.make(AdoptionNotice, name="TestAdoption1")
|
||||
adoption2 = baker.make(AdoptionNotice, name="TestAdoption2")
|
||||
adoption3 = baker.make(AdoptionNotice, name="TestAdoption3")
|
||||
|
||||
berlin = Location.get_location_from_string("Berlin")
|
||||
adoption1.location = berlin
|
||||
adoption1.save()
|
||||
|
||||
stuttgart = Location.get_location_from_string("Stuttgart")
|
||||
adoption3.location = stuttgart
|
||||
adoption3.save()
|
||||
|
||||
adoption1.set_active()
|
||||
adoption3.set_active()
|
||||
adoption2.set_unchecked()
|
||||
|
||||
|
||||
def test_subscribe(self):
|
||||
self.client.login(username='testuser0', password='12345')
|
||||
response = self.client.post(
|
||||
reverse('adoption-notice-detail', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)),
|
||||
data={"action": "subscribe"})
|
||||
self.assertTrue(Subscriptions.objects.filter(owner__username="testuser0").exists())
|
||||
|
||||
|
||||
def test_unsubscribe(self):
|
||||
# Make sure subscription exists
|
||||
an = AdoptionNotice.objects.get(name="TestAdoption1")
|
||||
user = User.objects.get(username="testuser0")
|
||||
subscription = Subscriptions.objects.get_or_create(owner=user, adoption_notice=an)
|
||||
|
||||
# Unsubscribe
|
||||
self.client.login(username='testuser0', password='12345')
|
||||
response = self.client.post(
|
||||
reverse('adoption-notice-detail', args=str(an.pk)),
|
||||
data={"action": "unsubscribe"})
|
||||
self.assertFalse(Subscriptions.objects.filter(owner__username="testuser0").exists())
|
||||
|
||||
def test_login_required(self):
|
||||
response = self.client.post(
|
||||
reverse('adoption-notice-detail', args=str(AdoptionNotice.objects.get(name="TestAdoption1").pk)),
|
||||
data={"action": "subscribe"})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/accounts/login/?next=/vermittlung/1/")
|
43
src/tests/test_views/test_basic_views.py
Normal file
43
src/tests/test_views/test_basic_views.py
Normal file
@ -0,0 +1,43 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from fellchensammlung.models import User, TrustLevel, AdoptionNotice, Species
|
||||
from model_bakery import baker
|
||||
|
||||
|
||||
class BasicViewTest(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
test_user0 = User.objects.create_user(username='testuser0',
|
||||
first_name="Admin",
|
||||
last_name="BOFH",
|
||||
password='12345')
|
||||
|
||||
test_user1 = User.objects.create_user(username='testuser1',
|
||||
first_name="Max",
|
||||
last_name="Müller",
|
||||
password='12345')
|
||||
test_user0.trust_level = TrustLevel.ADMIN
|
||||
test_user0.save()
|
||||
|
||||
ans = []
|
||||
for i in range(0, 8):
|
||||
ans.append(baker.make(AdoptionNotice, name=f"TestAdoption{i}"))
|
||||
for i in range(0, 4):
|
||||
AdoptionNotice.objects.get(name=f"TestAdoption{i}").set_active()
|
||||
|
||||
def test_index_logged_in(self):
|
||||
self.client.login(username='testuser0', password='12345')
|
||||
|
||||
response = self.client.get(reverse('index'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Check our user is logged in
|
||||
self.assertEqual(str(response.context['user']), 'testuser0')
|
||||
self.assertContains(response, "TestAdoption0")
|
||||
self.assertNotContains(response, "TestAdoption5") # Should not be active, therefore not shown
|
||||
|
||||
def test_index_anonymous(self):
|
||||
response = self.client.get(reverse('index'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "TestAdoption1")
|
||||
self.assertNotContains(response, "TestAdoption4") # Should not be active, therefore not shown
|
Loading…
x
Reference in New Issue
Block a user