diff --git a/bookwyrm/templates/ostatus/success.html b/bookwyrm/templates/ostatus/success.html
index 66577e83f6..c2b8edd75b 100644
--- a/bookwyrm/templates/ostatus/success.html
+++ b/bookwyrm/templates/ostatus/success.html
@@ -2,6 +2,10 @@
{% load i18n %}
{% load utilities %}
+{% block title %}
+{% blocktrans with display_name=user.display_name %}You are now following {{ display_name }}!{% endblocktrans %}
+{% endblock %}
+
{% block content %}
diff --git a/bookwyrm/tests/views/test_search.py b/bookwyrm/tests/views/test_search.py
index 6c7e41cf31..3b3e78e178 100644
--- a/bookwyrm/tests/views/test_search.py
+++ b/bookwyrm/tests/views/test_search.py
@@ -103,6 +103,20 @@ def test_search_books(self):
connector_results = response.context_data["remote_results"]
self.assertEqual(connector_results[0]["results"][0].title, "Mock Book")
+ def test_search_books_extra_whitespace(self):
+ """just the search page"""
+ view = views.Search.as_view()
+ request = self.factory.get("", {"q": " Test Book ", "remote": False})
+ request.user = self.local_user
+ with patch("bookwyrm.views.search.is_api_request") as is_api:
+ is_api.return_value = False
+ response = view(request)
+ self.assertIsInstance(response, TemplateResponse)
+ validate_html(response.render())
+
+ local_results = response.context_data["results"]
+ self.assertEqual(local_results[0].title, "Test Book")
+
def test_search_book_anonymous(self):
"""Don't search remote for logged out user"""
view = views.Search.as_view()
@@ -150,6 +164,17 @@ def test_search_users(self):
validate_html(response.render())
self.assertEqual(response.context_data["results"][0], self.local_user)
+ def test_search_users_extra_whitespace(self):
+ """searches remote connectors"""
+ view = views.Search.as_view()
+ request = self.factory.get("", {"q": " mouse ", "type": "user"})
+ request.user = self.local_user
+ response = view(request)
+
+ self.assertIsInstance(response, TemplateResponse)
+ validate_html(response.render())
+ self.assertEqual(response.context_data["results"][0], self.local_user)
+
def test_search_users_logged_out(self):
"""searches remote connectors"""
view = views.Search.as_view()
@@ -181,3 +206,21 @@ def test_search_lists(self):
self.assertIsInstance(response, TemplateResponse)
validate_html(response.render())
self.assertEqual(response.context_data["results"][0], booklist)
+
+ def test_search_lists_extra_whitespace(self):
+ """searches remote connectors"""
+ with (
+ patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"),
+ patch("bookwyrm.lists_stream.remove_list_task.delay"),
+ ):
+ booklist = models.List.objects.create(
+ user=self.local_user, name="test list"
+ )
+ view = views.Search.as_view()
+ request = self.factory.get("", {"q": " test ", "type": "list"})
+ request.user = self.local_user
+ response = view(request)
+
+ self.assertIsInstance(response, TemplateResponse)
+ validate_html(response.render())
+ self.assertEqual(response.context_data["results"][0], booklist)
diff --git a/bookwyrm/tests/views/test_status.py b/bookwyrm/tests/views/test_status.py
index 52582a2357..af8a6dc3fc 100644
--- a/bookwyrm/tests/views/test_status.py
+++ b/bookwyrm/tests/views/test_status.py
@@ -1,9 +1,11 @@
""" test for app action functionality """
import json
from unittest.mock import patch
+import dateutil
from django.core.exceptions import PermissionDenied
from django.test import TestCase, TransactionTestCase
from django.test.client import RequestFactory
+from django.utils import timezone
from bookwyrm import forms, models, views
from bookwyrm.views.status import find_mentions, find_or_create_hashtags
@@ -167,6 +169,37 @@ def test_create_status_rating(self, *_):
self.assertEqual(status.rating, 4.0)
self.assertIsNone(status.edited_date)
+ def test_create_status_progress(self, *_):
+ """create a status that updates a readthrough"""
+ start_date = timezone.make_aware(dateutil.parser.parse("2024-07-27"))
+ readthrough = models.ReadThrough.objects.create(
+ book=self.book, user=self.local_user, start_date=start_date
+ )
+
+ self.assertEqual(start_date, readthrough.start_date)
+ self.assertIsNone(readthrough.progress)
+
+ view = views.CreateStatus.as_view()
+ form = forms.CommentForm(
+ {
+ "progress": 1,
+ "progress_mode": "PG",
+ "content": "I started the book",
+ "id": readthrough.id,
+ "book": self.book.id,
+ "user": self.local_user.id,
+ "privacy": "public",
+ }
+ )
+ request = self.factory.post("", form.data)
+ request.user = self.local_user
+
+ view(request, "comment")
+ readthrough.refresh_from_db()
+
+ self.assertEqual(1, readthrough.progress)
+ self.assertEqual(start_date, readthrough.start_date) # not overwritten
+
def test_create_status_wrong_user(self, *_):
"""You can't compose statuses for someone else"""
view = views.CreateStatus.as_view()
diff --git a/bookwyrm/views/search.py b/bookwyrm/views/search.py
index 13695a7d40..95845db640 100644
--- a/bookwyrm/views/search.py
+++ b/bookwyrm/views/search.py
@@ -53,7 +53,7 @@ def get(self, request):
def api_book_search(request):
"""Return books via API response"""
- query = request.GET.get("q")
+ query = request.GET.get("q").strip()
query = isbn_check_and_format(query)
min_confidence = request.GET.get("min_confidence", 0)
# only return local book results via json so we don't cascade
@@ -65,7 +65,7 @@ def api_book_search(request):
def book_search(request):
"""the real business is elsewhere"""
- query = request.GET.get("q")
+ query = request.GET.get("q").strip()
# check if query is isbn
query = isbn_check_and_format(query)
min_confidence = request.GET.get("min_confidence", 0)
@@ -123,8 +123,7 @@ def author_search(request):
def user_search(request):
"""user search: search for a user"""
viewer = request.user
- query = request.GET.get("q")
- query = query.strip()
+ query = request.GET.get("q").strip()
data = {"type": "user", "query": query}
# use webfinger for mastodon style account@domain.com username to load the user if
@@ -162,7 +161,7 @@ def user_search(request):
def list_search(request):
"""any relevent lists?"""
- query = request.GET.get("q")
+ query = request.GET.get("q").strip()
data = {"query": query, "type": "list"}
results = (
models.List.privacy_filter(
diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py
index 1935c916b6..99401431ba 100644
--- a/bookwyrm/views/status.py
+++ b/bookwyrm/views/status.py
@@ -201,12 +201,11 @@ def edit_readthrough(request):
# TODO: remove this, it duplicates the code in the ReadThrough view
readthrough = get_object_or_404(models.ReadThrough, id=request.POST.get("id"))
- readthrough.start_date = load_date_in_user_tz_as_utc(
- request.POST.get("start_date"), request.user
- )
- readthrough.finish_date = load_date_in_user_tz_as_utc(
- request.POST.get("finish_date"), request.user
- )
+ if start_date := request.POST.get("start_date"):
+ readthrough.start_date = load_date_in_user_tz_as_utc(start_date, request.user)
+
+ if finish_date := request.POST.get("finish_date"):
+ readthrough.finish_date = load_date_in_user_tz_as_utc(finish_date, request.user)
progress = request.POST.get("progress")
try:
diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py
index 20f194a12e..3ca9b27486 100644
--- a/celerywyrm/settings.py
+++ b/celerywyrm/settings.py
@@ -15,8 +15,12 @@
f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}",
)
-CELERY_BROKER_URL = REDIS_BROKER_URL.replace("unix:", "redis+socket:")
-CELERY_RESULT_BACKEND = REDIS_BROKER_URL.replace("unix:", "redis+socket:")
+CELERY_BROKER_URL = env(
+ "CELERY_BROKER_URL", REDIS_BROKER_URL.replace("unix:", "redis+socket:")
+)
+CELERY_RESULT_BACKEND = env(
+ "CELERY_RESULT_BACKEND", REDIS_BROKER_URL.replace("unix:", "redis+socket:")
+)
CELERY_DEFAULT_QUEUE = "low_priority"
CELERY_CREATE_MISSING_QUEUES = True
diff --git a/pytest.ini b/pytest.ini
index b963fb316a..74970e877f 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -15,11 +15,8 @@ env =
ALLOWED_HOSTS = your.domain.here
BOOKWYRM_DATABASE_BACKEND = postgres
MEDIA_ROOT = images/
- CELERY_BROKER =
- REDIS_BROKER_PORT = 6379
- REDIS_BROKER_PASSWORD = beep
- REDIS_ACTIVITY_PORT = 6379
- REDIS_ACTIVITY_PASSWORD = beep
+ CELERY_BROKER_URL = memory://
+ CELERY_RESULT_BACKEND = cache+memory://
USE_DUMMY_CACHE = true
FLOWER_PORT = 8888
EMAIL_HOST = smtp.mailgun.org
diff --git a/requirements.txt b/requirements.txt
index 0c4d3cf7ee..64ef099c7d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,7 @@ boto3==1.34.74
bw-file-resubmit==0.6.0rc2
celery==5.3.6
colorthief==0.2.1
-Django==4.2.14
+Django==4.2.15
django-celery-beat==2.6.0
django-compressor==4.4
django-csp==3.8