forked from mirrors/bookwyrm
Merge pull request #880 from bookwyrm-social/suggestions-redis
Uses redis for storing suggested users
This commit is contained in:
commit
0a9fe4631c
90 changed files with 1363 additions and 915 deletions
|
@ -106,6 +106,7 @@ class ActivityObject:
|
|||
value = field.default
|
||||
setattr(self, field.name, value)
|
||||
|
||||
# pylint: disable=too-many-locals,too-many-branches
|
||||
def to_model(self, model=None, instance=None, allow_create=True, save=True):
|
||||
"""convert from an activity to a model instance"""
|
||||
model = model or get_model_from_type(self.type)
|
||||
|
@ -126,27 +127,36 @@ class ActivityObject:
|
|||
return None
|
||||
instance = instance or model()
|
||||
|
||||
# keep track of what we've changed
|
||||
update_fields = []
|
||||
for field in instance.simple_fields:
|
||||
try:
|
||||
field.set_field_from_activity(instance, self)
|
||||
changed = field.set_field_from_activity(instance, self)
|
||||
if changed:
|
||||
update_fields.append(field.name)
|
||||
except AttributeError as e:
|
||||
raise ActivitySerializerError(e)
|
||||
|
||||
# image fields have to be set after other fields because they can save
|
||||
# too early and jank up users
|
||||
for field in instance.image_fields:
|
||||
field.set_field_from_activity(instance, self, save=save)
|
||||
changed = field.set_field_from_activity(instance, self, save=save)
|
||||
if changed:
|
||||
update_fields.append(field.name)
|
||||
|
||||
if not save:
|
||||
return instance
|
||||
|
||||
with transaction.atomic():
|
||||
# can't force an update on fields unless the object already exists in the db
|
||||
if not instance.id:
|
||||
update_fields = None
|
||||
# we can't set many to many and reverse fields on an unsaved object
|
||||
try:
|
||||
try:
|
||||
instance.save(broadcast=False)
|
||||
instance.save(broadcast=False, update_fields=update_fields)
|
||||
except TypeError:
|
||||
instance.save()
|
||||
instance.save(update_fields=update_fields)
|
||||
except IntegrityError as e:
|
||||
raise ActivitySerializerError(e)
|
||||
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
""" Re-create user streams """
|
||||
from django.core.management.base import BaseCommand
|
||||
import redis
|
||||
|
||||
from bookwyrm import activitystreams, models, settings
|
||||
|
||||
r = redis.Redis(
|
||||
host=settings.REDIS_ACTIVITY_HOST, port=settings.REDIS_ACTIVITY_PORT, db=0
|
||||
)
|
||||
from bookwyrm import activitystreams, models
|
||||
|
||||
|
||||
def populate_streams():
|
||||
|
|
25
bookwyrm/management/commands/populate_suggestions.py
Normal file
25
bookwyrm/management/commands/populate_suggestions.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
""" Populate suggested users """
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.suggested_users import rerank_suggestions_task
|
||||
|
||||
|
||||
def populate_suggestions():
|
||||
"""build all the streams for all the users"""
|
||||
users = models.User.objects.filter(
|
||||
local=True,
|
||||
is_active=True,
|
||||
).values_list("id", flat=True)
|
||||
for user in users:
|
||||
rerank_suggestions_task.delay(user)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""start all over with user suggestions"""
|
||||
|
||||
help = "Populate suggested users for all users"
|
||||
# pylint: disable=no-self-use,unused-argument
|
||||
def handle(self, *args, **options):
|
||||
"""run builder"""
|
||||
populate_suggestions()
|
|
@ -30,7 +30,7 @@ class Favorite(ActivityMixin, BookWyrmModel):
|
|||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
self.user.last_active_date = timezone.now()
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
if self.status.user.local and self.status.user != self.user:
|
||||
|
|
|
@ -67,7 +67,7 @@ class ActivitypubFieldMixin:
|
|||
super().__init__(*args, **kwargs)
|
||||
|
||||
def set_field_from_activity(self, instance, data):
|
||||
"""helper function for assinging a value to the field"""
|
||||
"""helper function for assinging a value to the field. Returns if changed"""
|
||||
try:
|
||||
value = getattr(data, self.get_activitypub_field())
|
||||
except AttributeError:
|
||||
|
@ -77,8 +77,14 @@ class ActivitypubFieldMixin:
|
|||
value = getattr(data, "actor")
|
||||
formatted = self.field_from_activity(value)
|
||||
if formatted is None or formatted is MISSING or formatted == {}:
|
||||
return
|
||||
return False
|
||||
|
||||
# the field is unchanged
|
||||
if hasattr(instance, self.name) and getattr(instance, self.name) == formatted:
|
||||
return False
|
||||
|
||||
setattr(instance, self.name, formatted)
|
||||
return True
|
||||
|
||||
def set_activity_from_field(self, activity, instance):
|
||||
"""update the json object"""
|
||||
|
@ -205,6 +211,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
|||
|
||||
# pylint: disable=invalid-name
|
||||
def set_field_from_activity(self, instance, data):
|
||||
original = getattr(instance, self.name)
|
||||
to = data.to
|
||||
cc = data.cc
|
||||
if to == [self.public]:
|
||||
|
@ -215,6 +222,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
|||
setattr(instance, self.name, "unlisted")
|
||||
else:
|
||||
setattr(instance, self.name, "followers")
|
||||
return original == getattr(instance, self.name)
|
||||
|
||||
def set_activity_from_field(self, activity, instance):
|
||||
# explicitly to anyone mentioned (statuses only)
|
||||
|
@ -270,9 +278,10 @@ class ManyToManyField(ActivitypubFieldMixin, models.ManyToManyField):
|
|||
value = getattr(data, self.get_activitypub_field())
|
||||
formatted = self.field_from_activity(value)
|
||||
if formatted is None or formatted is MISSING:
|
||||
return
|
||||
return False
|
||||
getattr(instance, self.name).set(formatted)
|
||||
instance.save(broadcast=False)
|
||||
return True
|
||||
|
||||
def field_to_activity(self, value):
|
||||
if self.link_only:
|
||||
|
@ -373,8 +382,10 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
|
|||
value = getattr(data, self.get_activitypub_field())
|
||||
formatted = self.field_from_activity(value)
|
||||
if formatted is None or formatted is MISSING:
|
||||
return
|
||||
return False
|
||||
|
||||
getattr(instance, self.name).save(*formatted, save=save)
|
||||
return True
|
||||
|
||||
def set_activity_from_field(self, activity, instance):
|
||||
value = getattr(instance, self.name)
|
||||
|
|
|
@ -30,7 +30,7 @@ class ReadThrough(BookWyrmModel):
|
|||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
self.user.last_active_date = timezone.now()
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def create_update(self):
|
||||
|
@ -55,5 +55,5 @@ class ProgressUpdate(BookWyrmModel):
|
|||
def save(self, *args, **kwargs):
|
||||
"""update user active time"""
|
||||
self.user.last_active_date = timezone.now()
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
super().save(*args, **kwargs)
|
||||
|
|
|
@ -243,7 +243,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
|||
# generate a username that uses the domain (webfinger format)
|
||||
actor_parts = urlparse(self.remote_id)
|
||||
self.username = "%s@%s" % (self.username, actor_parts.netloc)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# this user already exists, no need to populate fields
|
||||
if not created:
|
||||
|
@ -276,7 +275,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
|||
self.key_pair = KeyPair.objects.create(
|
||||
remote_id="%s/#main-key" % self.remote_id
|
||||
)
|
||||
self.save(broadcast=False)
|
||||
self.save(broadcast=False, update_fields=["key_pair"])
|
||||
|
||||
shelves = [
|
||||
{
|
||||
|
@ -406,7 +405,7 @@ def set_remote_server(user_id):
|
|||
user = User.objects.get(id=user_id)
|
||||
actor_parts = urlparse(user.remote_id)
|
||||
user.federated_server = get_or_create_remote_server(actor_parts.netloc)
|
||||
user.save(broadcast=False)
|
||||
user.save(broadcast=False, update_fields=["federated_server"])
|
||||
if user.bookwyrm_user and user.outbox:
|
||||
get_remote_reviews.delay(user.outbox)
|
||||
|
||||
|
|
|
@ -338,9 +338,9 @@ def save_and_cleanup(image, instance=None):
|
|||
|
||||
save_without_broadcast = isinstance(instance, (models.Book, models.User))
|
||||
if save_without_broadcast:
|
||||
instance.save(broadcast=False)
|
||||
instance.save(broadcast=False, update_fields=["preview_image"])
|
||||
else:
|
||||
instance.save()
|
||||
instance.save(update_fields=["preview_image"])
|
||||
|
||||
# Clean up old file after saving
|
||||
if old_path and default_storage.exists(old_path):
|
||||
|
|
|
@ -50,15 +50,15 @@ class RedisStore(ABC):
|
|||
pipeline.execute()
|
||||
|
||||
def bulk_remove_objects_from_store(self, objs, store):
|
||||
"""remoev a list of objects from a given store"""
|
||||
"""remove a list of objects from a given store"""
|
||||
pipeline = r.pipeline()
|
||||
for obj in objs[: self.max_length]:
|
||||
pipeline.zrem(store, -1, obj.id)
|
||||
pipeline.execute()
|
||||
|
||||
def get_store(self, store): # pylint: disable=no-self-use
|
||||
def get_store(self, store, **kwargs): # pylint: disable=no-self-use
|
||||
"""load the values in a store"""
|
||||
return r.zrevrange(store, 0, -1)
|
||||
return r.zrevrange(store, 0, -1, **kwargs)
|
||||
|
||||
def populate_store(self, store):
|
||||
"""go from zero to a store"""
|
||||
|
|
221
bookwyrm/suggested_users.py
Normal file
221
bookwyrm/suggested_users.py
Normal file
|
@ -0,0 +1,221 @@
|
|||
""" store recommended follows in redis """
|
||||
import math
|
||||
import logging
|
||||
from django.dispatch import receiver
|
||||
from django.db.models import signals, Count, Q
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.redis_store import RedisStore, r
|
||||
from bookwyrm.tasks import app
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SuggestedUsers(RedisStore):
|
||||
"""suggested users for a user"""
|
||||
|
||||
max_length = 30
|
||||
|
||||
def get_rank(self, obj):
|
||||
"""get computed rank"""
|
||||
return obj.mutuals + (1.0 - (1.0 / (obj.shared_books + 1)))
|
||||
|
||||
def store_id(self, user): # pylint: disable=no-self-use
|
||||
"""the key used to store this user's recs"""
|
||||
if isinstance(user, int):
|
||||
return "{:d}-suggestions".format(user)
|
||||
return "{:d}-suggestions".format(user.id)
|
||||
|
||||
def get_counts_from_rank(self, rank): # pylint: disable=no-self-use
|
||||
"""calculate mutuals count and shared books count from rank"""
|
||||
return {
|
||||
"mutuals": math.floor(rank),
|
||||
"shared_books": int(1 / (-1 * (rank % 1 - 1))) - 1,
|
||||
}
|
||||
|
||||
def get_objects_for_store(self, store):
|
||||
"""a list of potential follows for a user"""
|
||||
user = models.User.objects.get(id=store.split("-")[0])
|
||||
|
||||
return get_annotated_users(
|
||||
user,
|
||||
~Q(id=user.id),
|
||||
~Q(followers=user),
|
||||
~Q(follower_requests=user),
|
||||
bookwyrm_user=True,
|
||||
)
|
||||
|
||||
def get_stores_for_object(self, obj):
|
||||
return [self.store_id(u) for u in self.get_users_for_object(obj)]
|
||||
|
||||
def get_users_for_object(self, obj): # pylint: disable=no-self-use
|
||||
"""given a user, who might want to follow them"""
|
||||
return models.User.objects.filter(local=True,).exclude(
|
||||
Q(id=obj.id) | Q(followers=obj) | Q(id__in=obj.blocks.all()) | Q(blocks=obj)
|
||||
)
|
||||
|
||||
def rerank_obj(self, obj, update_only=True):
|
||||
"""update all the instances of this user with new ranks"""
|
||||
pipeline = r.pipeline()
|
||||
for store_user in self.get_users_for_object(obj):
|
||||
annotated_user = get_annotated_users(
|
||||
store_user,
|
||||
id=obj.id,
|
||||
).first()
|
||||
if not annotated_user:
|
||||
continue
|
||||
|
||||
pipeline.zadd(
|
||||
self.store_id(store_user),
|
||||
self.get_value(annotated_user),
|
||||
xx=update_only,
|
||||
)
|
||||
pipeline.execute()
|
||||
|
||||
def rerank_user_suggestions(self, user):
|
||||
"""update the ranks of the follows suggested to a user"""
|
||||
self.populate_store(self.store_id(user))
|
||||
|
||||
def remove_suggestion(self, user, suggested_user):
|
||||
"""take a user out of someone's suggestions"""
|
||||
self.bulk_remove_objects_from_store([suggested_user], self.store_id(user))
|
||||
|
||||
def get_suggestions(self, user):
|
||||
"""get suggestions"""
|
||||
values = self.get_store(self.store_id(user), withscores=True)
|
||||
results = []
|
||||
# annotate users with mutuals and shared book counts
|
||||
for user_id, rank in values[:5]:
|
||||
counts = self.get_counts_from_rank(rank)
|
||||
try:
|
||||
user = models.User.objects.get(id=user_id)
|
||||
except models.User.DoesNotExist as err:
|
||||
# if this happens, the suggestions are janked way up
|
||||
logger.exception(err)
|
||||
continue
|
||||
user.mutuals = counts["mutuals"]
|
||||
user.shared_books = counts["shared_books"]
|
||||
results.append(user)
|
||||
return results
|
||||
|
||||
|
||||
def get_annotated_users(viewer, *args, **kwargs):
|
||||
"""Users, annotated with things they have in common"""
|
||||
return (
|
||||
models.User.objects.filter(discoverable=True, is_active=True, *args, **kwargs)
|
||||
.exclude(Q(id__in=viewer.blocks.all()) | Q(blocks=viewer))
|
||||
.annotate(
|
||||
mutuals=Count(
|
||||
"followers",
|
||||
filter=Q(
|
||||
~Q(id=viewer.id),
|
||||
~Q(id__in=viewer.following.all()),
|
||||
followers__in=viewer.following.all(),
|
||||
),
|
||||
distinct=True,
|
||||
),
|
||||
shared_books=Count(
|
||||
"shelfbook",
|
||||
filter=Q(
|
||||
~Q(id=viewer.id),
|
||||
shelfbook__book__parent_work__in=[
|
||||
s.book.parent_work for s in viewer.shelfbook_set.all()
|
||||
],
|
||||
),
|
||||
distinct=True,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
suggested_users = SuggestedUsers()
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=models.UserFollows)
|
||||
# pylint: disable=unused-argument
|
||||
def update_suggestions_on_follow(sender, instance, created, *args, **kwargs):
|
||||
"""remove a follow from the recs and update the ranks"""
|
||||
if not created or not instance.user_object.discoverable:
|
||||
return
|
||||
|
||||
if instance.user_subject.local:
|
||||
remove_suggestion_task.delay(instance.user_subject.id, instance.user_object.id)
|
||||
rerank_user_task.delay(instance.user_object.id, update_only=False)
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=models.UserBlocks)
|
||||
# pylint: disable=unused-argument
|
||||
def update_suggestions_on_block(sender, instance, *args, **kwargs):
|
||||
"""remove blocked users from recs"""
|
||||
if instance.user_subject.local and instance.user_object.discoverable:
|
||||
remove_suggestion_task.delay(instance.user_subject.id, instance.user_object.id)
|
||||
if instance.user_object.local and instance.user_subject.discoverable:
|
||||
remove_suggestion_task.delay(instance.user_object.id, instance.user_subject.id)
|
||||
|
||||
|
||||
@receiver(signals.post_delete, sender=models.UserFollows)
|
||||
# pylint: disable=unused-argument
|
||||
def update_suggestions_on_unfollow(sender, instance, **kwargs):
|
||||
"""update rankings, but don't re-suggest because it was probably intentional"""
|
||||
if instance.user_object.discoverable:
|
||||
rerank_user_task.delay(instance.user_object.id, update_only=False)
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=models.ShelfBook)
|
||||
@receiver(signals.post_delete, sender=models.ShelfBook)
|
||||
# pylint: disable=unused-argument
|
||||
def update_rank_on_shelving(sender, instance, *args, **kwargs):
|
||||
"""when a user shelves or unshelves a book, re-compute their rank"""
|
||||
# if it's a local user, re-calculate who is rec'ed to them
|
||||
if instance.user.local:
|
||||
rerank_suggestions_task.delay(instance.user.id)
|
||||
|
||||
# if the user is discoverable, update their rankings
|
||||
if instance.user.discoverable:
|
||||
rerank_user_task.delay(instance.user.id)
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=models.User)
|
||||
# pylint: disable=unused-argument, too-many-arguments
|
||||
def add_new_user(sender, instance, created, update_fields=None, **kwargs):
|
||||
"""a new user, wow how cool"""
|
||||
# a new user is found, create suggestions for them
|
||||
if created and instance.local:
|
||||
rerank_suggestions_task.delay(instance.id)
|
||||
|
||||
if update_fields and not "discoverable" in update_fields:
|
||||
return
|
||||
|
||||
# this happens on every save, not just when discoverability changes, annoyingly
|
||||
if instance.discoverable:
|
||||
rerank_user_task.delay(instance.id, update_only=False)
|
||||
elif not created:
|
||||
remove_user_task.delay(instance.id)
|
||||
|
||||
|
||||
@app.task
|
||||
def rerank_suggestions_task(user_id):
|
||||
"""do the hard work in celery"""
|
||||
suggested_users.rerank_user_suggestions(user_id)
|
||||
|
||||
|
||||
@app.task
|
||||
def rerank_user_task(user_id, update_only=False):
|
||||
"""do the hard work in celery"""
|
||||
user = models.User.objects.get(id=user_id)
|
||||
suggested_users.rerank_obj(user, update_only=update_only)
|
||||
|
||||
|
||||
@app.task
|
||||
def remove_user_task(user_id):
|
||||
"""do the hard work in celery"""
|
||||
user = models.User.objects.get(id=user_id)
|
||||
suggested_users.remove_object_from_related_stores(user)
|
||||
|
||||
|
||||
@app.task
|
||||
def remove_suggestion_task(user_id, suggested_user_id):
|
||||
"""remove a specific user from a specific user's suggestions"""
|
||||
suggested_user = models.User.objects.get(id=suggested_user_id)
|
||||
suggested_users.remove_suggestion(user_id, suggested_user)
|
|
@ -44,18 +44,22 @@
|
|||
|
||||
{# activity feed #}
|
||||
{% if not activities %}
|
||||
<p>{% trans "There aren't any activities right now! Try following a user to get started" %}</p>
|
||||
<div class="block content">
|
||||
<p>{% trans "There aren't any activities right now! Try following a user to get started" %}</p>
|
||||
|
||||
{% if suggested_users %}
|
||||
{# suggested users for when things are very lonely #}
|
||||
{% include 'feed/suggested_users.html' with suggested_users=suggested_users %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% for activity in activities %}
|
||||
|
||||
{% if not activities.number > 1 and forloop.counter0 == 2 and suggested_users %}
|
||||
{# suggested users on the first page, two statuses down #}
|
||||
<section class="block">
|
||||
<h2 class="title is-5">{% trans "Who to follow" %}</h2>
|
||||
{% include 'feed/suggested_users.html' with suggested_users=suggested_users %}
|
||||
<a class="help" href="{% url 'directory' %}">View directory <span class="icon icon-arrow-right"></a>
|
||||
</section>
|
||||
{% include 'feed/suggested_users.html' with suggested_users=suggested_users %}
|
||||
{% endif %}
|
||||
<div class="block">
|
||||
{% include 'snippets/status/status.html' with status=activity %}
|
||||
|
|
|
@ -1,25 +1,6 @@
|
|||
{% load i18n %}
|
||||
{% load utilities %}
|
||||
{% load humanize %}
|
||||
<div class="columns is-mobile scroll-x mb-0">
|
||||
{% for user in suggested_users %}
|
||||
<div class="column is-flex is-flex-grow-0">
|
||||
<div class="box has-text-centered is-shadowless has-background-white-bis m-0">
|
||||
<a href="{{ user.local_path }}" class="has-text-black">
|
||||
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||
<span title="{{ user.display_name }}" class="is-block is-6 has-text-weight-bold">{{ user.display_name|truncatechars:10 }}</span>
|
||||
<span title="@{{ user|username }}" class="is-block pb-3">@{{ user|username|truncatechars:8 }}</span>
|
||||
</a>
|
||||
{% include 'snippets/follow_button.html' with user=user minimal=True %}
|
||||
{% if user.mutuals %}
|
||||
<p class="help">
|
||||
{% blocktrans with mutuals=user.mutuals|intcomma count counter=user.mutuals %}{{ mutuals }} follower you follow{% plural %}{{ mutuals }} followers you follow{% endblocktrans %}
|
||||
</p>
|
||||
{% elif user.shared_books %}
|
||||
<p class="help">{% blocktrans with shared_books=user.shared_books|intcomma count counter=user.shared_books %}{{ shared_books }} book on your shelves{% plural %}{{ shared_books }} books on your shelves{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<section class="block">
|
||||
<h2 class="title is-5">{% trans "Who to follow" %}</h2>
|
||||
{% include 'snippets/suggested_users.html' with suggested_users=suggested_users %}
|
||||
<a class="help" href="{% url 'directory' %}">View directory <span class="icon icon-arrow-right"></a>
|
||||
</section>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<form class="field has-addons" method="get" action="{% url 'get-started-users' %}">
|
||||
<div class="control">
|
||||
<input type="text" name="query" value="{{ request.GET.query }}" class="input" placeholder="{% trans 'Search for a user' %}" aria-label="{% trans 'Search for a user' %}">
|
||||
{% if request.GET.query and not user_results %}
|
||||
{% if request.GET.query and no_results %}
|
||||
<p class="help">{% blocktrans with query=request.GET.query %}No users found for "{{ query }}"{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
{% include 'feed/suggested_users.html' with suggested_users=suggested_users %}
|
||||
{% include 'snippets/suggested_users.html' with suggested_users=suggested_users %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
25
bookwyrm/templates/snippets/suggested_users.html
Normal file
25
bookwyrm/templates/snippets/suggested_users.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
{% load i18n %}
|
||||
{% load utilities %}
|
||||
{% load humanize %}
|
||||
<div class="columns is-mobile scroll-x mb-0">
|
||||
{% for user in suggested_users %}
|
||||
<div class="column is-flex is-flex-grow-0">
|
||||
<div class="box has-text-centered is-shadowless has-background-white-bis m-0">
|
||||
<a href="{{ user.local_path }}" class="has-text-black">
|
||||
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||
<span title="{{ user.display_name }}" class="is-block is-6 has-text-weight-bold">{{ user.display_name|truncatechars:10 }}</span>
|
||||
<span title="@{{ user|username }}" class="is-block pb-3">@{{ user|username|truncatechars:8 }}</span>
|
||||
</a>
|
||||
{% include 'snippets/follow_button.html' with user=user minimal=True %}
|
||||
{% if user.mutuals %}
|
||||
<p class="help">
|
||||
{% blocktrans with mutuals=user.mutuals|intcomma count counter=user.mutuals %}{{ mutuals }} follower you follow{% plural %}{{ mutuals }} followers you follow{% endblocktrans %}
|
||||
</p>
|
||||
{% elif user.shared_books %}
|
||||
<p class="help">{% blocktrans with shared_books=user.shared_books|intcomma count counter=user.shared_books %}{{ shared_books }} book on your shelves{% plural %}{{ shared_books }} books on your shelves{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
@ -12,3 +12,4 @@
|
|||
{% blocktrans with username=user.display_name %}{{ username }} isn't following any users{% endblocktrans %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import datetime
|
||||
|
||||
from unittest.mock import patch
|
||||
"""test author serializer"""
|
||||
from django.test import TestCase
|
||||
from bookwyrm import models
|
||||
|
||||
|
|
|
@ -20,16 +20,20 @@ from bookwyrm import models
|
|||
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class BaseActivity(TestCase):
|
||||
"""the super class for model-linked activitypub dataclasses"""
|
||||
|
||||
def setUp(self):
|
||||
"""we're probably going to re-use this so why copy/paste"""
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
self.user.remote_id = "http://example.com/a/b"
|
||||
self.user.save(broadcast=False)
|
||||
self.user.save(broadcast=False, update_fields=["remote_id"])
|
||||
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||
self.userdata = json.loads(datafile.read_bytes())
|
||||
|
@ -44,24 +48,24 @@ class BaseActivity(TestCase):
|
|||
image.save(output, format=image.format)
|
||||
self.image_data = output.getvalue()
|
||||
|
||||
def test_init(self, _):
|
||||
def test_init(self, *_):
|
||||
"""simple successfuly init"""
|
||||
instance = ActivityObject(id="a", type="b")
|
||||
self.assertTrue(hasattr(instance, "id"))
|
||||
self.assertTrue(hasattr(instance, "type"))
|
||||
|
||||
def test_init_missing(self, _):
|
||||
def test_init_missing(self, *_):
|
||||
"""init with missing required params"""
|
||||
with self.assertRaises(ActivitySerializerError):
|
||||
ActivityObject()
|
||||
|
||||
def test_init_extra_fields(self, _):
|
||||
def test_init_extra_fields(self, *_):
|
||||
"""init ignoring additional fields"""
|
||||
instance = ActivityObject(id="a", type="b", fish="c")
|
||||
self.assertTrue(hasattr(instance, "id"))
|
||||
self.assertTrue(hasattr(instance, "type"))
|
||||
|
||||
def test_init_default_field(self, _):
|
||||
def test_init_default_field(self, *_):
|
||||
"""replace an existing required field with a default field"""
|
||||
|
||||
@dataclass(init=False)
|
||||
|
@ -74,7 +78,7 @@ class BaseActivity(TestCase):
|
|||
self.assertEqual(instance.id, "a")
|
||||
self.assertEqual(instance.type, "TestObject")
|
||||
|
||||
def test_serialize(self, _):
|
||||
def test_serialize(self, *_):
|
||||
"""simple function for converting dataclass to dict"""
|
||||
instance = ActivityObject(id="a", type="b")
|
||||
serialized = instance.serialize()
|
||||
|
@ -83,7 +87,7 @@ class BaseActivity(TestCase):
|
|||
self.assertEqual(serialized["type"], "b")
|
||||
|
||||
@responses.activate
|
||||
def test_resolve_remote_id(self, _):
|
||||
def test_resolve_remote_id(self, *_):
|
||||
"""look up or load remote data"""
|
||||
# existing item
|
||||
result = resolve_remote_id("http://example.com/a/b", model=models.User)
|
||||
|
@ -105,14 +109,14 @@ class BaseActivity(TestCase):
|
|||
self.assertEqual(result.remote_id, "https://example.com/user/mouse")
|
||||
self.assertEqual(result.name, "MOUSE?? MOUSE!!")
|
||||
|
||||
def test_to_model_invalid_model(self, _):
|
||||
def test_to_model_invalid_model(self, *_):
|
||||
"""catch mismatch between activity type and model type"""
|
||||
instance = ActivityObject(id="a", type="b")
|
||||
with self.assertRaises(ActivitySerializerError):
|
||||
instance.to_model(model=models.User)
|
||||
|
||||
@responses.activate
|
||||
def test_to_model_image(self, _):
|
||||
def test_to_model_image(self, *_):
|
||||
"""update an image field"""
|
||||
activity = activitypub.Person(
|
||||
id=self.user.remote_id,
|
||||
|
@ -145,7 +149,7 @@ class BaseActivity(TestCase):
|
|||
self.assertEqual(self.user.name, "New Name")
|
||||
self.assertEqual(self.user.key_pair.public_key, "hi")
|
||||
|
||||
def test_to_model_many_to_many(self, _):
|
||||
def test_to_model_many_to_many(self, *_):
|
||||
"""annoying that these all need special handling"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
status = models.Status.objects.create(
|
||||
|
@ -176,7 +180,7 @@ class BaseActivity(TestCase):
|
|||
self.assertEqual(status.mention_books.first(), book)
|
||||
|
||||
@responses.activate
|
||||
def test_to_model_one_to_many(self, _):
|
||||
def test_to_model_one_to_many(self, *_):
|
||||
"""these are reversed relationships, where the secondary object
|
||||
keys the primary object but not vice versa"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
|
@ -215,7 +219,7 @@ class BaseActivity(TestCase):
|
|||
self.assertIsNone(status.attachments.first())
|
||||
|
||||
@responses.activate
|
||||
def test_set_related_field(self, _):
|
||||
def test_set_related_field(self, *_):
|
||||
"""celery task to add back-references to created objects"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
status = models.Status.objects.create(
|
||||
|
|
|
@ -119,10 +119,12 @@ class AbstractConnector(TestCase):
|
|||
@responses.activate
|
||||
def test_get_or_create_author(self):
|
||||
"""load an author"""
|
||||
self.connector.author_mappings = [
|
||||
Mapping("id"),
|
||||
Mapping("name"),
|
||||
]
|
||||
self.connector.author_mappings = (
|
||||
[ # pylint: disable=attribute-defined-outside-init
|
||||
Mapping("id"),
|
||||
Mapping("name"),
|
||||
]
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"bookwyrmUser": true,
|
||||
"manuallyApprovesFollowers": false,
|
||||
"discoverable": true,
|
||||
"discoverable": false,
|
||||
"devices": "https://friend.camp/users/tripofmice/collections/devices",
|
||||
"tag": [],
|
||||
"icon": {
|
||||
|
|
|
@ -16,9 +16,12 @@ from bookwyrm.settings import DOMAIN
|
|||
|
||||
|
||||
def make_date(*args):
|
||||
"""helper function to easily generate a date obj"""
|
||||
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
||||
|
||||
|
||||
# pylint: disable=consider-using-with
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class GoodreadsImport(TestCase):
|
||||
"""importing from goodreads csv"""
|
||||
|
||||
|
@ -27,9 +30,10 @@ class GoodreadsImport(TestCase):
|
|||
self.importer = GoodreadsImporter()
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||
self.csv = open(datafile, "r", encoding=self.importer.encoding)
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True
|
||||
)
|
||||
|
||||
models.Connector.objects.create(
|
||||
identifier=DOMAIN,
|
||||
|
@ -49,7 +53,7 @@ class GoodreadsImport(TestCase):
|
|||
parent_work=work,
|
||||
)
|
||||
|
||||
def test_create_job(self):
|
||||
def test_create_job(self, _):
|
||||
"""creates the import job entry and checks csv"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "public")
|
||||
self.assertEqual(import_job.user, self.user)
|
||||
|
@ -65,7 +69,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(import_items[2].index, 2)
|
||||
self.assertEqual(import_items[2].data["Book Id"], "28694510")
|
||||
|
||||
def test_create_retry_job(self):
|
||||
def test_create_retry_job(self, _):
|
||||
"""trying again with items that didn't import"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||
import_items = models.ImportItem.objects.filter(job=import_job).all()[:2]
|
||||
|
@ -83,7 +87,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(retry_items[1].index, 1)
|
||||
self.assertEqual(retry_items[1].data["Book Id"], "52691223")
|
||||
|
||||
def test_start_import(self):
|
||||
def test_start_import(self, _):
|
||||
"""begin loading books"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||
MockTask = namedtuple("Task", ("id"))
|
||||
|
@ -95,7 +99,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(import_job.task_id, "7")
|
||||
|
||||
@responses.activate
|
||||
def test_import_data(self):
|
||||
def test_import_data(self, _):
|
||||
"""resolve entry"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||
book = models.Edition.objects.create(title="Test Book")
|
||||
|
@ -110,7 +114,7 @@ class GoodreadsImport(TestCase):
|
|||
import_item = models.ImportItem.objects.get(job=import_job, index=0)
|
||||
self.assertEqual(import_item.book.id, book.id)
|
||||
|
||||
def test_handle_imported_book(self):
|
||||
def test_handle_imported_book(self, _):
|
||||
"""goodreads import added a book, this adds related connections"""
|
||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||
self.assertIsNone(shelf.books.first())
|
||||
|
@ -141,7 +145,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(readthrough.start_date, make_date(2020, 10, 21))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||
|
||||
def test_handle_imported_book_already_shelved(self):
|
||||
def test_handle_imported_book_already_shelved(self, _):
|
||||
"""goodreads import added a book, this adds related connections"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
shelf = self.user.shelf_set.filter(identifier="to-read").first()
|
||||
|
@ -179,7 +183,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(readthrough.start_date, make_date(2020, 10, 21))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||
|
||||
def test_handle_import_twice(self):
|
||||
def test_handle_import_twice(self, _):
|
||||
"""re-importing books"""
|
||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
|
@ -212,7 +216,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_handle_imported_book_review(self, _):
|
||||
def test_handle_imported_book_review(self, *_):
|
||||
"""goodreads review import"""
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||
|
@ -234,7 +238,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(review.privacy, "unlisted")
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_handle_imported_book_rating(self, _):
|
||||
def test_handle_imported_book_rating(self, *_):
|
||||
"""goodreads rating import"""
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
||||
|
@ -257,7 +261,7 @@ class GoodreadsImport(TestCase):
|
|||
self.assertEqual(review.published_date, make_date(2019, 7, 8))
|
||||
self.assertEqual(review.privacy, "unlisted")
|
||||
|
||||
def test_handle_imported_book_reviews_disabled(self):
|
||||
def test_handle_imported_book_reviews_disabled(self, _):
|
||||
"""goodreads review import"""
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||
|
|
|
@ -19,6 +19,8 @@ def make_date(*args):
|
|||
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
||||
|
||||
|
||||
# pylint: disable=consider-using-with
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class LibrarythingImport(TestCase):
|
||||
"""importing from librarything tsv"""
|
||||
|
||||
|
@ -29,9 +31,10 @@ class LibrarythingImport(TestCase):
|
|||
|
||||
# Librarything generates latin encoded exports...
|
||||
self.csv = open(datafile, "r", encoding=self.importer.encoding)
|
||||
self.user = models.User.objects.create_user(
|
||||
"mmai", "mmai@mmai.mmai", "password", local=True
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mmai", "mmai@mmai.mmai", "password", local=True
|
||||
)
|
||||
|
||||
models.Connector.objects.create(
|
||||
identifier=DOMAIN,
|
||||
|
@ -51,7 +54,7 @@ class LibrarythingImport(TestCase):
|
|||
parent_work=work,
|
||||
)
|
||||
|
||||
def test_create_job(self):
|
||||
def test_create_job(self, _):
|
||||
"""creates the import job entry and checks csv"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "public")
|
||||
self.assertEqual(import_job.user, self.user)
|
||||
|
@ -67,7 +70,7 @@ class LibrarythingImport(TestCase):
|
|||
self.assertEqual(import_items[2].index, 2)
|
||||
self.assertEqual(import_items[2].data["Book Id"], "5015399")
|
||||
|
||||
def test_create_retry_job(self):
|
||||
def test_create_retry_job(self, _):
|
||||
"""trying again with items that didn't import"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||
import_items = models.ImportItem.objects.filter(job=import_job).all()[:2]
|
||||
|
@ -86,7 +89,7 @@ class LibrarythingImport(TestCase):
|
|||
self.assertEqual(retry_items[1].data["Book Id"], "5015319")
|
||||
|
||||
@responses.activate
|
||||
def test_import_data(self):
|
||||
def test_import_data(self, _):
|
||||
"""resolve entry"""
|
||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||
book = models.Edition.objects.create(title="Test Book")
|
||||
|
@ -101,7 +104,7 @@ class LibrarythingImport(TestCase):
|
|||
import_item = models.ImportItem.objects.get(job=import_job, index=0)
|
||||
self.assertEqual(import_item.book.id, book.id)
|
||||
|
||||
def test_handle_imported_book(self):
|
||||
def test_handle_imported_book(self, _):
|
||||
"""librarything import added a book, this adds related connections"""
|
||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||
self.assertIsNone(shelf.books.first())
|
||||
|
@ -131,7 +134,7 @@ class LibrarythingImport(TestCase):
|
|||
self.assertEqual(readthrough.start_date, make_date(2007, 4, 16))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||
|
||||
def test_handle_imported_book_already_shelved(self):
|
||||
def test_handle_imported_book_already_shelved(self, _):
|
||||
"""librarything import added a book, this adds related connections"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
shelf = self.user.shelf_set.filter(identifier="to-read").first()
|
||||
|
@ -163,7 +166,7 @@ class LibrarythingImport(TestCase):
|
|||
self.assertEqual(readthrough.start_date, make_date(2007, 4, 16))
|
||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||
|
||||
def test_handle_import_twice(self):
|
||||
def test_handle_import_twice(self, _):
|
||||
"""re-importing books"""
|
||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
|
@ -195,7 +198,7 @@ class LibrarythingImport(TestCase):
|
|||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
def test_handle_imported_book_review(self, _):
|
||||
def test_handle_imported_book_review(self, *_):
|
||||
"""librarything review import"""
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv")
|
||||
|
@ -216,7 +219,7 @@ class LibrarythingImport(TestCase):
|
|||
self.assertEqual(review.published_date, make_date(2007, 5, 8))
|
||||
self.assertEqual(review.privacy, "unlisted")
|
||||
|
||||
def test_handle_imported_book_reviews_disabled(self):
|
||||
def test_handle_imported_book_reviews_disabled(self, _):
|
||||
"""librarything review import"""
|
||||
import_job = models.ImportJob.objects.create(user=self.user)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv")
|
||||
|
|
|
@ -12,16 +12,17 @@ class Activitystreams(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""we need some stuff"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||
)
|
||||
self.another_user = models.User.objects.create_user(
|
||||
"nutria",
|
||||
"nutria@nutria.nutria",
|
||||
"password",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||
)
|
||||
self.another_user = models.User.objects.create_user(
|
||||
"nutria",
|
||||
"nutria@nutria.nutria",
|
||||
"password",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -27,11 +27,12 @@ class ActivitypubMixins(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""shared data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
self.local_user.remote_id = "http://example.com/a/b"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -189,7 +190,7 @@ class ActivitypubMixins(TestCase):
|
|||
def test_get_recipients_combine_inboxes(self, *_):
|
||||
"""should combine users with the same shared_inbox"""
|
||||
self.remote_user.shared_inbox = "http://example.com/inbox"
|
||||
self.remote_user.save(broadcast=False)
|
||||
self.remote_user.save(broadcast=False, update_fields=["shared_inbox"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
another_remote_user = models.User.objects.create_user(
|
||||
"nutria",
|
||||
|
|
|
@ -13,9 +13,10 @@ class BaseModel(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""shared data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -24,10 +24,11 @@ from bookwyrm.models.base_model import BookWyrmModel
|
|||
from bookwyrm.models.activitypub_mixin import ActivitypubMixin
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
class ActivitypubFields(TestCase):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class ModelFields(TestCase):
|
||||
"""overwrites standard model feilds to work with activitypub"""
|
||||
|
||||
def test_validate_remote_id(self):
|
||||
def test_validate_remote_id(self, _):
|
||||
"""should look like a url"""
|
||||
self.assertIsNone(fields.validate_remote_id("http://www.example.com"))
|
||||
self.assertIsNone(fields.validate_remote_id("https://www.example.com"))
|
||||
|
@ -44,7 +45,7 @@ class ActivitypubFields(TestCase):
|
|||
"http://www.example.com/dlfjg 23/x",
|
||||
)
|
||||
|
||||
def test_activitypub_field_mixin(self):
|
||||
def test_activitypub_field_mixin(self, _):
|
||||
"""generic mixin with super basic to and from functionality"""
|
||||
instance = fields.ActivitypubFieldMixin()
|
||||
self.assertEqual(instance.field_to_activity("fish"), "fish")
|
||||
|
@ -62,7 +63,7 @@ class ActivitypubFields(TestCase):
|
|||
instance.name = "snake_case_name"
|
||||
self.assertEqual(instance.get_activitypub_field(), "snakeCaseName")
|
||||
|
||||
def test_set_field_from_activity(self):
|
||||
def test_set_field_from_activity(self, _):
|
||||
"""setter from entire json blob"""
|
||||
|
||||
@dataclass
|
||||
|
@ -81,7 +82,7 @@ class ActivitypubFields(TestCase):
|
|||
instance.set_field_from_activity(mock_model, data)
|
||||
self.assertEqual(mock_model.field_name, "hi")
|
||||
|
||||
def test_set_activity_from_field(self):
|
||||
def test_set_activity_from_field(self, _):
|
||||
"""set json field given entire model"""
|
||||
|
||||
@dataclass
|
||||
|
@ -99,7 +100,7 @@ class ActivitypubFields(TestCase):
|
|||
instance.set_activity_from_field(data, mock_model)
|
||||
self.assertEqual(data["fieldName"], "bip")
|
||||
|
||||
def test_remote_id_field(self):
|
||||
def test_remote_id_field(self, _):
|
||||
"""just sets some defaults on charfield"""
|
||||
instance = fields.RemoteIdField()
|
||||
self.assertEqual(instance.max_length, 255)
|
||||
|
@ -108,7 +109,7 @@ class ActivitypubFields(TestCase):
|
|||
with self.assertRaises(ValidationError):
|
||||
instance.run_validators("http://www.example.com/dlfjg 23/x")
|
||||
|
||||
def test_username_field(self):
|
||||
def test_username_field(self, _):
|
||||
"""again, just setting defaults on username field"""
|
||||
instance = fields.UsernameField()
|
||||
self.assertEqual(instance.activitypub_field, "preferredUsername")
|
||||
|
@ -129,7 +130,7 @@ class ActivitypubFields(TestCase):
|
|||
|
||||
self.assertEqual(instance.field_to_activity("test@example.com"), "test")
|
||||
|
||||
def test_privacy_field_defaults(self):
|
||||
def test_privacy_field_defaults(self, _):
|
||||
"""post privacy field's many default values"""
|
||||
instance = fields.PrivacyField()
|
||||
self.assertEqual(instance.max_length, 255)
|
||||
|
@ -142,7 +143,7 @@ class ActivitypubFields(TestCase):
|
|||
instance.public, "https://www.w3.org/ns/activitystreams#Public"
|
||||
)
|
||||
|
||||
def test_privacy_field_set_field_from_activity(self):
|
||||
def test_privacy_field_set_field_from_activity(self, _):
|
||||
"""translate between to/cc fields and privacy"""
|
||||
|
||||
@dataclass(init=False)
|
||||
|
@ -230,7 +231,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(activity["to"], [user.remote_id])
|
||||
self.assertEqual(activity["cc"], [])
|
||||
|
||||
def test_foreign_key(self):
|
||||
def test_foreign_key(self, _):
|
||||
"""should be able to format a related model"""
|
||||
instance = fields.ForeignKey("User", on_delete=models.CASCADE)
|
||||
Serializable = namedtuple("Serializable", ("to_activity", "remote_id"))
|
||||
|
@ -239,7 +240,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(instance.field_to_activity(item), "https://e.b/c")
|
||||
|
||||
@responses.activate
|
||||
def test_foreign_key_from_activity_str(self):
|
||||
def test_foreign_key_from_activity_str(self, _):
|
||||
"""create a new object from a foreign key"""
|
||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||
|
@ -266,7 +267,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(value.remote_id, "https://example.com/user/mouse")
|
||||
self.assertEqual(value.name, "MOUSE?? MOUSE!!")
|
||||
|
||||
def test_foreign_key_from_activity_dict(self):
|
||||
def test_foreign_key_from_activity_dict(self, *_):
|
||||
"""test recieving activity json"""
|
||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||
|
@ -286,7 +287,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(value.name, "MOUSE?? MOUSE!!")
|
||||
# et cetera but we're not testing serializing user json
|
||||
|
||||
def test_foreign_key_from_activity_dict_existing(self):
|
||||
def test_foreign_key_from_activity_dict_existing(self, _):
|
||||
"""test receiving a dict of an existing object in the db"""
|
||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||
|
@ -295,7 +296,7 @@ class ActivitypubFields(TestCase):
|
|||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
user.remote_id = "https://example.com/user/mouse"
|
||||
user.save(broadcast=False)
|
||||
user.save(broadcast=False, update_fields=["remote_id"])
|
||||
|
||||
User.objects.create_user(
|
||||
"rat", "rat@rat.rat", "ratword", local=True, localname="rat"
|
||||
|
@ -305,7 +306,7 @@ class ActivitypubFields(TestCase):
|
|||
value = instance.field_from_activity(activitypub.Person(**userdata))
|
||||
self.assertEqual(value, user)
|
||||
|
||||
def test_foreign_key_from_activity_str_existing(self):
|
||||
def test_foreign_key_from_activity_str_existing(self, _):
|
||||
"""test receiving a remote id of an existing object in the db"""
|
||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||
user = User.objects.create_user(
|
||||
|
@ -318,14 +319,14 @@ class ActivitypubFields(TestCase):
|
|||
value = instance.field_from_activity(user.remote_id)
|
||||
self.assertEqual(value, user)
|
||||
|
||||
def test_one_to_one_field(self):
|
||||
def test_one_to_one_field(self, _):
|
||||
"""a gussied up foreign key"""
|
||||
instance = fields.OneToOneField("User", on_delete=models.CASCADE)
|
||||
Serializable = namedtuple("Serializable", ("to_activity", "remote_id"))
|
||||
item = Serializable(lambda: {"a": "b"}, "https://e.b/c")
|
||||
self.assertEqual(instance.field_to_activity(item), {"a": "b"})
|
||||
|
||||
def test_many_to_many_field(self):
|
||||
def test_many_to_many_field(self, _):
|
||||
"""lists!"""
|
||||
instance = fields.ManyToManyField("User")
|
||||
|
||||
|
@ -343,7 +344,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(instance.field_to_activity(items), "example.com/snake_case")
|
||||
|
||||
@responses.activate
|
||||
def test_many_to_many_field_from_activity(self):
|
||||
def test_many_to_many_field_from_activity(self, _):
|
||||
"""resolve related fields for a list, takes a list of remote ids"""
|
||||
instance = fields.ManyToManyField(User)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||
|
@ -363,7 +364,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(len(value), 1)
|
||||
self.assertIsInstance(value[0], User)
|
||||
|
||||
def test_tag_field(self):
|
||||
def test_tag_field(self, _):
|
||||
"""a special type of many to many field"""
|
||||
instance = fields.TagField("User")
|
||||
|
||||
|
@ -382,13 +383,14 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(result[0].name, "Name")
|
||||
self.assertEqual(result[0].type, "Serializable")
|
||||
|
||||
def test_tag_field_from_activity(self):
|
||||
def test_tag_field_from_activity(self, _):
|
||||
"""loadin' a list of items from Links"""
|
||||
# TODO
|
||||
|
||||
@responses.activate
|
||||
@patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast")
|
||||
def test_image_field(self, _):
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_image_field(self, *_):
|
||||
"""storing images"""
|
||||
user = User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
|
@ -426,7 +428,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertIsInstance(loaded_image, list)
|
||||
self.assertIsInstance(loaded_image[1], ContentFile)
|
||||
|
||||
def test_image_serialize(self):
|
||||
def test_image_serialize(self, _):
|
||||
"""make sure we're creating sensible image paths"""
|
||||
ValueMock = namedtuple("ValueMock", ("url"))
|
||||
value_mock = ValueMock("/images/fish.jpg")
|
||||
|
@ -435,7 +437,7 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(result.url, "https://your.domain.here/images/fish.jpg")
|
||||
self.assertEqual(result.name, "hello")
|
||||
|
||||
def test_datetime_field(self):
|
||||
def test_datetime_field(self, _):
|
||||
"""this one is pretty simple, it just has to use isoformat"""
|
||||
instance = fields.DateTimeField()
|
||||
now = timezone.now()
|
||||
|
@ -443,12 +445,12 @@ class ActivitypubFields(TestCase):
|
|||
self.assertEqual(instance.field_from_activity(now.isoformat()), now)
|
||||
self.assertEqual(instance.field_from_activity("bip"), None)
|
||||
|
||||
def test_array_field(self):
|
||||
def test_array_field(self, _):
|
||||
"""idk why it makes them strings but probably for a good reason"""
|
||||
instance = fields.ArrayField(fields.IntegerField)
|
||||
self.assertEqual(instance.field_to_activity([0, 1]), ["0", "1"])
|
||||
|
||||
def test_html_field(self):
|
||||
def test_html_field(self, _):
|
||||
"""sanitizes html, the sanitizer has its own tests"""
|
||||
instance = fields.HtmlField()
|
||||
self.assertEqual(
|
||||
|
|
|
@ -59,9 +59,10 @@ class ImportJob(TestCase):
|
|||
unknown_read_data["Exclusive Shelf"] = "read"
|
||||
unknown_read_data["Date Read"] = ""
|
||||
|
||||
user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
job = models.ImportJob.objects.create(user=user)
|
||||
self.item_1 = models.ImportItem.objects.create(
|
||||
job=job, index=1, data=currently_reading_data
|
||||
|
|
|
@ -11,9 +11,10 @@ class List(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""look, a list"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
work = models.Work.objects.create(title="hello")
|
||||
self.book = models.Edition.objects.create(title="hi", parent_work=work)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" testing models """
|
||||
from unittest.mock import patch
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
@ -10,9 +11,10 @@ class ReadThrough(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""look, a shelf"""
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
|
||||
self.work = models.Work.objects.create(title="Example Work")
|
||||
self.edition = models.Edition.objects.create(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" testing models """
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
from django.test import TestCase
|
||||
|
||||
|
@ -20,25 +21,21 @@ class Relationship(TestCase):
|
|||
inbox="https://example.com/users/rat/inbox",
|
||||
outbox="https://example.com/users/rat/outbox",
|
||||
)
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
self.local_user.remote_id = "http://local.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
|
||||
def test_user_follows_from_request(self):
|
||||
"""convert a follow request into a follow"""
|
||||
real_broadcast = models.UserFollowRequest.broadcast
|
||||
|
||||
def mock_broadcast(_, activity, user):
|
||||
"""introspect what's being sent out"""
|
||||
self.assertEqual(user.remote_id, self.local_user.remote_id)
|
||||
self.assertEqual(activity["type"], "Follow")
|
||||
|
||||
models.UserFollowRequest.broadcast = mock_broadcast
|
||||
request = models.UserFollowRequest.objects.create(
|
||||
user_subject=self.local_user, user_object=self.remote_user
|
||||
)
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
|
||||
request = models.UserFollowRequest.objects.create(
|
||||
user_subject=self.local_user, user_object=self.remote_user
|
||||
)
|
||||
activity = json.loads(mock.call_args[0][1])
|
||||
self.assertEqual(activity["type"], "Follow")
|
||||
self.assertEqual(
|
||||
request.remote_id, "http://local.com/user/mouse#follows/%d" % request.id
|
||||
)
|
||||
|
@ -51,7 +48,6 @@ class Relationship(TestCase):
|
|||
self.assertEqual(rel.status, "follows")
|
||||
self.assertEqual(rel.user_subject, self.local_user)
|
||||
self.assertEqual(rel.user_object, self.remote_user)
|
||||
models.UserFollowRequest.broadcast = real_broadcast
|
||||
|
||||
def test_user_follows_from_request_custom_remote_id(self):
|
||||
"""store a specific remote id for a relationship provided by remote"""
|
||||
|
@ -70,36 +66,26 @@ class Relationship(TestCase):
|
|||
self.assertEqual(rel.user_subject, self.local_user)
|
||||
self.assertEqual(rel.user_object, self.remote_user)
|
||||
|
||||
def test_follow_request_activity(self):
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
def test_follow_request_activity(self, broadcast_mock):
|
||||
"""accept a request and make it a relationship"""
|
||||
real_broadcast = models.UserFollowRequest.broadcast
|
||||
|
||||
def mock_broadcast(_, activity, user):
|
||||
self.assertEqual(user.remote_id, self.local_user.remote_id)
|
||||
self.assertEqual(activity["actor"], self.local_user.remote_id)
|
||||
self.assertEqual(activity["object"], self.remote_user.remote_id)
|
||||
self.assertEqual(activity["type"], "Follow")
|
||||
|
||||
models.UserFollowRequest.broadcast = mock_broadcast
|
||||
models.UserFollowRequest.objects.create(
|
||||
user_subject=self.local_user,
|
||||
user_object=self.remote_user,
|
||||
)
|
||||
models.UserFollowRequest.broadcast = real_broadcast
|
||||
activity = json.loads(broadcast_mock.call_args[0][1])
|
||||
self.assertEqual(activity["actor"], self.local_user.remote_id)
|
||||
self.assertEqual(activity["object"], self.remote_user.remote_id)
|
||||
self.assertEqual(activity["type"], "Follow")
|
||||
|
||||
def test_follow_request_accept(self):
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
def test_follow_request_accept(self, broadcast_mock):
|
||||
"""accept a request and make it a relationship"""
|
||||
real_broadcast = models.UserFollowRequest.broadcast
|
||||
|
||||
def mock_broadcast(_, activity, user):
|
||||
self.assertEqual(user.remote_id, self.local_user.remote_id)
|
||||
self.assertEqual(activity["type"], "Accept")
|
||||
self.assertEqual(activity["actor"], self.local_user.remote_id)
|
||||
self.assertEqual(activity["object"]["id"], "https://www.hi.com/")
|
||||
|
||||
self.local_user.manually_approves_followers = True
|
||||
self.local_user.save(broadcast=False)
|
||||
models.UserFollowRequest.broadcast = mock_broadcast
|
||||
self.local_user.save(
|
||||
broadcast=False, update_fields=["manually_approves_followers"]
|
||||
)
|
||||
|
||||
request = models.UserFollowRequest.objects.create(
|
||||
user_subject=self.remote_user,
|
||||
user_object=self.local_user,
|
||||
|
@ -107,32 +93,34 @@ class Relationship(TestCase):
|
|||
)
|
||||
request.accept()
|
||||
|
||||
activity = json.loads(broadcast_mock.call_args[0][1])
|
||||
self.assertEqual(activity["type"], "Accept")
|
||||
self.assertEqual(activity["actor"], self.local_user.remote_id)
|
||||
self.assertEqual(activity["object"]["id"], "https://www.hi.com/")
|
||||
|
||||
self.assertFalse(models.UserFollowRequest.objects.exists())
|
||||
self.assertTrue(models.UserFollows.objects.exists())
|
||||
rel = models.UserFollows.objects.get()
|
||||
self.assertEqual(rel.user_subject, self.remote_user)
|
||||
self.assertEqual(rel.user_object, self.local_user)
|
||||
models.UserFollowRequest.broadcast = real_broadcast
|
||||
|
||||
def test_follow_request_reject(self):
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
def test_follow_request_reject(self, broadcast_mock):
|
||||
"""accept a request and make it a relationship"""
|
||||
real_broadcast = models.UserFollowRequest.broadcast
|
||||
|
||||
def mock_reject(_, activity, user):
|
||||
self.assertEqual(user.remote_id, self.local_user.remote_id)
|
||||
self.assertEqual(activity["type"], "Reject")
|
||||
self.assertEqual(activity["actor"], self.local_user.remote_id)
|
||||
self.assertEqual(activity["object"]["id"], request.remote_id)
|
||||
|
||||
models.UserFollowRequest.broadcast = mock_reject
|
||||
self.local_user.manually_approves_followers = True
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(
|
||||
broadcast=False, update_fields=["manually_approves_followers"]
|
||||
)
|
||||
request = models.UserFollowRequest.objects.create(
|
||||
user_subject=self.remote_user,
|
||||
user_object=self.local_user,
|
||||
)
|
||||
request.reject()
|
||||
|
||||
activity = json.loads(broadcast_mock.call_args[0][1])
|
||||
self.assertEqual(activity["type"], "Reject")
|
||||
self.assertEqual(activity["actor"], self.local_user.remote_id)
|
||||
self.assertEqual(activity["object"]["id"], request.remote_id)
|
||||
|
||||
self.assertFalse(models.UserFollowRequest.objects.exists())
|
||||
self.assertFalse(models.UserFollows.objects.exists())
|
||||
models.UserFollowRequest.broadcast = real_broadcast
|
||||
|
|
|
@ -7,18 +7,20 @@ from bookwyrm import models, settings
|
|||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class Shelf(TestCase):
|
||||
"""some activitypub oddness ahead"""
|
||||
|
||||
def setUp(self):
|
||||
"""look, a shelf"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(title="test book", parent_work=work)
|
||||
|
||||
def test_remote_id(self):
|
||||
def test_remote_id(self, _):
|
||||
"""shelves use custom remote ids"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
shelf = models.Shelf.objects.create(
|
||||
|
@ -27,7 +29,7 @@ class Shelf(TestCase):
|
|||
expected_id = "https://%s/user/mouse/books/test-shelf" % settings.DOMAIN
|
||||
self.assertEqual(shelf.get_remote_id(), expected_id)
|
||||
|
||||
def test_to_activity(self):
|
||||
def test_to_activity(self, _):
|
||||
"""jsonify it"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
shelf = models.Shelf.objects.create(
|
||||
|
@ -41,7 +43,7 @@ class Shelf(TestCase):
|
|||
self.assertEqual(activity_json["name"], "Test Shelf")
|
||||
self.assertEqual(activity_json["owner"], self.local_user.remote_id)
|
||||
|
||||
def test_create_update_shelf(self):
|
||||
def test_create_update_shelf(self, _):
|
||||
"""create and broadcast shelf creation"""
|
||||
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
|
||||
|
@ -62,7 +64,7 @@ class Shelf(TestCase):
|
|||
self.assertEqual(activity["object"]["name"], "arthur russel")
|
||||
self.assertEqual(shelf.name, "arthur russel")
|
||||
|
||||
def test_shelve(self):
|
||||
def test_shelve(self, _):
|
||||
"""create and broadcast shelf creation"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
shelf = models.Shelf.objects.create(
|
||||
|
|
|
@ -22,9 +22,10 @@ class Status(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""useful things for creating a status"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -11,15 +11,16 @@ from bookwyrm.settings import DOMAIN
|
|||
# pylint: disable=missing-function-docstring
|
||||
class User(TestCase):
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse@%s" % DOMAIN,
|
||||
"mouse@mouse.mouse",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
name="hi",
|
||||
bookwyrm_user=False,
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse@%s" % DOMAIN,
|
||||
"mouse@mouse.mouse",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
name="hi",
|
||||
bookwyrm_user=False,
|
||||
)
|
||||
|
||||
def test_computed_fields(self):
|
||||
"""username instead of id here"""
|
||||
|
@ -154,7 +155,8 @@ class User(TestCase):
|
|||
self.assertIsNone(server.application_type)
|
||||
self.assertIsNone(server.application_version)
|
||||
|
||||
def test_delete_user(self):
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_delete_user(self, _):
|
||||
"""deactivate a user"""
|
||||
self.assertTrue(self.user.is_active)
|
||||
with patch(
|
||||
|
|
|
@ -11,16 +11,17 @@ class Activitystreams(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""use a test csv"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||
)
|
||||
self.another_user = models.User.objects.create_user(
|
||||
"nutria",
|
||||
"nutria@nutria.nutria",
|
||||
"password",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||
)
|
||||
self.another_user = models.User.objects.create_user(
|
||||
"nutria",
|
||||
"nutria@nutria.nutria",
|
||||
"password",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -14,13 +14,14 @@ class Emailing(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_invite_email(self, email_mock):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
""" test generating preview images """
|
||||
import pathlib
|
||||
from unittest.mock import patch
|
||||
from PIL import Image
|
||||
|
||||
from django.test import TestCase
|
||||
|
@ -8,7 +9,6 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
|||
from django.db.models.fields.files import ImageFieldFile
|
||||
|
||||
from bookwyrm import models, settings
|
||||
|
||||
from bookwyrm.preview_images import (
|
||||
generate_site_preview_image_task,
|
||||
generate_edition_preview_image_task,
|
||||
|
@ -29,18 +29,19 @@ class PreviewImages(TestCase):
|
|||
avatar_file = pathlib.Path(__file__).parent.joinpath(
|
||||
"../static/images/no_cover.jpg"
|
||||
)
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"possum@local.com",
|
||||
"possum@possum.possum",
|
||||
"password",
|
||||
local=True,
|
||||
localname="possum",
|
||||
avatar=SimpleUploadedFile(
|
||||
avatar_file,
|
||||
open(avatar_file, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
),
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"possum@local.com",
|
||||
"possum@possum.possum",
|
||||
"password",
|
||||
local=True,
|
||||
localname="possum",
|
||||
avatar=SimpleUploadedFile(
|
||||
avatar_file,
|
||||
open(avatar_file, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
),
|
||||
)
|
||||
|
||||
self.work = models.Work.objects.create(title="Test Work")
|
||||
self.edition = models.Edition.objects.create(
|
||||
|
|
|
@ -37,19 +37,20 @@ class Signature(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""create users and test data"""
|
||||
self.mouse = models.User.objects.create_user(
|
||||
"mouse@%s" % DOMAIN,
|
||||
"mouse@example.com",
|
||||
"",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@%s" % DOMAIN, "rat@example.com", "", local=True, localname="rat"
|
||||
)
|
||||
self.cat = models.User.objects.create_user(
|
||||
"cat@%s" % DOMAIN, "cat@example.com", "", local=True, localname="cat"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.mouse = models.User.objects.create_user(
|
||||
"mouse@%s" % DOMAIN,
|
||||
"mouse@example.com",
|
||||
"",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@%s" % DOMAIN, "rat@example.com", "", local=True, localname="rat"
|
||||
)
|
||||
self.cat = models.User.objects.create_user(
|
||||
"cat@%s" % DOMAIN, "cat@example.com", "", local=True, localname="cat"
|
||||
)
|
||||
|
||||
private_key, public_key = create_key_pair()
|
||||
|
||||
|
|
173
bookwyrm/tests/test_suggested_users.py
Normal file
173
bookwyrm/tests/test_suggested_users.py
Normal file
|
@ -0,0 +1,173 @@
|
|||
""" testing user follow suggestions """
|
||||
from collections import namedtuple
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.db.models import Q
|
||||
from django.test import TestCase
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.suggested_users import suggested_users, get_annotated_users
|
||||
|
||||
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
class SuggestedUsers(TestCase):
|
||||
"""using redis to build activity streams"""
|
||||
|
||||
def setUp(self):
|
||||
"""use a test csv"""
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||
)
|
||||
|
||||
def test_get_rank(self, *_):
|
||||
"""a float that reflects both the mutuals count and shared books"""
|
||||
Mock = namedtuple("AnnotatedUserMock", ("mutuals", "shared_books"))
|
||||
annotated_user_mock = Mock(3, 27)
|
||||
rank = suggested_users.get_rank(annotated_user_mock)
|
||||
self.assertEqual(rank, 3.9642857142857144)
|
||||
|
||||
def test_store_id(self, *_):
|
||||
"""redis key generation"""
|
||||
self.assertEqual(
|
||||
suggested_users.store_id(self.local_user),
|
||||
"{:d}-suggestions".format(self.local_user.id),
|
||||
)
|
||||
|
||||
def test_get_counts_from_rank(self, *_):
|
||||
"""reverse the rank computation to get the mutuals and shared books counts"""
|
||||
counts = suggested_users.get_counts_from_rank(3.9642857142857144)
|
||||
self.assertEqual(counts["mutuals"], 3)
|
||||
self.assertEqual(counts["shared_books"], 27)
|
||||
|
||||
def test_get_objects_for_store(self, *_):
|
||||
"""list of people to follow for a given user"""
|
||||
|
||||
mutual_user = models.User.objects.create_user(
|
||||
"rat", "rat@local.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
suggestable_user = models.User.objects.create_user(
|
||||
"nutria",
|
||||
"nutria@nutria.nutria",
|
||||
"password",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
discoverable=True,
|
||||
)
|
||||
|
||||
# you follow rat
|
||||
mutual_user.followers.add(self.local_user)
|
||||
# rat follows the suggested user
|
||||
suggestable_user.followers.add(mutual_user)
|
||||
|
||||
results = suggested_users.get_objects_for_store(
|
||||
"{:d}-suggestions".format(self.local_user.id)
|
||||
)
|
||||
self.assertEqual(results.count(), 1)
|
||||
match = results.first()
|
||||
self.assertEqual(match.id, suggestable_user.id)
|
||||
self.assertEqual(match.mutuals, 1)
|
||||
|
||||
def test_create_user_signal(self, *_):
|
||||
"""build suggestions for new users"""
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") as mock:
|
||||
models.User.objects.create_user(
|
||||
"nutria", "nutria@nu.tria", "password", local=True, localname="nutria"
|
||||
)
|
||||
|
||||
self.assertEqual(mock.call_count, 1)
|
||||
|
||||
def test_get_annotated_users(self, *_):
|
||||
"""list of people you might know"""
|
||||
user_1 = models.User.objects.create_user(
|
||||
"nutria@local.com",
|
||||
"nutria@nutria.com",
|
||||
"nutriaword",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
discoverable=True,
|
||||
)
|
||||
user_2 = models.User.objects.create_user(
|
||||
"fish@local.com",
|
||||
"fish@fish.com",
|
||||
"fishword",
|
||||
local=True,
|
||||
localname="fish",
|
||||
)
|
||||
work = models.Work.objects.create(title="Test Work")
|
||||
book = models.Edition.objects.create(
|
||||
title="Test Book",
|
||||
remote_id="https://example.com/book/1",
|
||||
parent_work=work,
|
||||
)
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
# 1 shared follow
|
||||
self.local_user.following.add(user_2)
|
||||
user_1.followers.add(user_2)
|
||||
|
||||
# 1 shared book
|
||||
models.ShelfBook.objects.create(
|
||||
user=self.local_user,
|
||||
book=book,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
models.ShelfBook.objects.create(
|
||||
user=user_1, book=book, shelf=user_1.shelf_set.first()
|
||||
)
|
||||
|
||||
result = get_annotated_users(self.local_user)
|
||||
self.assertEqual(result.count(), 1)
|
||||
self.assertTrue(user_1 in result)
|
||||
self.assertFalse(user_2 in result)
|
||||
|
||||
user_1_annotated = result.get(id=user_1.id)
|
||||
self.assertEqual(user_1_annotated.mutuals, 1)
|
||||
self.assertEqual(user_1_annotated.shared_books, 1)
|
||||
|
||||
def test_get_annotated_users_counts(self, *_):
|
||||
"""correct counting for multiple shared attributed"""
|
||||
user_1 = models.User.objects.create_user(
|
||||
"nutria@local.com",
|
||||
"nutria@nutria.com",
|
||||
"nutriaword",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
discoverable=True,
|
||||
)
|
||||
for i in range(3):
|
||||
user = models.User.objects.create_user(
|
||||
"{:d}@local.com".format(i),
|
||||
"{:d}@nutria.com".format(i),
|
||||
"password",
|
||||
local=True,
|
||||
localname=i,
|
||||
)
|
||||
user.following.add(user_1)
|
||||
user.followers.add(self.local_user)
|
||||
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
for i in range(3):
|
||||
book = models.Edition.objects.create(
|
||||
title=i,
|
||||
parent_work=models.Work.objects.create(title=i),
|
||||
)
|
||||
models.ShelfBook.objects.create(
|
||||
user=self.local_user,
|
||||
book=book,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
models.ShelfBook.objects.create(
|
||||
user=user_1, book=book, shelf=user_1.shelf_set.first()
|
||||
)
|
||||
|
||||
result = get_annotated_users(
|
||||
self.local_user,
|
||||
~Q(id=self.local_user.id),
|
||||
~Q(followers=self.local_user),
|
||||
)
|
||||
user_1_annotated = result.get(id=user_1.id)
|
||||
self.assertEqual(user_1_annotated.mutuals, 3)
|
|
@ -22,13 +22,14 @@ class TemplateTags(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""create some filler objects"""
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.mouse",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.mouse",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -19,15 +19,16 @@ class Inbox(TestCase):
|
|||
self.client = Client()
|
||||
self.factory = RequestFactory()
|
||||
|
||||
local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
local_user.remote_id = "https://example.com/user/mouse"
|
||||
local_user.save(broadcast=False)
|
||||
local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -144,7 +145,8 @@ class Inbox(TestCase):
|
|||
)
|
||||
self.assertTrue(views.inbox.is_blocked_activity(activity))
|
||||
|
||||
def test_create_by_deactivated_user(self):
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_create_by_deactivated_user(self, _):
|
||||
"""don't let deactivated users post"""
|
||||
self.remote_user.delete(broadcast=False)
|
||||
self.assertTrue(self.remote_user.deleted)
|
||||
|
|
|
@ -13,15 +13,16 @@ class InboxAdd(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
local_user.remote_id = "https://example.com/user/mouse"
|
||||
local_user.save(broadcast=False)
|
||||
local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -13,15 +13,16 @@ class InboxActivities(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -12,15 +12,16 @@ class InboxBlock(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -16,15 +16,16 @@ class InboxCreate(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -13,15 +13,16 @@ class InboxActivities(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -105,7 +106,8 @@ class InboxActivities(TestCase):
|
|||
self.assertEqual(models.Notification.objects.count(), 1)
|
||||
self.assertEqual(models.Notification.objects.get(), notif)
|
||||
|
||||
def test_delete_user(self):
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_delete_user(self, _):
|
||||
"""delete a user"""
|
||||
self.assertTrue(models.User.objects.get(username="rat@example.com").is_active)
|
||||
activity = {
|
||||
|
|
|
@ -13,15 +13,16 @@ class InboxRelationships(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -102,7 +103,9 @@ class InboxRelationships(TestCase):
|
|||
}
|
||||
|
||||
self.local_user.manually_approves_followers = True
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(
|
||||
broadcast=False, update_fields=["manually_approves_followers"]
|
||||
)
|
||||
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
views.inbox.activity_task(activity)
|
||||
|
@ -124,7 +127,9 @@ class InboxRelationships(TestCase):
|
|||
def test_undo_follow_request(self):
|
||||
"""the requester cancels a follow request"""
|
||||
self.local_user.manually_approves_followers = True
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(
|
||||
broadcast=False, update_fields=["manually_approves_followers"]
|
||||
)
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
request = models.UserFollowRequest.objects.create(
|
||||
user_subject=self.remote_user, user_object=self.local_user
|
||||
|
|
|
@ -12,15 +12,16 @@ class InboxActivities(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -12,15 +12,16 @@ class InboxRemove(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -14,15 +14,16 @@ class InboxUpdate(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""basic user and book data"""
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@example.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.local_user.remote_id = "https://example.com/user/mouse"
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(broadcast=False, update_fields=["remote_id"])
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -79,7 +80,8 @@ class InboxUpdate(TestCase):
|
|||
self.assertEqual(book_list.description, "summary text")
|
||||
self.assertEqual(book_list.remote_id, "https://example.com/list/22")
|
||||
|
||||
def test_update_user(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
|
||||
def test_update_user(self, _):
|
||||
"""update an existing user"""
|
||||
models.UserFollows.objects.create(
|
||||
user_subject=self.local_user,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" test for app action functionality """
|
||||
from unittest.mock import patch
|
||||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
@ -12,13 +13,14 @@ class AnnouncementViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
|
|
|
@ -13,25 +13,27 @@ from bookwyrm.settings import DOMAIN
|
|||
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class AuthenticationViews(TestCase):
|
||||
"""login and password management"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.anonymous_user = AnonymousUser
|
||||
self.anonymous_user.is_authenticated = False
|
||||
|
||||
self.settings = models.SiteSettings.objects.create(id=1)
|
||||
|
||||
def test_login_get(self):
|
||||
def test_login_get(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
login = views.Login.as_view()
|
||||
request = self.factory.get("")
|
||||
|
@ -47,7 +49,7 @@ class AuthenticationViews(TestCase):
|
|||
self.assertEqual(result.url, "/")
|
||||
self.assertEqual(result.status_code, 302)
|
||||
|
||||
def test_register(self):
|
||||
def test_register(self, _):
|
||||
"""create a user"""
|
||||
view = views.Register.as_view()
|
||||
self.assertEqual(models.User.objects.count(), 1)
|
||||
|
@ -68,7 +70,7 @@ class AuthenticationViews(TestCase):
|
|||
self.assertEqual(nutria.localname, "nutria-user.user_nutria")
|
||||
self.assertEqual(nutria.local, True)
|
||||
|
||||
def test_register_trailing_space(self):
|
||||
def test_register_trailing_space(self, _):
|
||||
"""django handles this so weirdly"""
|
||||
view = views.Register.as_view()
|
||||
request = self.factory.post(
|
||||
|
@ -84,7 +86,7 @@ class AuthenticationViews(TestCase):
|
|||
self.assertEqual(nutria.localname, "nutria")
|
||||
self.assertEqual(nutria.local, True)
|
||||
|
||||
def test_register_invalid_email(self):
|
||||
def test_register_invalid_email(self, _):
|
||||
"""gotta have an email"""
|
||||
view = views.Register.as_view()
|
||||
self.assertEqual(models.User.objects.count(), 1)
|
||||
|
@ -95,7 +97,7 @@ class AuthenticationViews(TestCase):
|
|||
self.assertEqual(models.User.objects.count(), 1)
|
||||
response.render()
|
||||
|
||||
def test_register_invalid_username(self):
|
||||
def test_register_invalid_username(self, _):
|
||||
"""gotta have an email"""
|
||||
view = views.Register.as_view()
|
||||
self.assertEqual(models.User.objects.count(), 1)
|
||||
|
@ -123,7 +125,7 @@ class AuthenticationViews(TestCase):
|
|||
self.assertEqual(models.User.objects.count(), 1)
|
||||
response.render()
|
||||
|
||||
def test_register_closed_instance(self):
|
||||
def test_register_closed_instance(self, _):
|
||||
"""you can't just register"""
|
||||
view = views.Register.as_view()
|
||||
self.settings.allow_registration = False
|
||||
|
@ -135,7 +137,7 @@ class AuthenticationViews(TestCase):
|
|||
with self.assertRaises(PermissionDenied):
|
||||
view(request)
|
||||
|
||||
def test_register_invite(self):
|
||||
def test_register_invite(self, _):
|
||||
"""you can't just register"""
|
||||
view = views.Register.as_view()
|
||||
self.settings.allow_registration = False
|
||||
|
|
|
@ -17,14 +17,15 @@ class AuthorViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.group = Group.objects.create(name="editor")
|
||||
self.group.permissions.add(
|
||||
Permission.objects.create(
|
||||
|
|
|
@ -14,13 +14,14 @@ class BlockViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -23,14 +23,15 @@ class BookViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.group = Group.objects.create(name="editor")
|
||||
self.group.permissions.add(
|
||||
Permission.objects.create(
|
||||
|
@ -200,7 +201,8 @@ class BookViews(TestCase):
|
|||
self.assertEqual(book.authors.first().name, "Sappho")
|
||||
self.assertEqual(book.authors.first(), book.parent_work.authors.first())
|
||||
|
||||
def test_switch_edition(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
def test_switch_edition(self, _):
|
||||
"""updates user's relationships to a book"""
|
||||
work = models.Work.objects.create(title="test work")
|
||||
edition1 = models.Edition.objects.create(title="first ed", parent_work=work)
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
""" test for app action functionality """
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
@ -12,15 +15,25 @@ class DirectoryViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
|
||||
models.SiteSettings.objects.create()
|
||||
self.anonymous_user = AnonymousUser
|
||||
self.anonymous_user.is_authenticated = False
|
||||
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
|
||||
def test_directory_page(self, *_):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
|
@ -29,10 +42,16 @@ class DirectoryViews(TestCase):
|
|||
remote_id="https://example.com/users/rat",
|
||||
discoverable=True,
|
||||
)
|
||||
view = views.Directory.as_view()
|
||||
request = self.factory.get("")
|
||||
request.user = self.local_user
|
||||
|
||||
models.SiteSettings.objects.create()
|
||||
result = view(request)
|
||||
self.assertIsInstance(result, TemplateResponse)
|
||||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_directory_page(self):
|
||||
def test_directory_page_empty(self):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.Directory.as_view()
|
||||
request = self.factory.get("")
|
||||
|
@ -42,3 +61,12 @@ class DirectoryViews(TestCase):
|
|||
self.assertIsInstance(result, TemplateResponse)
|
||||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_directory_page_logged_out(self):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.Directory.as_view()
|
||||
request = self.factory.get("")
|
||||
request.user = self.anonymous_user
|
||||
|
||||
result = view(request)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
|
|
|
@ -15,36 +15,38 @@ from django.test.client import RequestFactory
|
|||
from bookwyrm import forms, models, views
|
||||
|
||||
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
class EditUserViews(TestCase):
|
||||
"""view user and edit profile"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
|
||||
self.book = models.Edition.objects.create(title="test")
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
models.ShelfBook.objects.create(
|
||||
book=self.book,
|
||||
user=self.local_user,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
|
||||
self.book = models.Edition.objects.create(title="test")
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
models.ShelfBook.objects.create(
|
||||
book=self.book,
|
||||
user=self.local_user,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
|
||||
models.SiteSettings.objects.create()
|
||||
self.anonymous_user = AnonymousUser
|
||||
self.anonymous_user.is_authenticated = False
|
||||
|
||||
def test_edit_user_page(self):
|
||||
def test_edit_user_page(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.EditUser.as_view()
|
||||
request = self.factory.get("")
|
||||
|
@ -54,7 +56,7 @@ class EditUserViews(TestCase):
|
|||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_edit_user(self):
|
||||
def test_edit_user(self, _):
|
||||
"""use a form to update a user"""
|
||||
view = views.EditUser.as_view()
|
||||
form = forms.EditUserForm(instance=self.local_user)
|
||||
|
@ -73,7 +75,7 @@ class EditUserViews(TestCase):
|
|||
self.assertEqual(self.local_user.name, "New Name")
|
||||
self.assertEqual(self.local_user.email, "wow@email.com")
|
||||
|
||||
def test_edit_user_avatar(self):
|
||||
def test_edit_user_avatar(self, _):
|
||||
"""use a form to update a user"""
|
||||
view = views.EditUser.as_view()
|
||||
form = forms.EditUserForm(instance=self.local_user)
|
||||
|
@ -100,7 +102,7 @@ class EditUserViews(TestCase):
|
|||
self.assertEqual(self.local_user.avatar.width, 120)
|
||||
self.assertEqual(self.local_user.avatar.height, 120)
|
||||
|
||||
def test_crop_avatar(self):
|
||||
def test_crop_avatar(self, _):
|
||||
"""reduce that image size"""
|
||||
image_file = pathlib.Path(__file__).parent.joinpath(
|
||||
"../../static/images/no_cover.jpg"
|
||||
|
@ -112,7 +114,7 @@ class EditUserViews(TestCase):
|
|||
image_result = Image.open(result)
|
||||
self.assertEqual(image_result.size, (120, 120))
|
||||
|
||||
def test_delete_user_page(self):
|
||||
def test_delete_user_page(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.DeleteUser.as_view()
|
||||
request = self.factory.get("")
|
||||
|
@ -122,7 +124,8 @@ class EditUserViews(TestCase):
|
|||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_delete_user(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task")
|
||||
def test_delete_user(self, *_):
|
||||
"""use a form to update a user"""
|
||||
view = views.DeleteUser.as_view()
|
||||
form = forms.DeleteUserForm()
|
||||
|
|
|
@ -15,13 +15,14 @@ class FederationViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -68,7 +69,7 @@ class FederationViews(TestCase):
|
|||
identifier="hi.there.com",
|
||||
)
|
||||
self.remote_user.federated_server = server
|
||||
self.remote_user.save()
|
||||
self.remote_user.save(update_fields=["federated_server"])
|
||||
|
||||
self.assertEqual(server.status, "federated")
|
||||
|
||||
|
@ -107,7 +108,9 @@ class FederationViews(TestCase):
|
|||
self.remote_user.federated_server = server
|
||||
self.remote_user.is_active = False
|
||||
self.remote_user.deactivation_reason = "domain_block"
|
||||
self.remote_user.save()
|
||||
self.remote_user.save(
|
||||
update_fields=["federated_server", "is_active", "deactivation_reason"]
|
||||
)
|
||||
|
||||
request = self.factory.post("")
|
||||
request.user = self.local_user
|
||||
|
@ -162,7 +165,7 @@ class FederationViews(TestCase):
|
|||
"""load a json file with a list of servers to block"""
|
||||
server = models.FederatedServer.objects.create(server_name="hi.there.com")
|
||||
self.remote_user.federated_server = server
|
||||
self.remote_user.save()
|
||||
self.remote_user.save(update_fields=["federated_server"])
|
||||
|
||||
data = [
|
||||
{"instance": "server.name", "url": "https://explanation.url"}, # new server
|
||||
|
|
|
@ -16,19 +16,22 @@ from bookwyrm.activitypub import ActivitypubResponse
|
|||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.get_activity_stream")
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
class FeedViews(TestCase):
|
||||
"""activity feed, statuses, dms"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.book = models.Edition.objects.create(
|
||||
parent_work=models.Work.objects.create(title="hi"),
|
||||
title="Example Edition",
|
||||
|
@ -36,6 +39,7 @@ class FeedViews(TestCase):
|
|||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
@patch("bookwyrm.suggested_users.SuggestedUsers.get_suggestions")
|
||||
def test_feed(self, *_):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.Feed.as_view()
|
||||
|
|
|
@ -10,20 +10,21 @@ from django.test.client import RequestFactory
|
|||
from bookwyrm import models, views
|
||||
|
||||
|
||||
class BookViews(TestCase):
|
||||
"""books books books"""
|
||||
class FollowViews(TestCase):
|
||||
"""follows"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -66,15 +67,16 @@ class BookViews(TestCase):
|
|||
|
||||
def test_handle_follow_local_manually_approves(self):
|
||||
"""send a follow request"""
|
||||
rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
manually_approves_followers=True,
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
manually_approves_followers=True,
|
||||
)
|
||||
request = self.factory.post("", {"user": rat})
|
||||
request.user = self.local_user
|
||||
self.assertEqual(models.UserFollowRequest.objects.count(), 0)
|
||||
|
@ -89,14 +91,15 @@ class BookViews(TestCase):
|
|||
|
||||
def test_handle_follow_local(self):
|
||||
"""send a follow request"""
|
||||
rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
request = self.factory.post("", {"user": rat})
|
||||
request.user = self.local_user
|
||||
self.assertEqual(models.UserFollowRequest.objects.count(), 0)
|
||||
|
@ -127,7 +130,9 @@ class BookViews(TestCase):
|
|||
def test_handle_accept(self):
|
||||
"""accept a follow request"""
|
||||
self.local_user.manually_approves_followers = True
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(
|
||||
broadcast=False, update_fields=["manually_approves_followers"]
|
||||
)
|
||||
request = self.factory.post("", {"user": self.remote_user.username})
|
||||
request.user = self.local_user
|
||||
rel = models.UserFollowRequest.objects.create(
|
||||
|
@ -144,7 +149,9 @@ class BookViews(TestCase):
|
|||
def test_handle_reject(self):
|
||||
"""reject a follow request"""
|
||||
self.local_user.manually_approves_followers = True
|
||||
self.local_user.save(broadcast=False)
|
||||
self.local_user.save(
|
||||
broadcast=False, update_fields=["manually_approves_followers"]
|
||||
)
|
||||
request = self.factory.post("", {"user": self.remote_user.username})
|
||||
request.user = self.local_user
|
||||
rel = models.UserFollowRequest.objects.create(
|
||||
|
|
|
@ -13,13 +13,14 @@ class GetStartedViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.book = models.Edition.objects.create(
|
||||
parent_work=models.Work.objects.create(title="hi"),
|
||||
title="Example Edition",
|
||||
|
@ -42,7 +43,9 @@ class GetStartedViews(TestCase):
|
|||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_profile_view_post(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
|
||||
def test_profile_view_post(self, *_):
|
||||
"""save basic user details"""
|
||||
view = views.GetStartedProfile.as_view()
|
||||
form = forms.LimitedEditUserForm(instance=self.local_user)
|
||||
|
@ -84,7 +87,8 @@ class GetStartedViews(TestCase):
|
|||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_books_view_post(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
def test_books_view_post(self, _):
|
||||
"""shelve some books"""
|
||||
view = views.GetStartedBooks.as_view()
|
||||
data = {self.book.id: self.local_user.shelf_set.first().id}
|
||||
|
@ -102,7 +106,8 @@ class GetStartedViews(TestCase):
|
|||
self.assertEqual(shelfbook.book, self.book)
|
||||
self.assertEqual(shelfbook.user, self.local_user)
|
||||
|
||||
def test_users_view(self):
|
||||
@patch("bookwyrm.suggested_users.SuggestedUsers.get_suggestions")
|
||||
def test_users_view(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.GetStartedUsers.as_view()
|
||||
request = self.factory.get("")
|
||||
|
@ -114,7 +119,8 @@ class GetStartedViews(TestCase):
|
|||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_users_view_with_query(self):
|
||||
@patch("bookwyrm.suggested_users.SuggestedUsers.get_suggestions")
|
||||
def test_users_view_with_query(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.GetStartedUsers.as_view()
|
||||
request = self.factory.get("?query=rat")
|
||||
|
|
|
@ -16,22 +16,23 @@ class GoalViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Example Edition",
|
||||
remote_id="https://example.com/book/1",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import json
|
||||
from unittest.mock import patch
|
||||
import pathlib
|
||||
from django.db.models import Q
|
||||
from django.http import Http404
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
@ -13,32 +12,37 @@ from bookwyrm.settings import USER_AGENT
|
|||
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
|
||||
class ViewsHelpers(TestCase):
|
||||
"""viewing and creating statuses"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
discoverable=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
with patch("bookwyrm.suggested_users.rerank_user_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
discoverable=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=False,
|
||||
remote_id="https://example.com/users/rat",
|
||||
discoverable=True,
|
||||
inbox="https://example.com/users/rat/inbox",
|
||||
outbox="https://example.com/users/rat/outbox",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_user_task.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=False,
|
||||
remote_id="https://example.com/users/rat",
|
||||
discoverable=True,
|
||||
inbox="https://example.com/users/rat/inbox",
|
||||
outbox="https://example.com/users/rat/outbox",
|
||||
)
|
||||
self.work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Test Book",
|
||||
|
@ -53,12 +57,12 @@ class ViewsHelpers(TestCase):
|
|||
name="Test Shelf", identifier="test-shelf", user=self.local_user
|
||||
)
|
||||
|
||||
def test_get_edition(self, _):
|
||||
def test_get_edition(self, *_):
|
||||
"""given an edition or a work, returns an edition"""
|
||||
self.assertEqual(views.helpers.get_edition(self.book.id), self.book)
|
||||
self.assertEqual(views.helpers.get_edition(self.work.id), self.book)
|
||||
|
||||
def test_get_user_from_username(self, _):
|
||||
def test_get_user_from_username(self, *_):
|
||||
"""works for either localname or username"""
|
||||
self.assertEqual(
|
||||
views.helpers.get_user_from_username(self.local_user, "mouse"),
|
||||
|
@ -71,7 +75,7 @@ class ViewsHelpers(TestCase):
|
|||
with self.assertRaises(Http404):
|
||||
views.helpers.get_user_from_username(self.local_user, "mojfse@example.com")
|
||||
|
||||
def test_is_api_request(self, _):
|
||||
def test_is_api_request(self, *_):
|
||||
"""should it return html or json"""
|
||||
request = self.factory.get("/path")
|
||||
request.headers = {"Accept": "application/json"}
|
||||
|
@ -85,12 +89,12 @@ class ViewsHelpers(TestCase):
|
|||
request.headers = {"Accept": "Praise"}
|
||||
self.assertFalse(views.helpers.is_api_request(request))
|
||||
|
||||
def test_is_api_request_no_headers(self, _):
|
||||
def test_is_api_request_no_headers(self, *_):
|
||||
"""should it return html or json"""
|
||||
request = self.factory.get("/path")
|
||||
self.assertFalse(views.helpers.is_api_request(request))
|
||||
|
||||
def test_is_bookwyrm_request(self, _):
|
||||
def test_is_bookwyrm_request(self, *_):
|
||||
"""checks if a request came from a bookwyrm instance"""
|
||||
request = self.factory.get("", {"q": "Test Book"})
|
||||
self.assertFalse(views.helpers.is_bookwyrm_request(request))
|
||||
|
@ -105,7 +109,7 @@ class ViewsHelpers(TestCase):
|
|||
request = self.factory.get("", {"q": "Test Book"}, HTTP_USER_AGENT=USER_AGENT)
|
||||
self.assertTrue(views.helpers.is_bookwyrm_request(request))
|
||||
|
||||
def test_existing_user(self, _):
|
||||
def test_existing_user(self, *_):
|
||||
"""simple database lookup by username"""
|
||||
result = views.helpers.handle_remote_webfinger("@mouse@local.com")
|
||||
self.assertEqual(result, self.local_user)
|
||||
|
@ -117,7 +121,7 @@ class ViewsHelpers(TestCase):
|
|||
self.assertEqual(result, self.local_user)
|
||||
|
||||
@responses.activate
|
||||
def test_load_user(self, _):
|
||||
def test_load_user(self, *_):
|
||||
"""find a remote user using webfinger"""
|
||||
username = "mouse@example.com"
|
||||
wellknown = {
|
||||
|
@ -147,7 +151,7 @@ class ViewsHelpers(TestCase):
|
|||
self.assertIsInstance(result, models.User)
|
||||
self.assertEqual(result.username, "mouse@example.com")
|
||||
|
||||
def test_user_on_blocked_server(self, _):
|
||||
def test_user_on_blocked_server(self, *_):
|
||||
"""find a remote user using webfinger"""
|
||||
models.FederatedServer.objects.create(
|
||||
server_name="example.com", status="blocked"
|
||||
|
@ -156,7 +160,7 @@ class ViewsHelpers(TestCase):
|
|||
result = views.helpers.handle_remote_webfinger("@mouse@example.com")
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_handle_reading_status_to_read(self, _):
|
||||
def test_handle_reading_status_to_read(self, *_):
|
||||
"""posts shelve activities"""
|
||||
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
|
@ -168,7 +172,7 @@ class ViewsHelpers(TestCase):
|
|||
self.assertEqual(status.mention_books.first(), self.book)
|
||||
self.assertEqual(status.content, "wants to read")
|
||||
|
||||
def test_handle_reading_status_reading(self, _):
|
||||
def test_handle_reading_status_reading(self, *_):
|
||||
"""posts shelve activities"""
|
||||
shelf = self.local_user.shelf_set.get(identifier="reading")
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
|
@ -180,7 +184,7 @@ class ViewsHelpers(TestCase):
|
|||
self.assertEqual(status.mention_books.first(), self.book)
|
||||
self.assertEqual(status.content, "started reading")
|
||||
|
||||
def test_handle_reading_status_read(self, _):
|
||||
def test_handle_reading_status_read(self, *_):
|
||||
"""posts shelve activities"""
|
||||
shelf = self.local_user.shelf_set.get(identifier="read")
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
|
@ -192,102 +196,10 @@ class ViewsHelpers(TestCase):
|
|||
self.assertEqual(status.mention_books.first(), self.book)
|
||||
self.assertEqual(status.content, "finished reading")
|
||||
|
||||
def test_handle_reading_status_other(self, _):
|
||||
def test_handle_reading_status_other(self, *_):
|
||||
"""posts shelve activities"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
views.helpers.handle_reading_status(
|
||||
self.local_user, self.shelf, self.book, "public"
|
||||
)
|
||||
self.assertFalse(models.GeneratedNote.objects.exists())
|
||||
|
||||
def test_get_annotated_users(self, _):
|
||||
"""list of people you might know"""
|
||||
user_1 = models.User.objects.create_user(
|
||||
"nutria@local.com",
|
||||
"nutria@nutria.com",
|
||||
"nutriaword",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
discoverable=True,
|
||||
)
|
||||
user_2 = models.User.objects.create_user(
|
||||
"fish@local.com",
|
||||
"fish@fish.com",
|
||||
"fishword",
|
||||
local=True,
|
||||
localname="fish",
|
||||
)
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
# 1 shared follow
|
||||
self.local_user.following.add(user_2)
|
||||
user_1.followers.add(user_2)
|
||||
|
||||
# 1 shared book
|
||||
models.ShelfBook.objects.create(
|
||||
user=self.local_user,
|
||||
book=self.book,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
models.ShelfBook.objects.create(
|
||||
user=user_1, book=self.book, shelf=user_1.shelf_set.first()
|
||||
)
|
||||
|
||||
result = views.helpers.get_annotated_users(self.local_user)
|
||||
self.assertEqual(result.count(), 3)
|
||||
self.assertTrue(user_1 in result)
|
||||
self.assertFalse(user_2 in result)
|
||||
self.assertTrue(self.local_user in result)
|
||||
self.assertTrue(self.remote_user in result)
|
||||
|
||||
user_1_annotated = result.get(id=user_1.id)
|
||||
self.assertEqual(user_1_annotated.mutuals, 1)
|
||||
self.assertEqual(user_1_annotated.shared_books, 1)
|
||||
|
||||
remote_user_annotated = result.get(id=self.remote_user.id)
|
||||
self.assertEqual(remote_user_annotated.mutuals, 0)
|
||||
self.assertEqual(remote_user_annotated.shared_books, 0)
|
||||
|
||||
def test_get_annotated_users_counts(self, _):
|
||||
"""correct counting for multiple shared attributed"""
|
||||
user_1 = models.User.objects.create_user(
|
||||
"nutria@local.com",
|
||||
"nutria@nutria.com",
|
||||
"nutriaword",
|
||||
local=True,
|
||||
localname="nutria",
|
||||
discoverable=True,
|
||||
)
|
||||
for i in range(3):
|
||||
user = models.User.objects.create_user(
|
||||
"{:d}@local.com".format(i),
|
||||
"{:d}@nutria.com".format(i),
|
||||
"password",
|
||||
local=True,
|
||||
localname=i,
|
||||
)
|
||||
user.following.add(user_1)
|
||||
user.followers.add(self.local_user)
|
||||
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
for i in range(3):
|
||||
book = models.Edition.objects.create(
|
||||
title=i,
|
||||
parent_work=models.Work.objects.create(title=i),
|
||||
)
|
||||
models.ShelfBook.objects.create(
|
||||
user=self.local_user,
|
||||
book=book,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
models.ShelfBook.objects.create(
|
||||
user=user_1, book=book, shelf=user_1.shelf_set.first()
|
||||
)
|
||||
|
||||
result = views.helpers.get_annotated_users(
|
||||
self.local_user,
|
||||
~Q(id=self.local_user.id),
|
||||
~Q(followers=self.local_user),
|
||||
)
|
||||
self.assertEqual(result.count(), 2)
|
||||
user_1_annotated = result.get(id=user_1.id)
|
||||
self.assertEqual(user_1_annotated.mutuals, 3)
|
||||
|
|
|
@ -14,13 +14,14 @@ class ImportViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_import_page(self):
|
||||
|
|
|
@ -15,14 +15,15 @@ class InteractionViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -16,13 +16,14 @@ class InviteViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_invite_page(self):
|
||||
|
|
|
@ -16,14 +16,15 @@ class IsbnViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Test Book",
|
||||
|
|
|
@ -15,18 +15,20 @@ class LandingViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.anonymous_user = AnonymousUser
|
||||
self.anonymous_user.is_authenticated = False
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_home_page(self):
|
||||
@patch("bookwyrm.suggested_users.SuggestedUsers.get_suggestions")
|
||||
def test_home_page(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.Home.as_view()
|
||||
request = self.factory.get("")
|
||||
|
|
|
@ -17,22 +17,23 @@ class ListViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
work = models.Work.objects.create(title="Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Example Edition",
|
||||
|
|
|
@ -15,22 +15,23 @@ class ListActionViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@rat.com",
|
||||
"ratword",
|
||||
local=True,
|
||||
localname="rat",
|
||||
remote_id="https://example.com/users/rat",
|
||||
)
|
||||
|
||||
work = models.Work.objects.create(title="Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" test for app action functionality """
|
||||
from unittest.mock import patch
|
||||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
@ -13,13 +14,14 @@ class NotificationViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_notifications_page(self):
|
||||
|
|
|
@ -18,14 +18,15 @@ class OutboxView(TestCase):
|
|||
def setUp(self):
|
||||
"""we'll need some data"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Example Edition",
|
||||
|
|
|
@ -15,13 +15,14 @@ class PasswordViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.anonymous_user = AnonymousUser
|
||||
self.anonymous_user.is_authenticated = False
|
||||
models.SiteSettings.objects.create(id=1)
|
||||
|
|
|
@ -9,20 +9,22 @@ from bookwyrm import models, views
|
|||
|
||||
|
||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class ReadingViews(TestCase):
|
||||
"""viewing and creating statuses"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -40,7 +42,7 @@ class ReadingViews(TestCase):
|
|||
parent_work=self.work,
|
||||
)
|
||||
|
||||
def test_start_reading(self, _):
|
||||
def test_start_reading(self, *_):
|
||||
"""begin a book"""
|
||||
shelf = self.local_user.shelf_set.get(identifier=models.Shelf.READING)
|
||||
self.assertFalse(shelf.books.exists())
|
||||
|
@ -71,7 +73,7 @@ class ReadingViews(TestCase):
|
|||
self.assertEqual(readthrough.user, self.local_user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
|
||||
def test_start_reading_reshelf(self, _):
|
||||
def test_start_reading_reshelf(self, *_):
|
||||
"""begin a book"""
|
||||
to_read_shelf = self.local_user.shelf_set.get(identifier=models.Shelf.TO_READ)
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
|
@ -91,7 +93,7 @@ class ReadingViews(TestCase):
|
|||
self.assertFalse(to_read_shelf.books.exists())
|
||||
self.assertEqual(shelf.books.get(), self.book)
|
||||
|
||||
def test_finish_reading(self, _):
|
||||
def test_finish_reading(self, *_):
|
||||
"""begin a book"""
|
||||
shelf = self.local_user.shelf_set.get(identifier=models.Shelf.READ_FINISHED)
|
||||
self.assertFalse(shelf.books.exists())
|
||||
|
@ -127,7 +129,7 @@ class ReadingViews(TestCase):
|
|||
self.assertEqual(readthrough.user, self.local_user)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
|
||||
def test_edit_readthrough(self, _):
|
||||
def test_edit_readthrough(self, *_):
|
||||
"""adding dates to an ongoing readthrough"""
|
||||
start = timezone.make_aware(dateutil.parser.parse("2021-01-03"))
|
||||
readthrough = models.ReadThrough.objects.create(
|
||||
|
@ -154,7 +156,7 @@ class ReadingViews(TestCase):
|
|||
self.assertEqual(readthrough.finish_date.day, 7)
|
||||
self.assertEqual(readthrough.book, self.book)
|
||||
|
||||
def test_delete_readthrough(self, _):
|
||||
def test_delete_readthrough(self, *_):
|
||||
"""remove a readthrough"""
|
||||
readthrough = models.ReadThrough.objects.create(
|
||||
book=self.book, user=self.local_user
|
||||
|
@ -171,7 +173,7 @@ class ReadingViews(TestCase):
|
|||
views.delete_readthrough(request)
|
||||
self.assertFalse(models.ReadThrough.objects.filter(id=readthrough.id).exists())
|
||||
|
||||
def test_create_readthrough(self, _):
|
||||
def test_create_readthrough(self, *_):
|
||||
"""adding new read dates"""
|
||||
request = self.factory.post(
|
||||
"",
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.utils import timezone
|
|||
from bookwyrm import models
|
||||
|
||||
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
class ReadThrough(TestCase):
|
||||
"""readthrough tests"""
|
||||
|
@ -21,14 +22,15 @@ class ReadThrough(TestCase):
|
|||
title="Example Edition", parent_work=self.work
|
||||
)
|
||||
|
||||
self.user = models.User.objects.create_user(
|
||||
"cinco", "cinco@example.com", "seissiete", local=True, localname="cinco"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"cinco", "cinco@example.com", "seissiete", local=True, localname="cinco"
|
||||
)
|
||||
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_create_basic_readthrough(self, delay_mock):
|
||||
def test_create_basic_readthrough(self, delay_mock, _):
|
||||
"""A basic readthrough doesn't create a progress update"""
|
||||
self.assertEqual(self.edition.readthrough_set.count(), 0)
|
||||
|
||||
|
@ -49,7 +51,7 @@ class ReadThrough(TestCase):
|
|||
self.assertEqual(readthroughs[0].finish_date, None)
|
||||
self.assertEqual(delay_mock.call_count, 1)
|
||||
|
||||
def test_create_progress_readthrough(self, delay_mock):
|
||||
def test_create_progress_readthrough(self, delay_mock, _):
|
||||
"""a readthrough with progress"""
|
||||
self.assertEqual(self.edition.readthrough_set.count(), 0)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" test for app action functionality """
|
||||
from unittest.mock import patch
|
||||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
@ -12,20 +13,21 @@ class ReportViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="rat",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com",
|
||||
"rat@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="rat",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_reports_page(self):
|
||||
|
@ -114,7 +116,9 @@ class ReportViews(TestCase):
|
|||
report.refresh_from_db()
|
||||
self.assertFalse(report.resolved)
|
||||
|
||||
def test_suspend_user(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_suspend_user(self, *_):
|
||||
"""toggle whether a user is able to log in"""
|
||||
self.assertTrue(self.rat.is_active)
|
||||
request = self.factory.post("")
|
||||
|
|
|
@ -14,9 +14,10 @@ class RssFeedView(TestCase):
|
|||
"""test data"""
|
||||
self.site = models.SiteSettings.objects.create()
|
||||
|
||||
self.user = models.User.objects.create_user(
|
||||
"rss_user", "rss@test.rss", "password", local=True
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.user = models.User.objects.create_user(
|
||||
"rss_user", "rss@test.rss", "password", local=True
|
||||
)
|
||||
|
||||
work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
|
|
|
@ -19,14 +19,15 @@ class Views(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Test Book",
|
||||
|
|
|
@ -10,20 +10,22 @@ from bookwyrm.activitypub import ActivitypubResponse
|
|||
|
||||
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
class ShelfViews(TestCase):
|
||||
"""tag views"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
self.work = models.Work.objects.create(title="Test Work")
|
||||
self.book = models.Edition.objects.create(
|
||||
title="Example Edition",
|
||||
|
@ -36,7 +38,7 @@ class ShelfViews(TestCase):
|
|||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_shelf_page(self, _):
|
||||
def test_shelf_page(self, *_):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.Shelf.as_view()
|
||||
shelf = self.local_user.shelf_set.first()
|
||||
|
@ -63,7 +65,7 @@ class ShelfViews(TestCase):
|
|||
self.assertIsInstance(result, ActivitypubResponse)
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_edit_shelf_privacy(self, _):
|
||||
def test_edit_shelf_privacy(self, *_):
|
||||
"""set name or privacy on shelf"""
|
||||
view = views.Shelf.as_view()
|
||||
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
||||
|
@ -83,7 +85,7 @@ class ShelfViews(TestCase):
|
|||
|
||||
self.assertEqual(shelf.privacy, "unlisted")
|
||||
|
||||
def test_edit_shelf_name(self, _):
|
||||
def test_edit_shelf_name(self, *_):
|
||||
"""change the name of an editable shelf"""
|
||||
view = views.Shelf.as_view()
|
||||
shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user)
|
||||
|
@ -100,7 +102,7 @@ class ShelfViews(TestCase):
|
|||
self.assertEqual(shelf.name, "cool name")
|
||||
self.assertEqual(shelf.identifier, "testshelf-%d" % shelf.id)
|
||||
|
||||
def test_edit_shelf_name_not_editable(self, _):
|
||||
def test_edit_shelf_name_not_editable(self, *_):
|
||||
"""can't change the name of an non-editable shelf"""
|
||||
view = views.Shelf.as_view()
|
||||
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
||||
|
@ -115,7 +117,7 @@ class ShelfViews(TestCase):
|
|||
|
||||
self.assertEqual(shelf.name, "To Read")
|
||||
|
||||
def test_handle_shelve(self, _):
|
||||
def test_handle_shelve(self, *_):
|
||||
"""shelve a book"""
|
||||
request = self.factory.post(
|
||||
"", {"book": self.book.id, "shelf": self.shelf.identifier}
|
||||
|
@ -133,7 +135,7 @@ class ShelfViews(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
self.assertEqual(self.shelf.books.get(), self.book)
|
||||
|
||||
def test_handle_shelve_to_read(self, _):
|
||||
def test_handle_shelve_to_read(self, *_):
|
||||
"""special behavior for the to-read shelf"""
|
||||
shelf = models.Shelf.objects.get(identifier="to-read")
|
||||
request = self.factory.post(
|
||||
|
@ -146,7 +148,7 @@ class ShelfViews(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
self.assertEqual(shelf.books.get(), self.book)
|
||||
|
||||
def test_handle_shelve_reading(self, _):
|
||||
def test_handle_shelve_reading(self, *_):
|
||||
"""special behavior for the reading shelf"""
|
||||
shelf = models.Shelf.objects.get(identifier="reading")
|
||||
request = self.factory.post(
|
||||
|
@ -159,7 +161,7 @@ class ShelfViews(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
self.assertEqual(shelf.books.get(), self.book)
|
||||
|
||||
def test_handle_shelve_read(self, _):
|
||||
def test_handle_shelve_read(self, *_):
|
||||
"""special behavior for the read shelf"""
|
||||
shelf = models.Shelf.objects.get(identifier="read")
|
||||
request = self.factory.post(
|
||||
|
@ -172,7 +174,7 @@ class ShelfViews(TestCase):
|
|||
# make sure the book is on the shelf
|
||||
self.assertEqual(shelf.books.get(), self.book)
|
||||
|
||||
def test_handle_unshelve(self, _):
|
||||
def test_handle_unshelve(self, *_):
|
||||
"""remove a book from a shelf"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
models.ShelfBook.objects.create(
|
||||
|
|
|
@ -9,6 +9,7 @@ from bookwyrm.settings import DOMAIN
|
|||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||
class StatusViews(TestCase):
|
||||
"""viewing and creating statuses"""
|
||||
|
@ -16,14 +17,15 @@ class StatusViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.com",
|
||||
"mouseword",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
remote_id="https://example.com/users/mouse",
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server"):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
"rat",
|
||||
|
@ -43,7 +45,7 @@ class StatusViews(TestCase):
|
|||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_handle_status(self, _):
|
||||
def test_handle_status(self, *_):
|
||||
"""create a status"""
|
||||
view = views.CreateStatus.as_view()
|
||||
form = forms.CommentForm(
|
||||
|
@ -66,7 +68,7 @@ class StatusViews(TestCase):
|
|||
self.assertEqual(status.user, self.local_user)
|
||||
self.assertEqual(status.book, self.book)
|
||||
|
||||
def test_handle_status_reply(self, _):
|
||||
def test_handle_status_reply(self, *_):
|
||||
"""create a status in reply to an existing status"""
|
||||
view = views.CreateStatus.as_view()
|
||||
user = models.User.objects.create_user(
|
||||
|
@ -96,7 +98,7 @@ class StatusViews(TestCase):
|
|||
self.assertEqual(status.user, user)
|
||||
self.assertEqual(models.Notification.objects.get().user, self.local_user)
|
||||
|
||||
def test_handle_status_mentions(self, _):
|
||||
def test_handle_status_mentions(self, *_):
|
||||
"""@mention a user in a post"""
|
||||
view = views.CreateStatus.as_view()
|
||||
user = models.User.objects.create_user(
|
||||
|
@ -128,7 +130,7 @@ class StatusViews(TestCase):
|
|||
status.content, '<p>hi <a href="%s">@rat</a></p>' % user.remote_id
|
||||
)
|
||||
|
||||
def test_handle_status_reply_with_mentions(self, _):
|
||||
def test_handle_status_reply_with_mentions(self, *_):
|
||||
"""reply to a post with an @mention'ed user"""
|
||||
view = views.CreateStatus.as_view()
|
||||
user = models.User.objects.create_user(
|
||||
|
@ -172,7 +174,7 @@ class StatusViews(TestCase):
|
|||
self.assertFalse(self.remote_user in reply.mention_users.all())
|
||||
self.assertTrue(self.local_user in reply.mention_users.all())
|
||||
|
||||
def test_delete_and_redraft(self, _):
|
||||
def test_delete_and_redraft(self, *_):
|
||||
"""delete and re-draft a status"""
|
||||
view = views.DeleteAndRedraft.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -193,7 +195,7 @@ class StatusViews(TestCase):
|
|||
status.refresh_from_db()
|
||||
self.assertTrue(status.deleted)
|
||||
|
||||
def test_delete_and_redraft_invalid_status_type_rating(self, _):
|
||||
def test_delete_and_redraft_invalid_status_type_rating(self, *_):
|
||||
"""you can't redraft generated statuses"""
|
||||
view = views.DeleteAndRedraft.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -213,7 +215,7 @@ class StatusViews(TestCase):
|
|||
status.refresh_from_db()
|
||||
self.assertFalse(status.deleted)
|
||||
|
||||
def test_delete_and_redraft_invalid_status_type_generated_note(self, _):
|
||||
def test_delete_and_redraft_invalid_status_type_generated_note(self, *_):
|
||||
"""you can't redraft generated statuses"""
|
||||
view = views.DeleteAndRedraft.as_view()
|
||||
request = self.factory.post("")
|
||||
|
@ -233,7 +235,7 @@ class StatusViews(TestCase):
|
|||
status.refresh_from_db()
|
||||
self.assertFalse(status.deleted)
|
||||
|
||||
def test_find_mentions(self, _):
|
||||
def test_find_mentions(self, *_):
|
||||
"""detect and look up @ mentions of users"""
|
||||
user = models.User.objects.create_user(
|
||||
"nutria@%s" % DOMAIN,
|
||||
|
@ -279,7 +281,7 @@ class StatusViews(TestCase):
|
|||
("@nutria@%s" % DOMAIN, user),
|
||||
)
|
||||
|
||||
def test_format_links(self, _):
|
||||
def test_format_links(self, *_):
|
||||
"""find and format urls into a tags"""
|
||||
url = "http://www.fish.com/"
|
||||
self.assertEqual(
|
||||
|
@ -302,7 +304,7 @@ class StatusViews(TestCase):
|
|||
"?q=arkady+strugatsky&mode=everything</a>" % url,
|
||||
)
|
||||
|
||||
def test_to_markdown(self, _):
|
||||
def test_to_markdown(self, *_):
|
||||
"""this is mostly handled in other places, but nonetheless"""
|
||||
text = "_hi_ and http://fish.com is <marquee>rad</marquee>"
|
||||
result = views.status.to_markdown(text)
|
||||
|
@ -311,7 +313,7 @@ class StatusViews(TestCase):
|
|||
'<p><em>hi</em> and <a href="http://fish.com">fish.com</a> ' "is rad</p>",
|
||||
)
|
||||
|
||||
def test_to_markdown_detect_url(self, _):
|
||||
def test_to_markdown_detect_url(self, *_):
|
||||
"""this is mostly handled in other places, but nonetheless"""
|
||||
text = "http://fish.com/@hello#okay"
|
||||
result = views.status.to_markdown(text)
|
||||
|
@ -320,13 +322,13 @@ class StatusViews(TestCase):
|
|||
'<p><a href="http://fish.com/@hello#okay">fish.com/@hello#okay</a></p>',
|
||||
)
|
||||
|
||||
def test_to_markdown_link(self, _):
|
||||
def test_to_markdown_link(self, *_):
|
||||
"""this is mostly handled in other places, but nonetheless"""
|
||||
text = "[hi](http://fish.com) is <marquee>rad</marquee>"
|
||||
result = views.status.to_markdown(text)
|
||||
self.assertEqual(result, '<p><a href="http://fish.com">hi</a> ' "is rad</p>")
|
||||
|
||||
def test_handle_delete_status(self, mock):
|
||||
def test_handle_delete_status(self, mock, *_):
|
||||
"""marks a status as deleted"""
|
||||
view = views.DeleteStatus.as_view()
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
|
@ -346,7 +348,7 @@ class StatusViews(TestCase):
|
|||
status.refresh_from_db()
|
||||
self.assertTrue(status.deleted)
|
||||
|
||||
def test_handle_delete_status_permission_denied(self, _):
|
||||
def test_handle_delete_status_permission_denied(self, *_):
|
||||
"""marks a status as deleted"""
|
||||
view = views.DeleteStatus.as_view()
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
|
@ -360,7 +362,7 @@ class StatusViews(TestCase):
|
|||
status.refresh_from_db()
|
||||
self.assertFalse(status.deleted)
|
||||
|
||||
def test_handle_delete_status_moderator(self, mock):
|
||||
def test_handle_delete_status_moderator(self, mock, _):
|
||||
"""marks a status as deleted"""
|
||||
view = views.DeleteStatus.as_view()
|
||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||
|
|
|
@ -15,13 +15,14 @@ class UpdateViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_get_notification_count(self):
|
||||
|
|
|
@ -17,23 +17,25 @@ class UserViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.rat = models.User.objects.create_user(
|
||||
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
self.book = models.Edition.objects.create(title="test")
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
models.ShelfBook.objects.create(
|
||||
book=self.book,
|
||||
user=self.local_user,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
models.ShelfBook.objects.create(
|
||||
book=self.book,
|
||||
user=self.local_user,
|
||||
shelf=self.local_user.shelf_set.first(),
|
||||
)
|
||||
|
||||
models.SiteSettings.objects.create()
|
||||
self.anonymous_user = AnonymousUser
|
||||
|
@ -94,7 +96,8 @@ class UserViews(TestCase):
|
|||
self.assertIsInstance(result, ActivitypubResponse)
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_followers_page_blocked(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
def test_followers_page_blocked(self, _):
|
||||
"""there are so many views, this just makes sure it LOADS"""
|
||||
view = views.Followers.as_view()
|
||||
request = self.factory.get("")
|
||||
|
|
|
@ -14,13 +14,14 @@ class UserAdminViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.SiteSettings.objects.create()
|
||||
|
||||
def test_user_admin_list_page(self):
|
||||
|
@ -47,7 +48,9 @@ class UserAdminViews(TestCase):
|
|||
result.render()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_user_admin_page_post(self):
|
||||
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||
def test_user_admin_page_post(self, *_):
|
||||
"""set the user's group"""
|
||||
group = Group.objects.create(name="editor")
|
||||
self.assertEqual(
|
||||
|
|
|
@ -16,16 +16,17 @@ class UserViews(TestCase):
|
|||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.User.objects.create_user(
|
||||
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
models.User.objects.create_user(
|
||||
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
|
||||
)
|
||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||
models.User.objects.create_user(
|
||||
"rat",
|
||||
|
|
|
@ -50,7 +50,7 @@ class Login(View):
|
|||
# successful login
|
||||
login(request, user)
|
||||
user.last_active_date = timezone.now()
|
||||
user.save(broadcast=False)
|
||||
user.save(broadcast=False, update_fields=["last_active_date"])
|
||||
return redirect(request.GET.get("next", "/"))
|
||||
|
||||
# login errors
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.template.response import TemplateResponse
|
|||
from django.views import View
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
from .helpers import get_annotated_users
|
||||
from bookwyrm import suggested_users
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
@ -23,7 +23,7 @@ class Directory(View):
|
|||
if scope == "local":
|
||||
filters["local"] = True
|
||||
|
||||
users = get_annotated_users(request.user, **filters)
|
||||
users = suggested_users.get_annotated_users(request.user, **filters)
|
||||
sort = request.GET.get("sort")
|
||||
if sort == "recent":
|
||||
users = users.order_by("-last_active_date")
|
||||
|
|
|
@ -11,7 +11,8 @@ from django.views import View
|
|||
from bookwyrm import activitystreams, forms, models
|
||||
from bookwyrm.activitypub import ActivitypubResponse
|
||||
from bookwyrm.settings import PAGE_LENGTH, STREAMS
|
||||
from .helpers import get_user_from_username, privacy_filter, get_suggested_users
|
||||
from bookwyrm.suggested_users import suggested_users
|
||||
from .helpers import get_user_from_username, privacy_filter
|
||||
from .helpers import is_api_request, is_bookwyrm_request
|
||||
|
||||
|
||||
|
@ -28,14 +29,14 @@ class Feed(View):
|
|||
activities = activitystreams.streams[tab].get_activity_stream(request.user)
|
||||
paginated = Paginator(activities, PAGE_LENGTH)
|
||||
|
||||
suggested_users = get_suggested_users(request.user)
|
||||
suggestions = suggested_users.get_suggestions(request.user)
|
||||
|
||||
data = {
|
||||
**feed_page_data(request.user),
|
||||
**{
|
||||
"user": request.user,
|
||||
"activities": paginated.get_page(request.GET.get("page")),
|
||||
"suggested_users": suggested_users,
|
||||
"suggested_users": suggestions,
|
||||
"tab": tab,
|
||||
"goal_form": forms.GoalForm(),
|
||||
"path": "/%s" % tab,
|
||||
|
|
|
@ -13,7 +13,7 @@ from django.views import View
|
|||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from .helpers import get_suggested_users
|
||||
from bookwyrm.suggested_users import suggested_users
|
||||
from .edit_user import save_user_form
|
||||
|
||||
|
||||
|
@ -118,11 +118,12 @@ class GetStartedUsers(View):
|
|||
)
|
||||
.order_by("-similarity")[:5]
|
||||
)
|
||||
data = {"no_results": not user_results}
|
||||
|
||||
if user_results.count() < 5:
|
||||
user_results = list(user_results) + list(get_suggested_users(request.user))
|
||||
user_results = list(user_results) + suggested_users.get_suggestions(
|
||||
request.user
|
||||
)
|
||||
|
||||
data = {
|
||||
"suggested_users": user_results,
|
||||
}
|
||||
data["suggested_users"] = user_results
|
||||
return TemplateResponse(request, "get_started/users.html", data)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import re
|
||||
from requests import HTTPError
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db.models import Count, Max, Q
|
||||
from django.db.models import Max, Q
|
||||
from django.http import Http404
|
||||
|
||||
from bookwyrm import activitypub, models
|
||||
|
@ -177,47 +177,3 @@ def get_discover_books():
|
|||
.order_by("-review__published_date__max")[:6]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_suggested_users(user):
|
||||
"""bookwyrm users you don't already know"""
|
||||
return (
|
||||
get_annotated_users(
|
||||
user,
|
||||
~Q(id=user.id),
|
||||
~Q(followers=user),
|
||||
~Q(follower_requests=user),
|
||||
bookwyrm_user=True,
|
||||
)
|
||||
.order_by("-mutuals", "-last_active_date")
|
||||
.all()[:5]
|
||||
)
|
||||
|
||||
|
||||
def get_annotated_users(user, *args, **kwargs):
|
||||
"""Users, annotated with things they have in common"""
|
||||
return (
|
||||
models.User.objects.filter(discoverable=True, is_active=True, *args, **kwargs)
|
||||
.exclude(Q(id__in=user.blocks.all()) | Q(blocks=user))
|
||||
.annotate(
|
||||
mutuals=Count(
|
||||
"followers",
|
||||
filter=Q(
|
||||
~Q(id=user.id),
|
||||
~Q(id__in=user.following.all()),
|
||||
followers__in=user.following.all(),
|
||||
),
|
||||
distinct=True,
|
||||
),
|
||||
shared_books=Count(
|
||||
"shelfbook",
|
||||
filter=Q(
|
||||
~Q(id=user.id),
|
||||
shelfbook__book__parent_work__in=[
|
||||
s.book.parent_work for s in user.shelfbook_set.all()
|
||||
],
|
||||
),
|
||||
distinct=True,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
|
|
@ -76,7 +76,7 @@ class PasswordReset(View):
|
|||
return TemplateResponse(request, "password_reset.html", data)
|
||||
|
||||
user.set_password(new_password)
|
||||
user.save(broadcast=False)
|
||||
user.save(broadcast=False, update_fields=["password"])
|
||||
login(request, user)
|
||||
reset_code.delete()
|
||||
return redirect("/")
|
||||
|
@ -100,6 +100,6 @@ class ChangePassword(View):
|
|||
return redirect("preferences/password")
|
||||
|
||||
request.user.set_password(new_password)
|
||||
request.user.save(broadcast=False)
|
||||
request.user.save(broadcast=False, update_fields=["password"])
|
||||
login(request, request.user)
|
||||
return redirect(request.user.local_path)
|
||||
|
|
4
bw-dev
4
bw-dev
|
@ -123,6 +123,9 @@ case "$CMD" in
|
|||
populate_streams)
|
||||
runweb python manage.py populate_streams
|
||||
;;
|
||||
populate_suggestions)
|
||||
runweb python manage.py populate_suggestions
|
||||
;;
|
||||
generate_preview_images)
|
||||
runweb python manage.py generate_preview_images $@
|
||||
;;
|
||||
|
@ -167,6 +170,7 @@ case "$CMD" in
|
|||
echo " clean"
|
||||
echo " black"
|
||||
echo " populate_streams"
|
||||
echo " populate_suggestions"
|
||||
echo " generate_preview_images [--all]"
|
||||
echo " copy_media_to_s3"
|
||||
echo " set_cors_to_s3 [cors file]"
|
||||
|
|
|
@ -26,4 +26,5 @@ app.autodiscover_tasks(["bookwyrm"], related_name="emailing")
|
|||
app.autodiscover_tasks(["bookwyrm"], related_name="goodreads_import")
|
||||
app.autodiscover_tasks(["bookwyrm"], related_name="preview_images")
|
||||
app.autodiscover_tasks(["bookwyrm"], related_name="models.user")
|
||||
app.autodiscover_tasks(["bookwyrm"], related_name="suggested_users")
|
||||
app.autodiscover_tasks(["bookwyrm"], related_name="views.inbox")
|
||||
|
|
Loading…
Reference in a new issue