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
|
value = field.default
|
||||||
setattr(self, field.name, value)
|
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):
|
def to_model(self, model=None, instance=None, allow_create=True, save=True):
|
||||||
"""convert from an activity to a model instance"""
|
"""convert from an activity to a model instance"""
|
||||||
model = model or get_model_from_type(self.type)
|
model = model or get_model_from_type(self.type)
|
||||||
|
@ -126,27 +127,36 @@ class ActivityObject:
|
||||||
return None
|
return None
|
||||||
instance = instance or model()
|
instance = instance or model()
|
||||||
|
|
||||||
|
# keep track of what we've changed
|
||||||
|
update_fields = []
|
||||||
for field in instance.simple_fields:
|
for field in instance.simple_fields:
|
||||||
try:
|
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:
|
except AttributeError as e:
|
||||||
raise ActivitySerializerError(e)
|
raise ActivitySerializerError(e)
|
||||||
|
|
||||||
# image fields have to be set after other fields because they can save
|
# image fields have to be set after other fields because they can save
|
||||||
# too early and jank up users
|
# too early and jank up users
|
||||||
for field in instance.image_fields:
|
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:
|
if not save:
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
with transaction.atomic():
|
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
|
# we can't set many to many and reverse fields on an unsaved object
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
instance.save(broadcast=False)
|
instance.save(broadcast=False, update_fields=update_fields)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
instance.save()
|
instance.save(update_fields=update_fields)
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
raise ActivitySerializerError(e)
|
raise ActivitySerializerError(e)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
""" Re-create user streams """
|
""" Re-create user streams """
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
import redis
|
from bookwyrm import activitystreams, models
|
||||||
|
|
||||||
from bookwyrm import activitystreams, models, settings
|
|
||||||
|
|
||||||
r = redis.Redis(
|
|
||||||
host=settings.REDIS_ACTIVITY_HOST, port=settings.REDIS_ACTIVITY_PORT, db=0
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def populate_streams():
|
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):
|
def save(self, *args, **kwargs):
|
||||||
"""update user active time"""
|
"""update user active time"""
|
||||||
self.user.last_active_date = timezone.now()
|
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)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
if self.status.user.local and self.status.user != self.user:
|
if self.status.user.local and self.status.user != self.user:
|
||||||
|
|
|
@ -67,7 +67,7 @@ class ActivitypubFieldMixin:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def set_field_from_activity(self, instance, data):
|
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:
|
try:
|
||||||
value = getattr(data, self.get_activitypub_field())
|
value = getattr(data, self.get_activitypub_field())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -77,8 +77,14 @@ class ActivitypubFieldMixin:
|
||||||
value = getattr(data, "actor")
|
value = getattr(data, "actor")
|
||||||
formatted = self.field_from_activity(value)
|
formatted = self.field_from_activity(value)
|
||||||
if formatted is None or formatted is MISSING or formatted == {}:
|
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)
|
setattr(instance, self.name, formatted)
|
||||||
|
return True
|
||||||
|
|
||||||
def set_activity_from_field(self, activity, instance):
|
def set_activity_from_field(self, activity, instance):
|
||||||
"""update the json object"""
|
"""update the json object"""
|
||||||
|
@ -205,6 +211,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def set_field_from_activity(self, instance, data):
|
def set_field_from_activity(self, instance, data):
|
||||||
|
original = getattr(instance, self.name)
|
||||||
to = data.to
|
to = data.to
|
||||||
cc = data.cc
|
cc = data.cc
|
||||||
if to == [self.public]:
|
if to == [self.public]:
|
||||||
|
@ -215,6 +222,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
|
||||||
setattr(instance, self.name, "unlisted")
|
setattr(instance, self.name, "unlisted")
|
||||||
else:
|
else:
|
||||||
setattr(instance, self.name, "followers")
|
setattr(instance, self.name, "followers")
|
||||||
|
return original == getattr(instance, self.name)
|
||||||
|
|
||||||
def set_activity_from_field(self, activity, instance):
|
def set_activity_from_field(self, activity, instance):
|
||||||
# explicitly to anyone mentioned (statuses only)
|
# explicitly to anyone mentioned (statuses only)
|
||||||
|
@ -270,9 +278,10 @@ class ManyToManyField(ActivitypubFieldMixin, models.ManyToManyField):
|
||||||
value = getattr(data, self.get_activitypub_field())
|
value = getattr(data, self.get_activitypub_field())
|
||||||
formatted = self.field_from_activity(value)
|
formatted = self.field_from_activity(value)
|
||||||
if formatted is None or formatted is MISSING:
|
if formatted is None or formatted is MISSING:
|
||||||
return
|
return False
|
||||||
getattr(instance, self.name).set(formatted)
|
getattr(instance, self.name).set(formatted)
|
||||||
instance.save(broadcast=False)
|
instance.save(broadcast=False)
|
||||||
|
return True
|
||||||
|
|
||||||
def field_to_activity(self, value):
|
def field_to_activity(self, value):
|
||||||
if self.link_only:
|
if self.link_only:
|
||||||
|
@ -373,8 +382,10 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
|
||||||
value = getattr(data, self.get_activitypub_field())
|
value = getattr(data, self.get_activitypub_field())
|
||||||
formatted = self.field_from_activity(value)
|
formatted = self.field_from_activity(value)
|
||||||
if formatted is None or formatted is MISSING:
|
if formatted is None or formatted is MISSING:
|
||||||
return
|
return False
|
||||||
|
|
||||||
getattr(instance, self.name).save(*formatted, save=save)
|
getattr(instance, self.name).save(*formatted, save=save)
|
||||||
|
return True
|
||||||
|
|
||||||
def set_activity_from_field(self, activity, instance):
|
def set_activity_from_field(self, activity, instance):
|
||||||
value = getattr(instance, self.name)
|
value = getattr(instance, self.name)
|
||||||
|
|
|
@ -30,7 +30,7 @@ class ReadThrough(BookWyrmModel):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""update user active time"""
|
"""update user active time"""
|
||||||
self.user.last_active_date = timezone.now()
|
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)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def create_update(self):
|
def create_update(self):
|
||||||
|
@ -55,5 +55,5 @@ class ProgressUpdate(BookWyrmModel):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""update user active time"""
|
"""update user active time"""
|
||||||
self.user.last_active_date = timezone.now()
|
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)
|
super().save(*args, **kwargs)
|
||||||
|
|
|
@ -243,7 +243,6 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
# generate a username that uses the domain (webfinger format)
|
# generate a username that uses the domain (webfinger format)
|
||||||
actor_parts = urlparse(self.remote_id)
|
actor_parts = urlparse(self.remote_id)
|
||||||
self.username = "%s@%s" % (self.username, actor_parts.netloc)
|
self.username = "%s@%s" % (self.username, actor_parts.netloc)
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
# this user already exists, no need to populate fields
|
# this user already exists, no need to populate fields
|
||||||
if not created:
|
if not created:
|
||||||
|
@ -276,7 +275,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
self.key_pair = KeyPair.objects.create(
|
self.key_pair = KeyPair.objects.create(
|
||||||
remote_id="%s/#main-key" % self.remote_id
|
remote_id="%s/#main-key" % self.remote_id
|
||||||
)
|
)
|
||||||
self.save(broadcast=False)
|
self.save(broadcast=False, update_fields=["key_pair"])
|
||||||
|
|
||||||
shelves = [
|
shelves = [
|
||||||
{
|
{
|
||||||
|
@ -406,7 +405,7 @@ def set_remote_server(user_id):
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
actor_parts = urlparse(user.remote_id)
|
actor_parts = urlparse(user.remote_id)
|
||||||
user.federated_server = get_or_create_remote_server(actor_parts.netloc)
|
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:
|
if user.bookwyrm_user and user.outbox:
|
||||||
get_remote_reviews.delay(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))
|
save_without_broadcast = isinstance(instance, (models.Book, models.User))
|
||||||
if save_without_broadcast:
|
if save_without_broadcast:
|
||||||
instance.save(broadcast=False)
|
instance.save(broadcast=False, update_fields=["preview_image"])
|
||||||
else:
|
else:
|
||||||
instance.save()
|
instance.save(update_fields=["preview_image"])
|
||||||
|
|
||||||
# Clean up old file after saving
|
# Clean up old file after saving
|
||||||
if old_path and default_storage.exists(old_path):
|
if old_path and default_storage.exists(old_path):
|
||||||
|
|
|
@ -50,15 +50,15 @@ class RedisStore(ABC):
|
||||||
pipeline.execute()
|
pipeline.execute()
|
||||||
|
|
||||||
def bulk_remove_objects_from_store(self, objs, store):
|
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()
|
pipeline = r.pipeline()
|
||||||
for obj in objs[: self.max_length]:
|
for obj in objs[: self.max_length]:
|
||||||
pipeline.zrem(store, -1, obj.id)
|
pipeline.zrem(store, -1, obj.id)
|
||||||
pipeline.execute()
|
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"""
|
"""load the values in a store"""
|
||||||
return r.zrevrange(store, 0, -1)
|
return r.zrevrange(store, 0, -1, **kwargs)
|
||||||
|
|
||||||
def populate_store(self, store):
|
def populate_store(self, store):
|
||||||
"""go from zero to a 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 #}
|
{# activity feed #}
|
||||||
{% if not activities %}
|
{% if not activities %}
|
||||||
|
<div class="block content">
|
||||||
<p>{% trans "There aren't any activities right now! Try following a user to get started" %}</p>
|
<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 %}
|
{% endif %}
|
||||||
|
|
||||||
{% for activity in activities %}
|
{% for activity in activities %}
|
||||||
|
|
||||||
{% if not activities.number > 1 and forloop.counter0 == 2 and suggested_users %}
|
{% if not activities.number > 1 and forloop.counter0 == 2 and suggested_users %}
|
||||||
{# suggested users on the first page, two statuses down #}
|
{# 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 %}
|
{% 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>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
{% include 'snippets/status/status.html' with status=activity %}
|
{% include 'snippets/status/status.html' with status=activity %}
|
||||||
|
|
|
@ -1,25 +1,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load utilities %}
|
<section class="block">
|
||||||
{% load humanize %}
|
<h2 class="title is-5">{% trans "Who to follow" %}</h2>
|
||||||
<div class="columns is-mobile scroll-x mb-0">
|
{% include 'snippets/suggested_users.html' with suggested_users=suggested_users %}
|
||||||
{% for user in suggested_users %}
|
<a class="help" href="{% url 'directory' %}">View directory <span class="icon icon-arrow-right"></a>
|
||||||
<div class="column is-flex is-flex-grow-0">
|
</section>
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<form class="field has-addons" method="get" action="{% url 'get-started-users' %}">
|
<form class="field has-addons" method="get" action="{% url 'get-started-users' %}">
|
||||||
<div class="control">
|
<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' %}">
|
<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>
|
<p class="help">{% blocktrans with query=request.GET.query %}No users found for "{{ query }}"{% endblocktrans %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% include 'feed/suggested_users.html' with suggested_users=suggested_users %}
|
{% include 'snippets/suggested_users.html' with suggested_users=suggested_users %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% 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 %}
|
{% blocktrans with username=user.display_name %}{{ username }} isn't following any users{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import datetime
|
"""test author serializer"""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
|
@ -20,16 +20,20 @@ from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@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):
|
class BaseActivity(TestCase):
|
||||||
"""the super class for model-linked activitypub dataclasses"""
|
"""the super class for model-linked activitypub dataclasses"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we're probably going to re-use this so why copy/paste"""
|
"""we're probably going to re-use this so why copy/paste"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
self.user.remote_id = "http://example.com/a/b"
|
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")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||||
self.userdata = json.loads(datafile.read_bytes())
|
self.userdata = json.loads(datafile.read_bytes())
|
||||||
|
@ -44,24 +48,24 @@ class BaseActivity(TestCase):
|
||||||
image.save(output, format=image.format)
|
image.save(output, format=image.format)
|
||||||
self.image_data = output.getvalue()
|
self.image_data = output.getvalue()
|
||||||
|
|
||||||
def test_init(self, _):
|
def test_init(self, *_):
|
||||||
"""simple successfuly init"""
|
"""simple successfuly init"""
|
||||||
instance = ActivityObject(id="a", type="b")
|
instance = ActivityObject(id="a", type="b")
|
||||||
self.assertTrue(hasattr(instance, "id"))
|
self.assertTrue(hasattr(instance, "id"))
|
||||||
self.assertTrue(hasattr(instance, "type"))
|
self.assertTrue(hasattr(instance, "type"))
|
||||||
|
|
||||||
def test_init_missing(self, _):
|
def test_init_missing(self, *_):
|
||||||
"""init with missing required params"""
|
"""init with missing required params"""
|
||||||
with self.assertRaises(ActivitySerializerError):
|
with self.assertRaises(ActivitySerializerError):
|
||||||
ActivityObject()
|
ActivityObject()
|
||||||
|
|
||||||
def test_init_extra_fields(self, _):
|
def test_init_extra_fields(self, *_):
|
||||||
"""init ignoring additional fields"""
|
"""init ignoring additional fields"""
|
||||||
instance = ActivityObject(id="a", type="b", fish="c")
|
instance = ActivityObject(id="a", type="b", fish="c")
|
||||||
self.assertTrue(hasattr(instance, "id"))
|
self.assertTrue(hasattr(instance, "id"))
|
||||||
self.assertTrue(hasattr(instance, "type"))
|
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"""
|
"""replace an existing required field with a default field"""
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
|
@ -74,7 +78,7 @@ class BaseActivity(TestCase):
|
||||||
self.assertEqual(instance.id, "a")
|
self.assertEqual(instance.id, "a")
|
||||||
self.assertEqual(instance.type, "TestObject")
|
self.assertEqual(instance.type, "TestObject")
|
||||||
|
|
||||||
def test_serialize(self, _):
|
def test_serialize(self, *_):
|
||||||
"""simple function for converting dataclass to dict"""
|
"""simple function for converting dataclass to dict"""
|
||||||
instance = ActivityObject(id="a", type="b")
|
instance = ActivityObject(id="a", type="b")
|
||||||
serialized = instance.serialize()
|
serialized = instance.serialize()
|
||||||
|
@ -83,7 +87,7 @@ class BaseActivity(TestCase):
|
||||||
self.assertEqual(serialized["type"], "b")
|
self.assertEqual(serialized["type"], "b")
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_resolve_remote_id(self, _):
|
def test_resolve_remote_id(self, *_):
|
||||||
"""look up or load remote data"""
|
"""look up or load remote data"""
|
||||||
# existing item
|
# existing item
|
||||||
result = resolve_remote_id("http://example.com/a/b", model=models.User)
|
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.remote_id, "https://example.com/user/mouse")
|
||||||
self.assertEqual(result.name, "MOUSE?? 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"""
|
"""catch mismatch between activity type and model type"""
|
||||||
instance = ActivityObject(id="a", type="b")
|
instance = ActivityObject(id="a", type="b")
|
||||||
with self.assertRaises(ActivitySerializerError):
|
with self.assertRaises(ActivitySerializerError):
|
||||||
instance.to_model(model=models.User)
|
instance.to_model(model=models.User)
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_to_model_image(self, _):
|
def test_to_model_image(self, *_):
|
||||||
"""update an image field"""
|
"""update an image field"""
|
||||||
activity = activitypub.Person(
|
activity = activitypub.Person(
|
||||||
id=self.user.remote_id,
|
id=self.user.remote_id,
|
||||||
|
@ -145,7 +149,7 @@ class BaseActivity(TestCase):
|
||||||
self.assertEqual(self.user.name, "New Name")
|
self.assertEqual(self.user.name, "New Name")
|
||||||
self.assertEqual(self.user.key_pair.public_key, "hi")
|
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"""
|
"""annoying that these all need special handling"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
status = models.Status.objects.create(
|
status = models.Status.objects.create(
|
||||||
|
@ -176,7 +180,7 @@ class BaseActivity(TestCase):
|
||||||
self.assertEqual(status.mention_books.first(), book)
|
self.assertEqual(status.mention_books.first(), book)
|
||||||
|
|
||||||
@responses.activate
|
@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
|
"""these are reversed relationships, where the secondary object
|
||||||
keys the primary object but not vice versa"""
|
keys the primary object but not vice versa"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
|
@ -215,7 +219,7 @@ class BaseActivity(TestCase):
|
||||||
self.assertIsNone(status.attachments.first())
|
self.assertIsNone(status.attachments.first())
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_set_related_field(self, _):
|
def test_set_related_field(self, *_):
|
||||||
"""celery task to add back-references to created objects"""
|
"""celery task to add back-references to created objects"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
status = models.Status.objects.create(
|
status = models.Status.objects.create(
|
||||||
|
|
|
@ -119,10 +119,12 @@ class AbstractConnector(TestCase):
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_get_or_create_author(self):
|
def test_get_or_create_author(self):
|
||||||
"""load an author"""
|
"""load an author"""
|
||||||
self.connector.author_mappings = [
|
self.connector.author_mappings = (
|
||||||
|
[ # pylint: disable=attribute-defined-outside-init
|
||||||
Mapping("id"),
|
Mapping("id"),
|
||||||
Mapping("name"),
|
Mapping("name"),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET,
|
responses.GET,
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
},
|
},
|
||||||
"bookwyrmUser": true,
|
"bookwyrmUser": true,
|
||||||
"manuallyApprovesFollowers": false,
|
"manuallyApprovesFollowers": false,
|
||||||
"discoverable": true,
|
"discoverable": false,
|
||||||
"devices": "https://friend.camp/users/tripofmice/collections/devices",
|
"devices": "https://friend.camp/users/tripofmice/collections/devices",
|
||||||
"tag": [],
|
"tag": [],
|
||||||
"icon": {
|
"icon": {
|
||||||
|
|
|
@ -16,9 +16,12 @@ from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
def make_date(*args):
|
def make_date(*args):
|
||||||
|
"""helper function to easily generate a date obj"""
|
||||||
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=consider-using-with
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
class GoodreadsImport(TestCase):
|
class GoodreadsImport(TestCase):
|
||||||
"""importing from goodreads csv"""
|
"""importing from goodreads csv"""
|
||||||
|
|
||||||
|
@ -27,6 +30,7 @@ class GoodreadsImport(TestCase):
|
||||||
self.importer = GoodreadsImporter()
|
self.importer = GoodreadsImporter()
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||||
self.csv = open(datafile, "r", encoding=self.importer.encoding)
|
self.csv = open(datafile, "r", encoding=self.importer.encoding)
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "password", local=True
|
"mouse", "mouse@mouse.mouse", "password", local=True
|
||||||
)
|
)
|
||||||
|
@ -49,7 +53,7 @@ class GoodreadsImport(TestCase):
|
||||||
parent_work=work,
|
parent_work=work,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_job(self):
|
def test_create_job(self, _):
|
||||||
"""creates the import job entry and checks csv"""
|
"""creates the import job entry and checks csv"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "public")
|
import_job = self.importer.create_job(self.user, self.csv, False, "public")
|
||||||
self.assertEqual(import_job.user, self.user)
|
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].index, 2)
|
||||||
self.assertEqual(import_items[2].data["Book Id"], "28694510")
|
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"""
|
"""trying again with items that didn't import"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||||
import_items = models.ImportItem.objects.filter(job=import_job).all()[:2]
|
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].index, 1)
|
||||||
self.assertEqual(retry_items[1].data["Book Id"], "52691223")
|
self.assertEqual(retry_items[1].data["Book Id"], "52691223")
|
||||||
|
|
||||||
def test_start_import(self):
|
def test_start_import(self, _):
|
||||||
"""begin loading books"""
|
"""begin loading books"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||||
MockTask = namedtuple("Task", ("id"))
|
MockTask = namedtuple("Task", ("id"))
|
||||||
|
@ -95,7 +99,7 @@ class GoodreadsImport(TestCase):
|
||||||
self.assertEqual(import_job.task_id, "7")
|
self.assertEqual(import_job.task_id, "7")
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_import_data(self):
|
def test_import_data(self, _):
|
||||||
"""resolve entry"""
|
"""resolve entry"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||||
book = models.Edition.objects.create(title="Test Book")
|
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)
|
import_item = models.ImportItem.objects.get(job=import_job, index=0)
|
||||||
self.assertEqual(import_item.book.id, book.id)
|
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"""
|
"""goodreads import added a book, this adds related connections"""
|
||||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||||
self.assertIsNone(shelf.books.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.start_date, make_date(2020, 10, 21))
|
||||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
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"""
|
"""goodreads import added a book, this adds related connections"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
shelf = self.user.shelf_set.filter(identifier="to-read").first()
|
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.start_date, make_date(2020, 10, 21))
|
||||||
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
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"""
|
"""re-importing books"""
|
||||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
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))
|
self.assertEqual(readthrough.finish_date, make_date(2020, 10, 25))
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||||
def test_handle_imported_book_review(self, _):
|
def test_handle_imported_book_review(self, *_):
|
||||||
"""goodreads review import"""
|
"""goodreads review import"""
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
import_job = models.ImportJob.objects.create(user=self.user)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||||
|
@ -234,7 +238,7 @@ class GoodreadsImport(TestCase):
|
||||||
self.assertEqual(review.privacy, "unlisted")
|
self.assertEqual(review.privacy, "unlisted")
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||||
def test_handle_imported_book_rating(self, _):
|
def test_handle_imported_book_rating(self, *_):
|
||||||
"""goodreads rating import"""
|
"""goodreads rating import"""
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
import_job = models.ImportJob.objects.create(user=self.user)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
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.published_date, make_date(2019, 7, 8))
|
||||||
self.assertEqual(review.privacy, "unlisted")
|
self.assertEqual(review.privacy, "unlisted")
|
||||||
|
|
||||||
def test_handle_imported_book_reviews_disabled(self):
|
def test_handle_imported_book_reviews_disabled(self, _):
|
||||||
"""goodreads review import"""
|
"""goodreads review import"""
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
import_job = models.ImportJob.objects.create(user=self.user)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/goodreads.csv")
|
||||||
|
|
|
@ -19,6 +19,8 @@ def make_date(*args):
|
||||||
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
return datetime.datetime(*args, tzinfo=pytz.UTC)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=consider-using-with
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
class LibrarythingImport(TestCase):
|
class LibrarythingImport(TestCase):
|
||||||
"""importing from librarything tsv"""
|
"""importing from librarything tsv"""
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ class LibrarythingImport(TestCase):
|
||||||
|
|
||||||
# Librarything generates latin encoded exports...
|
# Librarything generates latin encoded exports...
|
||||||
self.csv = open(datafile, "r", encoding=self.importer.encoding)
|
self.csv = open(datafile, "r", encoding=self.importer.encoding)
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"mmai", "mmai@mmai.mmai", "password", local=True
|
"mmai", "mmai@mmai.mmai", "password", local=True
|
||||||
)
|
)
|
||||||
|
@ -51,7 +54,7 @@ class LibrarythingImport(TestCase):
|
||||||
parent_work=work,
|
parent_work=work,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_job(self):
|
def test_create_job(self, _):
|
||||||
"""creates the import job entry and checks csv"""
|
"""creates the import job entry and checks csv"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "public")
|
import_job = self.importer.create_job(self.user, self.csv, False, "public")
|
||||||
self.assertEqual(import_job.user, self.user)
|
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].index, 2)
|
||||||
self.assertEqual(import_items[2].data["Book Id"], "5015399")
|
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"""
|
"""trying again with items that didn't import"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||||
import_items = models.ImportItem.objects.filter(job=import_job).all()[:2]
|
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")
|
self.assertEqual(retry_items[1].data["Book Id"], "5015319")
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_import_data(self):
|
def test_import_data(self, _):
|
||||||
"""resolve entry"""
|
"""resolve entry"""
|
||||||
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
import_job = self.importer.create_job(self.user, self.csv, False, "unlisted")
|
||||||
book = models.Edition.objects.create(title="Test Book")
|
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)
|
import_item = models.ImportItem.objects.get(job=import_job, index=0)
|
||||||
self.assertEqual(import_item.book.id, book.id)
|
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"""
|
"""librarything import added a book, this adds related connections"""
|
||||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||||
self.assertIsNone(shelf.books.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.start_date, make_date(2007, 4, 16))
|
||||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
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"""
|
"""librarything import added a book, this adds related connections"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
shelf = self.user.shelf_set.filter(identifier="to-read").first()
|
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.start_date, make_date(2007, 4, 16))
|
||||||
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
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"""
|
"""re-importing books"""
|
||||||
shelf = self.user.shelf_set.filter(identifier="read").first()
|
shelf = self.user.shelf_set.filter(identifier="read").first()
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
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))
|
self.assertEqual(readthrough.finish_date, make_date(2007, 5, 8))
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||||
def test_handle_imported_book_review(self, _):
|
def test_handle_imported_book_review(self, *_):
|
||||||
"""librarything review import"""
|
"""librarything review import"""
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
import_job = models.ImportJob.objects.create(user=self.user)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv")
|
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.published_date, make_date(2007, 5, 8))
|
||||||
self.assertEqual(review.privacy, "unlisted")
|
self.assertEqual(review.privacy, "unlisted")
|
||||||
|
|
||||||
def test_handle_imported_book_reviews_disabled(self):
|
def test_handle_imported_book_reviews_disabled(self, _):
|
||||||
"""librarything review import"""
|
"""librarything review import"""
|
||||||
import_job = models.ImportJob.objects.create(user=self.user)
|
import_job = models.ImportJob.objects.create(user=self.user)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/librarything.tsv")
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Activitystreams(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need some stuff"""
|
"""we need some stuff"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,11 +27,12 @@ class ActivitypubMixins(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""shared data"""
|
"""shared data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "http://example.com/a/b"
|
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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
@ -189,7 +190,7 @@ class ActivitypubMixins(TestCase):
|
||||||
def test_get_recipients_combine_inboxes(self, *_):
|
def test_get_recipients_combine_inboxes(self, *_):
|
||||||
"""should combine users with the same shared_inbox"""
|
"""should combine users with the same shared_inbox"""
|
||||||
self.remote_user.shared_inbox = "http://example.com/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
another_remote_user = models.User.objects.create_user(
|
another_remote_user = models.User.objects.create_user(
|
||||||
"nutria",
|
"nutria",
|
||||||
|
|
|
@ -13,6 +13,7 @@ class BaseModel(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""shared data"""
|
"""shared data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,10 +24,11 @@ from bookwyrm.models.base_model import BookWyrmModel
|
||||||
from bookwyrm.models.activitypub_mixin import ActivitypubMixin
|
from bookwyrm.models.activitypub_mixin import ActivitypubMixin
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# 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"""
|
"""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"""
|
"""should look like a url"""
|
||||||
self.assertIsNone(fields.validate_remote_id("http://www.example.com"))
|
self.assertIsNone(fields.validate_remote_id("http://www.example.com"))
|
||||||
self.assertIsNone(fields.validate_remote_id("https://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",
|
"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"""
|
"""generic mixin with super basic to and from functionality"""
|
||||||
instance = fields.ActivitypubFieldMixin()
|
instance = fields.ActivitypubFieldMixin()
|
||||||
self.assertEqual(instance.field_to_activity("fish"), "fish")
|
self.assertEqual(instance.field_to_activity("fish"), "fish")
|
||||||
|
@ -62,7 +63,7 @@ class ActivitypubFields(TestCase):
|
||||||
instance.name = "snake_case_name"
|
instance.name = "snake_case_name"
|
||||||
self.assertEqual(instance.get_activitypub_field(), "snakeCaseName")
|
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"""
|
"""setter from entire json blob"""
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -81,7 +82,7 @@ class ActivitypubFields(TestCase):
|
||||||
instance.set_field_from_activity(mock_model, data)
|
instance.set_field_from_activity(mock_model, data)
|
||||||
self.assertEqual(mock_model.field_name, "hi")
|
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"""
|
"""set json field given entire model"""
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -99,7 +100,7 @@ class ActivitypubFields(TestCase):
|
||||||
instance.set_activity_from_field(data, mock_model)
|
instance.set_activity_from_field(data, mock_model)
|
||||||
self.assertEqual(data["fieldName"], "bip")
|
self.assertEqual(data["fieldName"], "bip")
|
||||||
|
|
||||||
def test_remote_id_field(self):
|
def test_remote_id_field(self, _):
|
||||||
"""just sets some defaults on charfield"""
|
"""just sets some defaults on charfield"""
|
||||||
instance = fields.RemoteIdField()
|
instance = fields.RemoteIdField()
|
||||||
self.assertEqual(instance.max_length, 255)
|
self.assertEqual(instance.max_length, 255)
|
||||||
|
@ -108,7 +109,7 @@ class ActivitypubFields(TestCase):
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
instance.run_validators("http://www.example.com/dlfjg 23/x")
|
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"""
|
"""again, just setting defaults on username field"""
|
||||||
instance = fields.UsernameField()
|
instance = fields.UsernameField()
|
||||||
self.assertEqual(instance.activitypub_field, "preferredUsername")
|
self.assertEqual(instance.activitypub_field, "preferredUsername")
|
||||||
|
@ -129,7 +130,7 @@ class ActivitypubFields(TestCase):
|
||||||
|
|
||||||
self.assertEqual(instance.field_to_activity("test@example.com"), "test")
|
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"""
|
"""post privacy field's many default values"""
|
||||||
instance = fields.PrivacyField()
|
instance = fields.PrivacyField()
|
||||||
self.assertEqual(instance.max_length, 255)
|
self.assertEqual(instance.max_length, 255)
|
||||||
|
@ -142,7 +143,7 @@ class ActivitypubFields(TestCase):
|
||||||
instance.public, "https://www.w3.org/ns/activitystreams#Public"
|
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"""
|
"""translate between to/cc fields and privacy"""
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
|
@ -230,7 +231,7 @@ class ActivitypubFields(TestCase):
|
||||||
self.assertEqual(activity["to"], [user.remote_id])
|
self.assertEqual(activity["to"], [user.remote_id])
|
||||||
self.assertEqual(activity["cc"], [])
|
self.assertEqual(activity["cc"], [])
|
||||||
|
|
||||||
def test_foreign_key(self):
|
def test_foreign_key(self, _):
|
||||||
"""should be able to format a related model"""
|
"""should be able to format a related model"""
|
||||||
instance = fields.ForeignKey("User", on_delete=models.CASCADE)
|
instance = fields.ForeignKey("User", on_delete=models.CASCADE)
|
||||||
Serializable = namedtuple("Serializable", ("to_activity", "remote_id"))
|
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")
|
self.assertEqual(instance.field_to_activity(item), "https://e.b/c")
|
||||||
|
|
||||||
@responses.activate
|
@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"""
|
"""create a new object from a foreign key"""
|
||||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
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.remote_id, "https://example.com/user/mouse")
|
||||||
self.assertEqual(value.name, "MOUSE?? 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"""
|
"""test recieving activity json"""
|
||||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||||
|
@ -286,7 +287,7 @@ class ActivitypubFields(TestCase):
|
||||||
self.assertEqual(value.name, "MOUSE?? MOUSE!!")
|
self.assertEqual(value.name, "MOUSE?? MOUSE!!")
|
||||||
# et cetera but we're not testing serializing user json
|
# 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"""
|
"""test receiving a dict of an existing object in the db"""
|
||||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
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"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
user.remote_id = "https://example.com/user/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(
|
User.objects.create_user(
|
||||||
"rat", "rat@rat.rat", "ratword", local=True, localname="rat"
|
"rat", "rat@rat.rat", "ratword", local=True, localname="rat"
|
||||||
|
@ -305,7 +306,7 @@ class ActivitypubFields(TestCase):
|
||||||
value = instance.field_from_activity(activitypub.Person(**userdata))
|
value = instance.field_from_activity(activitypub.Person(**userdata))
|
||||||
self.assertEqual(value, user)
|
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"""
|
"""test receiving a remote id of an existing object in the db"""
|
||||||
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
instance = fields.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
user = User.objects.create_user(
|
user = User.objects.create_user(
|
||||||
|
@ -318,14 +319,14 @@ class ActivitypubFields(TestCase):
|
||||||
value = instance.field_from_activity(user.remote_id)
|
value = instance.field_from_activity(user.remote_id)
|
||||||
self.assertEqual(value, user)
|
self.assertEqual(value, user)
|
||||||
|
|
||||||
def test_one_to_one_field(self):
|
def test_one_to_one_field(self, _):
|
||||||
"""a gussied up foreign key"""
|
"""a gussied up foreign key"""
|
||||||
instance = fields.OneToOneField("User", on_delete=models.CASCADE)
|
instance = fields.OneToOneField("User", on_delete=models.CASCADE)
|
||||||
Serializable = namedtuple("Serializable", ("to_activity", "remote_id"))
|
Serializable = namedtuple("Serializable", ("to_activity", "remote_id"))
|
||||||
item = Serializable(lambda: {"a": "b"}, "https://e.b/c")
|
item = Serializable(lambda: {"a": "b"}, "https://e.b/c")
|
||||||
self.assertEqual(instance.field_to_activity(item), {"a": "b"})
|
self.assertEqual(instance.field_to_activity(item), {"a": "b"})
|
||||||
|
|
||||||
def test_many_to_many_field(self):
|
def test_many_to_many_field(self, _):
|
||||||
"""lists!"""
|
"""lists!"""
|
||||||
instance = fields.ManyToManyField("User")
|
instance = fields.ManyToManyField("User")
|
||||||
|
|
||||||
|
@ -343,7 +344,7 @@ class ActivitypubFields(TestCase):
|
||||||
self.assertEqual(instance.field_to_activity(items), "example.com/snake_case")
|
self.assertEqual(instance.field_to_activity(items), "example.com/snake_case")
|
||||||
|
|
||||||
@responses.activate
|
@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"""
|
"""resolve related fields for a list, takes a list of remote ids"""
|
||||||
instance = fields.ManyToManyField(User)
|
instance = fields.ManyToManyField(User)
|
||||||
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
|
||||||
|
@ -363,7 +364,7 @@ class ActivitypubFields(TestCase):
|
||||||
self.assertEqual(len(value), 1)
|
self.assertEqual(len(value), 1)
|
||||||
self.assertIsInstance(value[0], User)
|
self.assertIsInstance(value[0], User)
|
||||||
|
|
||||||
def test_tag_field(self):
|
def test_tag_field(self, _):
|
||||||
"""a special type of many to many field"""
|
"""a special type of many to many field"""
|
||||||
instance = fields.TagField("User")
|
instance = fields.TagField("User")
|
||||||
|
|
||||||
|
@ -382,13 +383,14 @@ class ActivitypubFields(TestCase):
|
||||||
self.assertEqual(result[0].name, "Name")
|
self.assertEqual(result[0].name, "Name")
|
||||||
self.assertEqual(result[0].type, "Serializable")
|
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"""
|
"""loadin' a list of items from Links"""
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
@patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast")
|
@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"""
|
"""storing images"""
|
||||||
user = User.objects.create_user(
|
user = User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"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, list)
|
||||||
self.assertIsInstance(loaded_image[1], ContentFile)
|
self.assertIsInstance(loaded_image[1], ContentFile)
|
||||||
|
|
||||||
def test_image_serialize(self):
|
def test_image_serialize(self, _):
|
||||||
"""make sure we're creating sensible image paths"""
|
"""make sure we're creating sensible image paths"""
|
||||||
ValueMock = namedtuple("ValueMock", ("url"))
|
ValueMock = namedtuple("ValueMock", ("url"))
|
||||||
value_mock = ValueMock("/images/fish.jpg")
|
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.url, "https://your.domain.here/images/fish.jpg")
|
||||||
self.assertEqual(result.name, "hello")
|
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"""
|
"""this one is pretty simple, it just has to use isoformat"""
|
||||||
instance = fields.DateTimeField()
|
instance = fields.DateTimeField()
|
||||||
now = timezone.now()
|
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(now.isoformat()), now)
|
||||||
self.assertEqual(instance.field_from_activity("bip"), None)
|
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"""
|
"""idk why it makes them strings but probably for a good reason"""
|
||||||
instance = fields.ArrayField(fields.IntegerField)
|
instance = fields.ArrayField(fields.IntegerField)
|
||||||
self.assertEqual(instance.field_to_activity([0, 1]), ["0", "1"])
|
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"""
|
"""sanitizes html, the sanitizer has its own tests"""
|
||||||
instance = fields.HtmlField()
|
instance = fields.HtmlField()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -59,6 +59,7 @@ class ImportJob(TestCase):
|
||||||
unknown_read_data["Exclusive Shelf"] = "read"
|
unknown_read_data["Exclusive Shelf"] = "read"
|
||||||
unknown_read_data["Date Read"] = ""
|
unknown_read_data["Date Read"] = ""
|
||||||
|
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
user = models.User.objects.create_user(
|
user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,7 @@ class List(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""look, a list"""
|
"""look, a list"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" testing models """
|
""" testing models """
|
||||||
|
from unittest.mock import patch
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ class ReadThrough(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""look, a shelf"""
|
"""look, a shelf"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" testing models """
|
""" testing models """
|
||||||
|
import json
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
@ -20,25 +21,21 @@ class Relationship(TestCase):
|
||||||
inbox="https://example.com/users/rat/inbox",
|
inbox="https://example.com/users/rat/inbox",
|
||||||
outbox="https://example.com/users/rat/outbox",
|
outbox="https://example.com/users/rat/outbox",
|
||||||
)
|
)
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "http://local.com/user/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):
|
def test_user_follows_from_request(self):
|
||||||
"""convert a follow request into a follow"""
|
"""convert a follow request into a follow"""
|
||||||
real_broadcast = models.UserFollowRequest.broadcast
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
|
||||||
|
|
||||||
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(
|
request = models.UserFollowRequest.objects.create(
|
||||||
user_subject=self.local_user, user_object=self.remote_user
|
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(
|
self.assertEqual(
|
||||||
request.remote_id, "http://local.com/user/mouse#follows/%d" % request.id
|
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.status, "follows")
|
||||||
self.assertEqual(rel.user_subject, self.local_user)
|
self.assertEqual(rel.user_subject, self.local_user)
|
||||||
self.assertEqual(rel.user_object, self.remote_user)
|
self.assertEqual(rel.user_object, self.remote_user)
|
||||||
models.UserFollowRequest.broadcast = real_broadcast
|
|
||||||
|
|
||||||
def test_user_follows_from_request_custom_remote_id(self):
|
def test_user_follows_from_request_custom_remote_id(self):
|
||||||
"""store a specific remote id for a relationship provided by remote"""
|
"""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_subject, self.local_user)
|
||||||
self.assertEqual(rel.user_object, self.remote_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"""
|
"""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(
|
models.UserFollowRequest.objects.create(
|
||||||
user_subject=self.local_user,
|
user_subject=self.local_user,
|
||||||
user_object=self.remote_user,
|
user_object=self.remote_user,
|
||||||
)
|
)
|
||||||
models.UserFollowRequest.broadcast = real_broadcast
|
activity = json.loads(broadcast_mock.call_args[0][1])
|
||||||
|
|
||||||
def test_follow_request_accept(self):
|
|
||||||
"""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["actor"], self.local_user.remote_id)
|
||||||
self.assertEqual(activity["object"]["id"], "https://www.hi.com/")
|
self.assertEqual(activity["object"], self.remote_user.remote_id)
|
||||||
|
self.assertEqual(activity["type"], "Follow")
|
||||||
|
|
||||||
|
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||||
|
def test_follow_request_accept(self, broadcast_mock):
|
||||||
|
"""accept a request and make it a relationship"""
|
||||||
self.local_user.manually_approves_followers = True
|
self.local_user.manually_approves_followers = True
|
||||||
self.local_user.save(broadcast=False)
|
self.local_user.save(
|
||||||
models.UserFollowRequest.broadcast = mock_broadcast
|
broadcast=False, update_fields=["manually_approves_followers"]
|
||||||
|
)
|
||||||
|
|
||||||
request = models.UserFollowRequest.objects.create(
|
request = models.UserFollowRequest.objects.create(
|
||||||
user_subject=self.remote_user,
|
user_subject=self.remote_user,
|
||||||
user_object=self.local_user,
|
user_object=self.local_user,
|
||||||
|
@ -107,32 +93,34 @@ class Relationship(TestCase):
|
||||||
)
|
)
|
||||||
request.accept()
|
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.assertFalse(models.UserFollowRequest.objects.exists())
|
||||||
self.assertTrue(models.UserFollows.objects.exists())
|
self.assertTrue(models.UserFollows.objects.exists())
|
||||||
rel = models.UserFollows.objects.get()
|
rel = models.UserFollows.objects.get()
|
||||||
self.assertEqual(rel.user_subject, self.remote_user)
|
self.assertEqual(rel.user_subject, self.remote_user)
|
||||||
self.assertEqual(rel.user_object, self.local_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"""
|
"""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.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(
|
request = models.UserFollowRequest.objects.create(
|
||||||
user_subject=self.remote_user,
|
user_subject=self.remote_user,
|
||||||
user_object=self.local_user,
|
user_object=self.local_user,
|
||||||
)
|
)
|
||||||
request.reject()
|
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.UserFollowRequest.objects.exists())
|
||||||
self.assertFalse(models.UserFollows.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
|
# pylint: disable=unused-argument
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
class Shelf(TestCase):
|
class Shelf(TestCase):
|
||||||
"""some activitypub oddness ahead"""
|
"""some activitypub oddness ahead"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""look, a shelf"""
|
"""look, a shelf"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
work = models.Work.objects.create(title="Test Work")
|
work = models.Work.objects.create(title="Test Work")
|
||||||
self.book = models.Edition.objects.create(title="test book", parent_work=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"""
|
"""shelves use custom remote ids"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
shelf = models.Shelf.objects.create(
|
shelf = models.Shelf.objects.create(
|
||||||
|
@ -27,7 +29,7 @@ class Shelf(TestCase):
|
||||||
expected_id = "https://%s/user/mouse/books/test-shelf" % settings.DOMAIN
|
expected_id = "https://%s/user/mouse/books/test-shelf" % settings.DOMAIN
|
||||||
self.assertEqual(shelf.get_remote_id(), expected_id)
|
self.assertEqual(shelf.get_remote_id(), expected_id)
|
||||||
|
|
||||||
def test_to_activity(self):
|
def test_to_activity(self, _):
|
||||||
"""jsonify it"""
|
"""jsonify it"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
shelf = models.Shelf.objects.create(
|
shelf = models.Shelf.objects.create(
|
||||||
|
@ -41,7 +43,7 @@ class Shelf(TestCase):
|
||||||
self.assertEqual(activity_json["name"], "Test Shelf")
|
self.assertEqual(activity_json["name"], "Test Shelf")
|
||||||
self.assertEqual(activity_json["owner"], self.local_user.remote_id)
|
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"""
|
"""create and broadcast shelf creation"""
|
||||||
|
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") as mock:
|
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(activity["object"]["name"], "arthur russel")
|
||||||
self.assertEqual(shelf.name, "arthur russel")
|
self.assertEqual(shelf.name, "arthur russel")
|
||||||
|
|
||||||
def test_shelve(self):
|
def test_shelve(self, _):
|
||||||
"""create and broadcast shelf creation"""
|
"""create and broadcast shelf creation"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
shelf = models.Shelf.objects.create(
|
shelf = models.Shelf.objects.create(
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Status(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""useful things for creating a status"""
|
"""useful things for creating a status"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,7 @@ from bookwyrm.settings import DOMAIN
|
||||||
# pylint: disable=missing-function-docstring
|
# pylint: disable=missing-function-docstring
|
||||||
class User(TestCase):
|
class User(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"mouse@%s" % DOMAIN,
|
"mouse@%s" % DOMAIN,
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -154,7 +155,8 @@ class User(TestCase):
|
||||||
self.assertIsNone(server.application_type)
|
self.assertIsNone(server.application_type)
|
||||||
self.assertIsNone(server.application_version)
|
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"""
|
"""deactivate a user"""
|
||||||
self.assertTrue(self.user.is_active)
|
self.assertTrue(self.user.is_active)
|
||||||
with patch(
|
with patch(
|
||||||
|
|
|
@ -11,6 +11,7 @@ class Activitystreams(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""use a test csv"""
|
"""use a test csv"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Emailing(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
""" test generating preview images """
|
""" test generating preview images """
|
||||||
import pathlib
|
import pathlib
|
||||||
|
from unittest.mock import patch
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from django.test import TestCase
|
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 django.db.models.fields.files import ImageFieldFile
|
||||||
|
|
||||||
from bookwyrm import models, settings
|
from bookwyrm import models, settings
|
||||||
|
|
||||||
from bookwyrm.preview_images import (
|
from bookwyrm.preview_images import (
|
||||||
generate_site_preview_image_task,
|
generate_site_preview_image_task,
|
||||||
generate_edition_preview_image_task,
|
generate_edition_preview_image_task,
|
||||||
|
@ -29,6 +29,7 @@ class PreviewImages(TestCase):
|
||||||
avatar_file = pathlib.Path(__file__).parent.joinpath(
|
avatar_file = pathlib.Path(__file__).parent.joinpath(
|
||||||
"../static/images/no_cover.jpg"
|
"../static/images/no_cover.jpg"
|
||||||
)
|
)
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"possum@local.com",
|
"possum@local.com",
|
||||||
"possum@possum.possum",
|
"possum@possum.possum",
|
||||||
|
|
|
@ -37,6 +37,7 @@ class Signature(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""create users and test data"""
|
"""create users and test data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.mouse = models.User.objects.create_user(
|
self.mouse = models.User.objects.create_user(
|
||||||
"mouse@%s" % DOMAIN,
|
"mouse@%s" % DOMAIN,
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
|
|
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,6 +22,7 @@ class TemplateTags(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""create some filler objects"""
|
"""create some filler objects"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Inbox(TestCase):
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
local_user = models.User.objects.create_user(
|
local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -27,7 +28,7 @@ class Inbox(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
@ -144,7 +145,8 @@ class Inbox(TestCase):
|
||||||
)
|
)
|
||||||
self.assertTrue(views.inbox.is_blocked_activity(activity))
|
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"""
|
"""don't let deactivated users post"""
|
||||||
self.remote_user.delete(broadcast=False)
|
self.remote_user.delete(broadcast=False)
|
||||||
self.assertTrue(self.remote_user.deleted)
|
self.assertTrue(self.remote_user.deleted)
|
||||||
|
|
|
@ -13,6 +13,7 @@ class InboxAdd(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
local_user = models.User.objects.create_user(
|
local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -21,7 +22,7 @@ class InboxAdd(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
|
|
@ -13,6 +13,7 @@ class InboxActivities(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -21,7 +22,7 @@ class InboxActivities(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
|
|
@ -12,6 +12,7 @@ class InboxBlock(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -20,7 +21,7 @@ class InboxBlock(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
|
|
@ -16,6 +16,7 @@ class InboxCreate(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -24,7 +25,7 @@ class InboxCreate(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
|
|
@ -13,6 +13,7 @@ class InboxActivities(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -21,7 +22,7 @@ class InboxActivities(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
@ -105,7 +106,8 @@ class InboxActivities(TestCase):
|
||||||
self.assertEqual(models.Notification.objects.count(), 1)
|
self.assertEqual(models.Notification.objects.count(), 1)
|
||||||
self.assertEqual(models.Notification.objects.get(), notif)
|
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"""
|
"""delete a user"""
|
||||||
self.assertTrue(models.User.objects.get(username="rat@example.com").is_active)
|
self.assertTrue(models.User.objects.get(username="rat@example.com").is_active)
|
||||||
activity = {
|
activity = {
|
||||||
|
|
|
@ -13,6 +13,7 @@ class InboxRelationships(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -21,7 +22,7 @@ class InboxRelationships(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
@ -102,7 +103,9 @@ class InboxRelationships(TestCase):
|
||||||
}
|
}
|
||||||
|
|
||||||
self.local_user.manually_approves_followers = True
|
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"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
views.inbox.activity_task(activity)
|
views.inbox.activity_task(activity)
|
||||||
|
@ -124,7 +127,9 @@ class InboxRelationships(TestCase):
|
||||||
def test_undo_follow_request(self):
|
def test_undo_follow_request(self):
|
||||||
"""the requester cancels a follow request"""
|
"""the requester cancels a follow request"""
|
||||||
self.local_user.manually_approves_followers = True
|
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"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
request = models.UserFollowRequest.objects.create(
|
request = models.UserFollowRequest.objects.create(
|
||||||
user_subject=self.remote_user, user_object=self.local_user
|
user_subject=self.remote_user, user_object=self.local_user
|
||||||
|
|
|
@ -12,6 +12,7 @@ class InboxActivities(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -20,7 +21,7 @@ class InboxActivities(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
|
|
@ -12,6 +12,7 @@ class InboxRemove(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -20,7 +21,7 @@ class InboxRemove(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
|
|
@ -14,6 +14,7 @@ class InboxUpdate(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""basic user and book data"""
|
"""basic user and book data"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@example.com",
|
"mouse@example.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -22,7 +23,7 @@ class InboxUpdate(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
)
|
)
|
||||||
self.local_user.remote_id = "https://example.com/user/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"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
|
@ -79,7 +80,8 @@ class InboxUpdate(TestCase):
|
||||||
self.assertEqual(book_list.description, "summary text")
|
self.assertEqual(book_list.description, "summary text")
|
||||||
self.assertEqual(book_list.remote_id, "https://example.com/list/22")
|
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"""
|
"""update an existing user"""
|
||||||
models.UserFollows.objects.create(
|
models.UserFollows.objects.create(
|
||||||
user_subject=self.local_user,
|
user_subject=self.local_user,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" test for app action functionality """
|
""" test for app action functionality """
|
||||||
|
from unittest.mock import patch
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
|
@ -12,6 +13,7 @@ class AnnouncementViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -13,12 +13,14 @@ from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
class AuthenticationViews(TestCase):
|
class AuthenticationViews(TestCase):
|
||||||
"""login and password management"""
|
"""login and password management"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -31,7 +33,7 @@ class AuthenticationViews(TestCase):
|
||||||
|
|
||||||
self.settings = models.SiteSettings.objects.create(id=1)
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
login = views.Login.as_view()
|
login = views.Login.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
@ -47,7 +49,7 @@ class AuthenticationViews(TestCase):
|
||||||
self.assertEqual(result.url, "/")
|
self.assertEqual(result.url, "/")
|
||||||
self.assertEqual(result.status_code, 302)
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
def test_register(self):
|
def test_register(self, _):
|
||||||
"""create a user"""
|
"""create a user"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
self.assertEqual(models.User.objects.count(), 1)
|
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.localname, "nutria-user.user_nutria")
|
||||||
self.assertEqual(nutria.local, True)
|
self.assertEqual(nutria.local, True)
|
||||||
|
|
||||||
def test_register_trailing_space(self):
|
def test_register_trailing_space(self, _):
|
||||||
"""django handles this so weirdly"""
|
"""django handles this so weirdly"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
|
@ -84,7 +86,7 @@ class AuthenticationViews(TestCase):
|
||||||
self.assertEqual(nutria.localname, "nutria")
|
self.assertEqual(nutria.localname, "nutria")
|
||||||
self.assertEqual(nutria.local, True)
|
self.assertEqual(nutria.local, True)
|
||||||
|
|
||||||
def test_register_invalid_email(self):
|
def test_register_invalid_email(self, _):
|
||||||
"""gotta have an email"""
|
"""gotta have an email"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
self.assertEqual(models.User.objects.count(), 1)
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
|
@ -95,7 +97,7 @@ class AuthenticationViews(TestCase):
|
||||||
self.assertEqual(models.User.objects.count(), 1)
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
response.render()
|
response.render()
|
||||||
|
|
||||||
def test_register_invalid_username(self):
|
def test_register_invalid_username(self, _):
|
||||||
"""gotta have an email"""
|
"""gotta have an email"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
self.assertEqual(models.User.objects.count(), 1)
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
|
@ -123,7 +125,7 @@ class AuthenticationViews(TestCase):
|
||||||
self.assertEqual(models.User.objects.count(), 1)
|
self.assertEqual(models.User.objects.count(), 1)
|
||||||
response.render()
|
response.render()
|
||||||
|
|
||||||
def test_register_closed_instance(self):
|
def test_register_closed_instance(self, _):
|
||||||
"""you can't just register"""
|
"""you can't just register"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
self.settings.allow_registration = False
|
self.settings.allow_registration = False
|
||||||
|
@ -135,7 +137,7 @@ class AuthenticationViews(TestCase):
|
||||||
with self.assertRaises(PermissionDenied):
|
with self.assertRaises(PermissionDenied):
|
||||||
view(request)
|
view(request)
|
||||||
|
|
||||||
def test_register_invite(self):
|
def test_register_invite(self, _):
|
||||||
"""you can't just register"""
|
"""you can't just register"""
|
||||||
view = views.Register.as_view()
|
view = views.Register.as_view()
|
||||||
self.settings.allow_registration = False
|
self.settings.allow_registration = False
|
||||||
|
|
|
@ -17,6 +17,7 @@ class AuthorViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -14,6 +14,7 @@ class BlockViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -23,6 +23,7 @@ class BookViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -200,7 +201,8 @@ class BookViews(TestCase):
|
||||||
self.assertEqual(book.authors.first().name, "Sappho")
|
self.assertEqual(book.authors.first().name, "Sappho")
|
||||||
self.assertEqual(book.authors.first(), book.parent_work.authors.first())
|
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"""
|
"""updates user's relationships to a book"""
|
||||||
work = models.Work.objects.create(title="test work")
|
work = models.Work.objects.create(title="test work")
|
||||||
edition1 = models.Edition.objects.create(title="first ed", parent_work=work)
|
edition1 = models.Edition.objects.create(title="first ed", parent_work=work)
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
""" test for app action functionality """
|
""" 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.template.response import TemplateResponse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
|
@ -12,6 +15,7 @@ class DirectoryViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -20,7 +24,16 @@ class DirectoryViews(TestCase):
|
||||||
localname="mouse",
|
localname="mouse",
|
||||||
remote_id="https://example.com/users/mouse",
|
remote_id="https://example.com/users/mouse",
|
||||||
)
|
)
|
||||||
self.rat = models.User.objects.create_user(
|
|
||||||
|
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@local.com",
|
||||||
"rat@rat.com",
|
"rat@rat.com",
|
||||||
"ratword",
|
"ratword",
|
||||||
|
@ -29,10 +42,16 @@ class DirectoryViews(TestCase):
|
||||||
remote_id="https://example.com/users/rat",
|
remote_id="https://example.com/users/rat",
|
||||||
discoverable=True,
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.Directory.as_view()
|
view = views.Directory.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
@ -42,3 +61,12 @@ class DirectoryViews(TestCase):
|
||||||
self.assertIsInstance(result, TemplateResponse)
|
self.assertIsInstance(result, TemplateResponse)
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
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,12 +15,14 @@ from django.test.client import RequestFactory
|
||||||
from bookwyrm import forms, models, views
|
from bookwyrm import forms, models, views
|
||||||
|
|
||||||
|
|
||||||
|
@patch("bookwyrm.suggested_users.remove_user_task.delay")
|
||||||
class EditUserViews(TestCase):
|
class EditUserViews(TestCase):
|
||||||
"""view user and edit profile"""
|
"""view user and edit profile"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -44,7 +46,7 @@ class EditUserViews(TestCase):
|
||||||
self.anonymous_user = AnonymousUser
|
self.anonymous_user = AnonymousUser
|
||||||
self.anonymous_user.is_authenticated = False
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.EditUser.as_view()
|
view = views.EditUser.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
@ -54,7 +56,7 @@ class EditUserViews(TestCase):
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
def test_edit_user(self):
|
def test_edit_user(self, _):
|
||||||
"""use a form to update a user"""
|
"""use a form to update a user"""
|
||||||
view = views.EditUser.as_view()
|
view = views.EditUser.as_view()
|
||||||
form = forms.EditUserForm(instance=self.local_user)
|
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.name, "New Name")
|
||||||
self.assertEqual(self.local_user.email, "wow@email.com")
|
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"""
|
"""use a form to update a user"""
|
||||||
view = views.EditUser.as_view()
|
view = views.EditUser.as_view()
|
||||||
form = forms.EditUserForm(instance=self.local_user)
|
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.width, 120)
|
||||||
self.assertEqual(self.local_user.avatar.height, 120)
|
self.assertEqual(self.local_user.avatar.height, 120)
|
||||||
|
|
||||||
def test_crop_avatar(self):
|
def test_crop_avatar(self, _):
|
||||||
"""reduce that image size"""
|
"""reduce that image size"""
|
||||||
image_file = pathlib.Path(__file__).parent.joinpath(
|
image_file = pathlib.Path(__file__).parent.joinpath(
|
||||||
"../../static/images/no_cover.jpg"
|
"../../static/images/no_cover.jpg"
|
||||||
|
@ -112,7 +114,7 @@ class EditUserViews(TestCase):
|
||||||
image_result = Image.open(result)
|
image_result = Image.open(result)
|
||||||
self.assertEqual(image_result.size, (120, 120))
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.DeleteUser.as_view()
|
view = views.DeleteUser.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
@ -122,7 +124,8 @@ class EditUserViews(TestCase):
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
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"""
|
"""use a form to update a user"""
|
||||||
view = views.DeleteUser.as_view()
|
view = views.DeleteUser.as_view()
|
||||||
form = forms.DeleteUserForm()
|
form = forms.DeleteUserForm()
|
||||||
|
|
|
@ -15,6 +15,7 @@ class FederationViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -68,7 +69,7 @@ class FederationViews(TestCase):
|
||||||
identifier="hi.there.com",
|
identifier="hi.there.com",
|
||||||
)
|
)
|
||||||
self.remote_user.federated_server = server
|
self.remote_user.federated_server = server
|
||||||
self.remote_user.save()
|
self.remote_user.save(update_fields=["federated_server"])
|
||||||
|
|
||||||
self.assertEqual(server.status, "federated")
|
self.assertEqual(server.status, "federated")
|
||||||
|
|
||||||
|
@ -107,7 +108,9 @@ class FederationViews(TestCase):
|
||||||
self.remote_user.federated_server = server
|
self.remote_user.federated_server = server
|
||||||
self.remote_user.is_active = False
|
self.remote_user.is_active = False
|
||||||
self.remote_user.deactivation_reason = "domain_block"
|
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 = self.factory.post("")
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
|
@ -162,7 +165,7 @@ class FederationViews(TestCase):
|
||||||
"""load a json file with a list of servers to block"""
|
"""load a json file with a list of servers to block"""
|
||||||
server = models.FederatedServer.objects.create(server_name="hi.there.com")
|
server = models.FederatedServer.objects.create(server_name="hi.there.com")
|
||||||
self.remote_user.federated_server = server
|
self.remote_user.federated_server = server
|
||||||
self.remote_user.save()
|
self.remote_user.save(update_fields=["federated_server"])
|
||||||
|
|
||||||
data = [
|
data = [
|
||||||
{"instance": "server.name", "url": "https://explanation.url"}, # new server
|
{"instance": "server.name", "url": "https://explanation.url"}, # new server
|
||||||
|
|
|
@ -16,12 +16,15 @@ from bookwyrm.activitypub import ActivitypubResponse
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.get_activity_stream")
|
@patch("bookwyrm.activitystreams.ActivityStream.get_activity_stream")
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@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):
|
class FeedViews(TestCase):
|
||||||
"""activity feed, statuses, dms"""
|
"""activity feed, statuses, dms"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -36,6 +39,7 @@ class FeedViews(TestCase):
|
||||||
)
|
)
|
||||||
models.SiteSettings.objects.create()
|
models.SiteSettings.objects.create()
|
||||||
|
|
||||||
|
@patch("bookwyrm.suggested_users.SuggestedUsers.get_suggestions")
|
||||||
def test_feed(self, *_):
|
def test_feed(self, *_):
|
||||||
"""there are so many views, this just makes sure it LOADS"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.Feed.as_view()
|
view = views.Feed.as_view()
|
||||||
|
|
|
@ -10,12 +10,13 @@ from django.test.client import RequestFactory
|
||||||
from bookwyrm import models, views
|
from bookwyrm import models, views
|
||||||
|
|
||||||
|
|
||||||
class BookViews(TestCase):
|
class FollowViews(TestCase):
|
||||||
"""books books books"""
|
"""follows"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -66,6 +67,7 @@ class BookViews(TestCase):
|
||||||
|
|
||||||
def test_handle_follow_local_manually_approves(self):
|
def test_handle_follow_local_manually_approves(self):
|
||||||
"""send a follow request"""
|
"""send a follow request"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
rat = models.User.objects.create_user(
|
rat = models.User.objects.create_user(
|
||||||
"rat@local.com",
|
"rat@local.com",
|
||||||
"rat@rat.com",
|
"rat@rat.com",
|
||||||
|
@ -89,6 +91,7 @@ class BookViews(TestCase):
|
||||||
|
|
||||||
def test_handle_follow_local(self):
|
def test_handle_follow_local(self):
|
||||||
"""send a follow request"""
|
"""send a follow request"""
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
rat = models.User.objects.create_user(
|
rat = models.User.objects.create_user(
|
||||||
"rat@local.com",
|
"rat@local.com",
|
||||||
"rat@rat.com",
|
"rat@rat.com",
|
||||||
|
@ -127,7 +130,9 @@ class BookViews(TestCase):
|
||||||
def test_handle_accept(self):
|
def test_handle_accept(self):
|
||||||
"""accept a follow request"""
|
"""accept a follow request"""
|
||||||
self.local_user.manually_approves_followers = True
|
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 = self.factory.post("", {"user": self.remote_user.username})
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
rel = models.UserFollowRequest.objects.create(
|
rel = models.UserFollowRequest.objects.create(
|
||||||
|
@ -144,7 +149,9 @@ class BookViews(TestCase):
|
||||||
def test_handle_reject(self):
|
def test_handle_reject(self):
|
||||||
"""reject a follow request"""
|
"""reject a follow request"""
|
||||||
self.local_user.manually_approves_followers = True
|
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 = self.factory.post("", {"user": self.remote_user.username})
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
rel = models.UserFollowRequest.objects.create(
|
rel = models.UserFollowRequest.objects.create(
|
||||||
|
|
|
@ -13,6 +13,7 @@ class GetStartedViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -42,7 +43,9 @@ class GetStartedViews(TestCase):
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
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"""
|
"""save basic user details"""
|
||||||
view = views.GetStartedProfile.as_view()
|
view = views.GetStartedProfile.as_view()
|
||||||
form = forms.LimitedEditUserForm(instance=self.local_user)
|
form = forms.LimitedEditUserForm(instance=self.local_user)
|
||||||
|
@ -84,7 +87,8 @@ class GetStartedViews(TestCase):
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
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"""
|
"""shelve some books"""
|
||||||
view = views.GetStartedBooks.as_view()
|
view = views.GetStartedBooks.as_view()
|
||||||
data = {self.book.id: self.local_user.shelf_set.first().id}
|
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.book, self.book)
|
||||||
self.assertEqual(shelfbook.user, self.local_user)
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.GetStartedUsers.as_view()
|
view = views.GetStartedUsers.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
@ -114,7 +119,8 @@ class GetStartedViews(TestCase):
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.GetStartedUsers.as_view()
|
view = views.GetStartedUsers.as_view()
|
||||||
request = self.factory.get("?query=rat")
|
request = self.factory.get("?query=rat")
|
||||||
|
|
|
@ -16,6 +16,7 @@ class GoalViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import json
|
import json
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
import pathlib
|
import pathlib
|
||||||
from django.db.models import Q
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
|
@ -13,12 +12,16 @@ from bookwyrm.settings import USER_AGENT
|
||||||
|
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@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):
|
class ViewsHelpers(TestCase):
|
||||||
"""viewing and creating statuses"""
|
"""viewing and creating statuses"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
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(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -29,6 +32,7 @@ class ViewsHelpers(TestCase):
|
||||||
remote_id="https://example.com/users/mouse",
|
remote_id="https://example.com/users/mouse",
|
||||||
)
|
)
|
||||||
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
with patch("bookwyrm.models.user.set_remote_server.delay"):
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_user_task.delay"):
|
||||||
self.remote_user = models.User.objects.create_user(
|
self.remote_user = models.User.objects.create_user(
|
||||||
"rat",
|
"rat",
|
||||||
"rat@rat.com",
|
"rat@rat.com",
|
||||||
|
@ -53,12 +57,12 @@ class ViewsHelpers(TestCase):
|
||||||
name="Test Shelf", identifier="test-shelf", user=self.local_user
|
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"""
|
"""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.book.id), self.book)
|
||||||
self.assertEqual(views.helpers.get_edition(self.work.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"""
|
"""works for either localname or username"""
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
views.helpers.get_user_from_username(self.local_user, "mouse"),
|
views.helpers.get_user_from_username(self.local_user, "mouse"),
|
||||||
|
@ -71,7 +75,7 @@ class ViewsHelpers(TestCase):
|
||||||
with self.assertRaises(Http404):
|
with self.assertRaises(Http404):
|
||||||
views.helpers.get_user_from_username(self.local_user, "mojfse@example.com")
|
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"""
|
"""should it return html or json"""
|
||||||
request = self.factory.get("/path")
|
request = self.factory.get("/path")
|
||||||
request.headers = {"Accept": "application/json"}
|
request.headers = {"Accept": "application/json"}
|
||||||
|
@ -85,12 +89,12 @@ class ViewsHelpers(TestCase):
|
||||||
request.headers = {"Accept": "Praise"}
|
request.headers = {"Accept": "Praise"}
|
||||||
self.assertFalse(views.helpers.is_api_request(request))
|
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"""
|
"""should it return html or json"""
|
||||||
request = self.factory.get("/path")
|
request = self.factory.get("/path")
|
||||||
self.assertFalse(views.helpers.is_api_request(request))
|
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"""
|
"""checks if a request came from a bookwyrm instance"""
|
||||||
request = self.factory.get("", {"q": "Test Book"})
|
request = self.factory.get("", {"q": "Test Book"})
|
||||||
self.assertFalse(views.helpers.is_bookwyrm_request(request))
|
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)
|
request = self.factory.get("", {"q": "Test Book"}, HTTP_USER_AGENT=USER_AGENT)
|
||||||
self.assertTrue(views.helpers.is_bookwyrm_request(request))
|
self.assertTrue(views.helpers.is_bookwyrm_request(request))
|
||||||
|
|
||||||
def test_existing_user(self, _):
|
def test_existing_user(self, *_):
|
||||||
"""simple database lookup by username"""
|
"""simple database lookup by username"""
|
||||||
result = views.helpers.handle_remote_webfinger("@mouse@local.com")
|
result = views.helpers.handle_remote_webfinger("@mouse@local.com")
|
||||||
self.assertEqual(result, self.local_user)
|
self.assertEqual(result, self.local_user)
|
||||||
|
@ -117,7 +121,7 @@ class ViewsHelpers(TestCase):
|
||||||
self.assertEqual(result, self.local_user)
|
self.assertEqual(result, self.local_user)
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_load_user(self, _):
|
def test_load_user(self, *_):
|
||||||
"""find a remote user using webfinger"""
|
"""find a remote user using webfinger"""
|
||||||
username = "mouse@example.com"
|
username = "mouse@example.com"
|
||||||
wellknown = {
|
wellknown = {
|
||||||
|
@ -147,7 +151,7 @@ class ViewsHelpers(TestCase):
|
||||||
self.assertIsInstance(result, models.User)
|
self.assertIsInstance(result, models.User)
|
||||||
self.assertEqual(result.username, "mouse@example.com")
|
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"""
|
"""find a remote user using webfinger"""
|
||||||
models.FederatedServer.objects.create(
|
models.FederatedServer.objects.create(
|
||||||
server_name="example.com", status="blocked"
|
server_name="example.com", status="blocked"
|
||||||
|
@ -156,7 +160,7 @@ class ViewsHelpers(TestCase):
|
||||||
result = views.helpers.handle_remote_webfinger("@mouse@example.com")
|
result = views.helpers.handle_remote_webfinger("@mouse@example.com")
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_handle_reading_status_to_read(self, _):
|
def test_handle_reading_status_to_read(self, *_):
|
||||||
"""posts shelve activities"""
|
"""posts shelve activities"""
|
||||||
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
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.mention_books.first(), self.book)
|
||||||
self.assertEqual(status.content, "wants to read")
|
self.assertEqual(status.content, "wants to read")
|
||||||
|
|
||||||
def test_handle_reading_status_reading(self, _):
|
def test_handle_reading_status_reading(self, *_):
|
||||||
"""posts shelve activities"""
|
"""posts shelve activities"""
|
||||||
shelf = self.local_user.shelf_set.get(identifier="reading")
|
shelf = self.local_user.shelf_set.get(identifier="reading")
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
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.mention_books.first(), self.book)
|
||||||
self.assertEqual(status.content, "started reading")
|
self.assertEqual(status.content, "started reading")
|
||||||
|
|
||||||
def test_handle_reading_status_read(self, _):
|
def test_handle_reading_status_read(self, *_):
|
||||||
"""posts shelve activities"""
|
"""posts shelve activities"""
|
||||||
shelf = self.local_user.shelf_set.get(identifier="read")
|
shelf = self.local_user.shelf_set.get(identifier="read")
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
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.mention_books.first(), self.book)
|
||||||
self.assertEqual(status.content, "finished reading")
|
self.assertEqual(status.content, "finished reading")
|
||||||
|
|
||||||
def test_handle_reading_status_other(self, _):
|
def test_handle_reading_status_other(self, *_):
|
||||||
"""posts shelve activities"""
|
"""posts shelve activities"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
views.helpers.handle_reading_status(
|
views.helpers.handle_reading_status(
|
||||||
self.local_user, self.shelf, self.book, "public"
|
self.local_user, self.shelf, self.book, "public"
|
||||||
)
|
)
|
||||||
self.assertFalse(models.GeneratedNote.objects.exists())
|
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,6 +14,7 @@ class ImportViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -15,6 +15,7 @@ class InteractionViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -16,6 +16,7 @@ class InviteViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -16,6 +16,7 @@ class IsbnViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -15,6 +15,7 @@ class LandingViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -26,7 +27,8 @@ class LandingViews(TestCase):
|
||||||
self.anonymous_user.is_authenticated = False
|
self.anonymous_user.is_authenticated = False
|
||||||
models.SiteSettings.objects.create()
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.Home.as_view()
|
view = views.Home.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
|
|
@ -17,6 +17,7 @@ class ListViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -15,6 +15,7 @@ class ListActionViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" test for app action functionality """
|
""" test for app action functionality """
|
||||||
|
from unittest.mock import patch
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
|
@ -13,6 +14,7 @@ class NotificationViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -18,6 +18,7 @@ class OutboxView(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we'll need some data"""
|
"""we'll need some data"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -15,6 +15,7 @@ class PasswordViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -9,12 +9,14 @@ from bookwyrm import models, views
|
||||||
|
|
||||||
|
|
||||||
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
@patch("bookwyrm.activitystreams.ActivityStream.add_status")
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
class ReadingViews(TestCase):
|
class ReadingViews(TestCase):
|
||||||
"""viewing and creating statuses"""
|
"""viewing and creating statuses"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -40,7 +42,7 @@ class ReadingViews(TestCase):
|
||||||
parent_work=self.work,
|
parent_work=self.work,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_start_reading(self, _):
|
def test_start_reading(self, *_):
|
||||||
"""begin a book"""
|
"""begin a book"""
|
||||||
shelf = self.local_user.shelf_set.get(identifier=models.Shelf.READING)
|
shelf = self.local_user.shelf_set.get(identifier=models.Shelf.READING)
|
||||||
self.assertFalse(shelf.books.exists())
|
self.assertFalse(shelf.books.exists())
|
||||||
|
@ -71,7 +73,7 @@ class ReadingViews(TestCase):
|
||||||
self.assertEqual(readthrough.user, self.local_user)
|
self.assertEqual(readthrough.user, self.local_user)
|
||||||
self.assertEqual(readthrough.book, self.book)
|
self.assertEqual(readthrough.book, self.book)
|
||||||
|
|
||||||
def test_start_reading_reshelf(self, _):
|
def test_start_reading_reshelf(self, *_):
|
||||||
"""begin a book"""
|
"""begin a book"""
|
||||||
to_read_shelf = self.local_user.shelf_set.get(identifier=models.Shelf.TO_READ)
|
to_read_shelf = self.local_user.shelf_set.get(identifier=models.Shelf.TO_READ)
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
|
@ -91,7 +93,7 @@ class ReadingViews(TestCase):
|
||||||
self.assertFalse(to_read_shelf.books.exists())
|
self.assertFalse(to_read_shelf.books.exists())
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
self.assertEqual(shelf.books.get(), self.book)
|
||||||
|
|
||||||
def test_finish_reading(self, _):
|
def test_finish_reading(self, *_):
|
||||||
"""begin a book"""
|
"""begin a book"""
|
||||||
shelf = self.local_user.shelf_set.get(identifier=models.Shelf.READ_FINISHED)
|
shelf = self.local_user.shelf_set.get(identifier=models.Shelf.READ_FINISHED)
|
||||||
self.assertFalse(shelf.books.exists())
|
self.assertFalse(shelf.books.exists())
|
||||||
|
@ -127,7 +129,7 @@ class ReadingViews(TestCase):
|
||||||
self.assertEqual(readthrough.user, self.local_user)
|
self.assertEqual(readthrough.user, self.local_user)
|
||||||
self.assertEqual(readthrough.book, self.book)
|
self.assertEqual(readthrough.book, self.book)
|
||||||
|
|
||||||
def test_edit_readthrough(self, _):
|
def test_edit_readthrough(self, *_):
|
||||||
"""adding dates to an ongoing readthrough"""
|
"""adding dates to an ongoing readthrough"""
|
||||||
start = timezone.make_aware(dateutil.parser.parse("2021-01-03"))
|
start = timezone.make_aware(dateutil.parser.parse("2021-01-03"))
|
||||||
readthrough = models.ReadThrough.objects.create(
|
readthrough = models.ReadThrough.objects.create(
|
||||||
|
@ -154,7 +156,7 @@ class ReadingViews(TestCase):
|
||||||
self.assertEqual(readthrough.finish_date.day, 7)
|
self.assertEqual(readthrough.finish_date.day, 7)
|
||||||
self.assertEqual(readthrough.book, self.book)
|
self.assertEqual(readthrough.book, self.book)
|
||||||
|
|
||||||
def test_delete_readthrough(self, _):
|
def test_delete_readthrough(self, *_):
|
||||||
"""remove a readthrough"""
|
"""remove a readthrough"""
|
||||||
readthrough = models.ReadThrough.objects.create(
|
readthrough = models.ReadThrough.objects.create(
|
||||||
book=self.book, user=self.local_user
|
book=self.book, user=self.local_user
|
||||||
|
@ -171,7 +173,7 @@ class ReadingViews(TestCase):
|
||||||
views.delete_readthrough(request)
|
views.delete_readthrough(request)
|
||||||
self.assertFalse(models.ReadThrough.objects.filter(id=readthrough.id).exists())
|
self.assertFalse(models.ReadThrough.objects.filter(id=readthrough.id).exists())
|
||||||
|
|
||||||
def test_create_readthrough(self, _):
|
def test_create_readthrough(self, *_):
|
||||||
"""adding new read dates"""
|
"""adding new read dates"""
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.utils import timezone
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||||
class ReadThrough(TestCase):
|
class ReadThrough(TestCase):
|
||||||
"""readthrough tests"""
|
"""readthrough tests"""
|
||||||
|
@ -21,6 +22,7 @@ class ReadThrough(TestCase):
|
||||||
title="Example Edition", parent_work=self.work
|
title="Example Edition", parent_work=self.work
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"cinco", "cinco@example.com", "seissiete", local=True, localname="cinco"
|
"cinco", "cinco@example.com", "seissiete", local=True, localname="cinco"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +30,7 @@ class ReadThrough(TestCase):
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
self.client.force_login(self.user)
|
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"""
|
"""A basic readthrough doesn't create a progress update"""
|
||||||
self.assertEqual(self.edition.readthrough_set.count(), 0)
|
self.assertEqual(self.edition.readthrough_set.count(), 0)
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ class ReadThrough(TestCase):
|
||||||
self.assertEqual(readthroughs[0].finish_date, None)
|
self.assertEqual(readthroughs[0].finish_date, None)
|
||||||
self.assertEqual(delay_mock.call_count, 1)
|
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"""
|
"""a readthrough with progress"""
|
||||||
self.assertEqual(self.edition.readthrough_set.count(), 0)
|
self.assertEqual(self.edition.readthrough_set.count(), 0)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" test for app action functionality """
|
""" test for app action functionality """
|
||||||
|
from unittest.mock import patch
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
|
@ -12,6 +13,7 @@ class ReportViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -114,7 +116,9 @@ class ReportViews(TestCase):
|
||||||
report.refresh_from_db()
|
report.refresh_from_db()
|
||||||
self.assertFalse(report.resolved)
|
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"""
|
"""toggle whether a user is able to log in"""
|
||||||
self.assertTrue(self.rat.is_active)
|
self.assertTrue(self.rat.is_active)
|
||||||
request = self.factory.post("")
|
request = self.factory.post("")
|
||||||
|
|
|
@ -14,6 +14,7 @@ class RssFeedView(TestCase):
|
||||||
"""test data"""
|
"""test data"""
|
||||||
self.site = models.SiteSettings.objects.create()
|
self.site = models.SiteSettings.objects.create()
|
||||||
|
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.user = models.User.objects.create_user(
|
self.user = models.User.objects.create_user(
|
||||||
"rss_user", "rss@test.rss", "password", local=True
|
"rss_user", "rss@test.rss", "password", local=True
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Views(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
|
|
@ -10,12 +10,14 @@ from bookwyrm.activitypub import ActivitypubResponse
|
||||||
|
|
||||||
|
|
||||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
class ShelfViews(TestCase):
|
class ShelfViews(TestCase):
|
||||||
"""tag views"""
|
"""tag views"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -36,7 +38,7 @@ class ShelfViews(TestCase):
|
||||||
)
|
)
|
||||||
models.SiteSettings.objects.create()
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.Shelf.as_view()
|
view = views.Shelf.as_view()
|
||||||
shelf = self.local_user.shelf_set.first()
|
shelf = self.local_user.shelf_set.first()
|
||||||
|
@ -63,7 +65,7 @@ class ShelfViews(TestCase):
|
||||||
self.assertIsInstance(result, ActivitypubResponse)
|
self.assertIsInstance(result, ActivitypubResponse)
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
def test_edit_shelf_privacy(self, _):
|
def test_edit_shelf_privacy(self, *_):
|
||||||
"""set name or privacy on shelf"""
|
"""set name or privacy on shelf"""
|
||||||
view = views.Shelf.as_view()
|
view = views.Shelf.as_view()
|
||||||
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
||||||
|
@ -83,7 +85,7 @@ class ShelfViews(TestCase):
|
||||||
|
|
||||||
self.assertEqual(shelf.privacy, "unlisted")
|
self.assertEqual(shelf.privacy, "unlisted")
|
||||||
|
|
||||||
def test_edit_shelf_name(self, _):
|
def test_edit_shelf_name(self, *_):
|
||||||
"""change the name of an editable shelf"""
|
"""change the name of an editable shelf"""
|
||||||
view = views.Shelf.as_view()
|
view = views.Shelf.as_view()
|
||||||
shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user)
|
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.name, "cool name")
|
||||||
self.assertEqual(shelf.identifier, "testshelf-%d" % shelf.id)
|
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"""
|
"""can't change the name of an non-editable shelf"""
|
||||||
view = views.Shelf.as_view()
|
view = views.Shelf.as_view()
|
||||||
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
shelf = self.local_user.shelf_set.get(identifier="to-read")
|
||||||
|
@ -115,7 +117,7 @@ class ShelfViews(TestCase):
|
||||||
|
|
||||||
self.assertEqual(shelf.name, "To Read")
|
self.assertEqual(shelf.name, "To Read")
|
||||||
|
|
||||||
def test_handle_shelve(self, _):
|
def test_handle_shelve(self, *_):
|
||||||
"""shelve a book"""
|
"""shelve a book"""
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
"", {"book": self.book.id, "shelf": self.shelf.identifier}
|
"", {"book": self.book.id, "shelf": self.shelf.identifier}
|
||||||
|
@ -133,7 +135,7 @@ class ShelfViews(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(self.shelf.books.get(), self.book)
|
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"""
|
"""special behavior for the to-read shelf"""
|
||||||
shelf = models.Shelf.objects.get(identifier="to-read")
|
shelf = models.Shelf.objects.get(identifier="to-read")
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
|
@ -146,7 +148,7 @@ class ShelfViews(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
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"""
|
"""special behavior for the reading shelf"""
|
||||||
shelf = models.Shelf.objects.get(identifier="reading")
|
shelf = models.Shelf.objects.get(identifier="reading")
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
|
@ -159,7 +161,7 @@ class ShelfViews(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
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"""
|
"""special behavior for the read shelf"""
|
||||||
shelf = models.Shelf.objects.get(identifier="read")
|
shelf = models.Shelf.objects.get(identifier="read")
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
|
@ -172,7 +174,7 @@ class ShelfViews(TestCase):
|
||||||
# make sure the book is on the shelf
|
# make sure the book is on the shelf
|
||||||
self.assertEqual(shelf.books.get(), self.book)
|
self.assertEqual(shelf.books.get(), self.book)
|
||||||
|
|
||||||
def test_handle_unshelve(self, _):
|
def test_handle_unshelve(self, *_):
|
||||||
"""remove a book from a shelf"""
|
"""remove a book from a shelf"""
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
models.ShelfBook.objects.create(
|
models.ShelfBook.objects.create(
|
||||||
|
|
|
@ -9,6 +9,7 @@ from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
|
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
|
||||||
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
|
||||||
class StatusViews(TestCase):
|
class StatusViews(TestCase):
|
||||||
"""viewing and creating statuses"""
|
"""viewing and creating statuses"""
|
||||||
|
@ -16,6 +17,7 @@ class StatusViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.com",
|
"mouse@mouse.com",
|
||||||
|
@ -43,7 +45,7 @@ class StatusViews(TestCase):
|
||||||
)
|
)
|
||||||
models.SiteSettings.objects.create()
|
models.SiteSettings.objects.create()
|
||||||
|
|
||||||
def test_handle_status(self, _):
|
def test_handle_status(self, *_):
|
||||||
"""create a status"""
|
"""create a status"""
|
||||||
view = views.CreateStatus.as_view()
|
view = views.CreateStatus.as_view()
|
||||||
form = forms.CommentForm(
|
form = forms.CommentForm(
|
||||||
|
@ -66,7 +68,7 @@ class StatusViews(TestCase):
|
||||||
self.assertEqual(status.user, self.local_user)
|
self.assertEqual(status.user, self.local_user)
|
||||||
self.assertEqual(status.book, self.book)
|
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"""
|
"""create a status in reply to an existing status"""
|
||||||
view = views.CreateStatus.as_view()
|
view = views.CreateStatus.as_view()
|
||||||
user = models.User.objects.create_user(
|
user = models.User.objects.create_user(
|
||||||
|
@ -96,7 +98,7 @@ class StatusViews(TestCase):
|
||||||
self.assertEqual(status.user, user)
|
self.assertEqual(status.user, user)
|
||||||
self.assertEqual(models.Notification.objects.get().user, self.local_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"""
|
"""@mention a user in a post"""
|
||||||
view = views.CreateStatus.as_view()
|
view = views.CreateStatus.as_view()
|
||||||
user = models.User.objects.create_user(
|
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
|
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"""
|
"""reply to a post with an @mention'ed user"""
|
||||||
view = views.CreateStatus.as_view()
|
view = views.CreateStatus.as_view()
|
||||||
user = models.User.objects.create_user(
|
user = models.User.objects.create_user(
|
||||||
|
@ -172,7 +174,7 @@ class StatusViews(TestCase):
|
||||||
self.assertFalse(self.remote_user in reply.mention_users.all())
|
self.assertFalse(self.remote_user in reply.mention_users.all())
|
||||||
self.assertTrue(self.local_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"""
|
"""delete and re-draft a status"""
|
||||||
view = views.DeleteAndRedraft.as_view()
|
view = views.DeleteAndRedraft.as_view()
|
||||||
request = self.factory.post("")
|
request = self.factory.post("")
|
||||||
|
@ -193,7 +195,7 @@ class StatusViews(TestCase):
|
||||||
status.refresh_from_db()
|
status.refresh_from_db()
|
||||||
self.assertTrue(status.deleted)
|
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"""
|
"""you can't redraft generated statuses"""
|
||||||
view = views.DeleteAndRedraft.as_view()
|
view = views.DeleteAndRedraft.as_view()
|
||||||
request = self.factory.post("")
|
request = self.factory.post("")
|
||||||
|
@ -213,7 +215,7 @@ class StatusViews(TestCase):
|
||||||
status.refresh_from_db()
|
status.refresh_from_db()
|
||||||
self.assertFalse(status.deleted)
|
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"""
|
"""you can't redraft generated statuses"""
|
||||||
view = views.DeleteAndRedraft.as_view()
|
view = views.DeleteAndRedraft.as_view()
|
||||||
request = self.factory.post("")
|
request = self.factory.post("")
|
||||||
|
@ -233,7 +235,7 @@ class StatusViews(TestCase):
|
||||||
status.refresh_from_db()
|
status.refresh_from_db()
|
||||||
self.assertFalse(status.deleted)
|
self.assertFalse(status.deleted)
|
||||||
|
|
||||||
def test_find_mentions(self, _):
|
def test_find_mentions(self, *_):
|
||||||
"""detect and look up @ mentions of users"""
|
"""detect and look up @ mentions of users"""
|
||||||
user = models.User.objects.create_user(
|
user = models.User.objects.create_user(
|
||||||
"nutria@%s" % DOMAIN,
|
"nutria@%s" % DOMAIN,
|
||||||
|
@ -279,7 +281,7 @@ class StatusViews(TestCase):
|
||||||
("@nutria@%s" % DOMAIN, user),
|
("@nutria@%s" % DOMAIN, user),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_format_links(self, _):
|
def test_format_links(self, *_):
|
||||||
"""find and format urls into a tags"""
|
"""find and format urls into a tags"""
|
||||||
url = "http://www.fish.com/"
|
url = "http://www.fish.com/"
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -302,7 +304,7 @@ class StatusViews(TestCase):
|
||||||
"?q=arkady+strugatsky&mode=everything</a>" % url,
|
"?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"""
|
"""this is mostly handled in other places, but nonetheless"""
|
||||||
text = "_hi_ and http://fish.com is <marquee>rad</marquee>"
|
text = "_hi_ and http://fish.com is <marquee>rad</marquee>"
|
||||||
result = views.status.to_markdown(text)
|
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>",
|
'<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"""
|
"""this is mostly handled in other places, but nonetheless"""
|
||||||
text = "http://fish.com/@hello#okay"
|
text = "http://fish.com/@hello#okay"
|
||||||
result = views.status.to_markdown(text)
|
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>',
|
'<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"""
|
"""this is mostly handled in other places, but nonetheless"""
|
||||||
text = "[hi](http://fish.com) is <marquee>rad</marquee>"
|
text = "[hi](http://fish.com) is <marquee>rad</marquee>"
|
||||||
result = views.status.to_markdown(text)
|
result = views.status.to_markdown(text)
|
||||||
self.assertEqual(result, '<p><a href="http://fish.com">hi</a> ' "is rad</p>")
|
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"""
|
"""marks a status as deleted"""
|
||||||
view = views.DeleteStatus.as_view()
|
view = views.DeleteStatus.as_view()
|
||||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||||
|
@ -346,7 +348,7 @@ class StatusViews(TestCase):
|
||||||
status.refresh_from_db()
|
status.refresh_from_db()
|
||||||
self.assertTrue(status.deleted)
|
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"""
|
"""marks a status as deleted"""
|
||||||
view = views.DeleteStatus.as_view()
|
view = views.DeleteStatus.as_view()
|
||||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||||
|
@ -360,7 +362,7 @@ class StatusViews(TestCase):
|
||||||
status.refresh_from_db()
|
status.refresh_from_db()
|
||||||
self.assertFalse(status.deleted)
|
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"""
|
"""marks a status as deleted"""
|
||||||
view = views.DeleteStatus.as_view()
|
view = views.DeleteStatus.as_view()
|
||||||
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
|
||||||
|
|
|
@ -15,6 +15,7 @@ class UpdateViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -17,6 +17,7 @@ class UserViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -29,6 +30,7 @@ class UserViews(TestCase):
|
||||||
)
|
)
|
||||||
self.book = models.Edition.objects.create(title="test")
|
self.book = models.Edition.objects.create(title="test")
|
||||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
models.ShelfBook.objects.create(
|
models.ShelfBook.objects.create(
|
||||||
book=self.book,
|
book=self.book,
|
||||||
user=self.local_user,
|
user=self.local_user,
|
||||||
|
@ -94,7 +96,8 @@ class UserViews(TestCase):
|
||||||
self.assertIsInstance(result, ActivitypubResponse)
|
self.assertIsInstance(result, ActivitypubResponse)
|
||||||
self.assertEqual(result.status_code, 200)
|
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"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
view = views.Followers.as_view()
|
view = views.Followers.as_view()
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
|
|
|
@ -14,6 +14,7 @@ class UserAdminViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
@ -47,7 +48,9 @@ class UserAdminViews(TestCase):
|
||||||
result.render()
|
result.render()
|
||||||
self.assertEqual(result.status_code, 200)
|
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"""
|
"""set the user's group"""
|
||||||
group = Group.objects.create(name="editor")
|
group = Group.objects.create(name="editor")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -16,6 +16,7 @@ class UserViews(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""we need basic test data and mocks"""
|
"""we need basic test data and mocks"""
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"):
|
||||||
self.local_user = models.User.objects.create_user(
|
self.local_user = models.User.objects.create_user(
|
||||||
"mouse@local.com",
|
"mouse@local.com",
|
||||||
"mouse@mouse.mouse",
|
"mouse@mouse.mouse",
|
||||||
|
|
|
@ -50,7 +50,7 @@ class Login(View):
|
||||||
# successful login
|
# successful login
|
||||||
login(request, user)
|
login(request, user)
|
||||||
user.last_active_date = timezone.now()
|
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", "/"))
|
return redirect(request.GET.get("next", "/"))
|
||||||
|
|
||||||
# login errors
|
# login errors
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.template.response import TemplateResponse
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
from .helpers import get_annotated_users
|
from bookwyrm import suggested_users
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@ -23,7 +23,7 @@ class Directory(View):
|
||||||
if scope == "local":
|
if scope == "local":
|
||||||
filters["local"] = True
|
filters["local"] = True
|
||||||
|
|
||||||
users = get_annotated_users(request.user, **filters)
|
users = suggested_users.get_annotated_users(request.user, **filters)
|
||||||
sort = request.GET.get("sort")
|
sort = request.GET.get("sort")
|
||||||
if sort == "recent":
|
if sort == "recent":
|
||||||
users = users.order_by("-last_active_date")
|
users = users.order_by("-last_active_date")
|
||||||
|
|
|
@ -11,7 +11,8 @@ from django.views import View
|
||||||
from bookwyrm import activitystreams, forms, models
|
from bookwyrm import activitystreams, forms, models
|
||||||
from bookwyrm.activitypub import ActivitypubResponse
|
from bookwyrm.activitypub import ActivitypubResponse
|
||||||
from bookwyrm.settings import PAGE_LENGTH, STREAMS
|
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
|
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)
|
activities = activitystreams.streams[tab].get_activity_stream(request.user)
|
||||||
paginated = Paginator(activities, PAGE_LENGTH)
|
paginated = Paginator(activities, PAGE_LENGTH)
|
||||||
|
|
||||||
suggested_users = get_suggested_users(request.user)
|
suggestions = suggested_users.get_suggestions(request.user)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
**feed_page_data(request.user),
|
**feed_page_data(request.user),
|
||||||
**{
|
**{
|
||||||
"user": request.user,
|
"user": request.user,
|
||||||
"activities": paginated.get_page(request.GET.get("page")),
|
"activities": paginated.get_page(request.GET.get("page")),
|
||||||
"suggested_users": suggested_users,
|
"suggested_users": suggestions,
|
||||||
"tab": tab,
|
"tab": tab,
|
||||||
"goal_form": forms.GoalForm(),
|
"goal_form": forms.GoalForm(),
|
||||||
"path": "/%s" % tab,
|
"path": "/%s" % tab,
|
||||||
|
|
|
@ -13,7 +13,7 @@ from django.views import View
|
||||||
|
|
||||||
from bookwyrm import forms, models
|
from bookwyrm import forms, models
|
||||||
from bookwyrm.connectors import connector_manager
|
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
|
from .edit_user import save_user_form
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,11 +118,12 @@ class GetStartedUsers(View):
|
||||||
)
|
)
|
||||||
.order_by("-similarity")[:5]
|
.order_by("-similarity")[:5]
|
||||||
)
|
)
|
||||||
|
data = {"no_results": not user_results}
|
||||||
|
|
||||||
if user_results.count() < 5:
|
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 = {
|
data["suggested_users"] = user_results
|
||||||
"suggested_users": user_results,
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, "get_started/users.html", data)
|
return TemplateResponse(request, "get_started/users.html", data)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import re
|
import re
|
||||||
from requests import HTTPError
|
from requests import HTTPError
|
||||||
from django.core.exceptions import FieldError
|
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 django.http import Http404
|
||||||
|
|
||||||
from bookwyrm import activitypub, models
|
from bookwyrm import activitypub, models
|
||||||
|
@ -177,47 +177,3 @@ def get_discover_books():
|
||||||
.order_by("-review__published_date__max")[:6]
|
.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)
|
return TemplateResponse(request, "password_reset.html", data)
|
||||||
|
|
||||||
user.set_password(new_password)
|
user.set_password(new_password)
|
||||||
user.save(broadcast=False)
|
user.save(broadcast=False, update_fields=["password"])
|
||||||
login(request, user)
|
login(request, user)
|
||||||
reset_code.delete()
|
reset_code.delete()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
@ -100,6 +100,6 @@ class ChangePassword(View):
|
||||||
return redirect("preferences/password")
|
return redirect("preferences/password")
|
||||||
|
|
||||||
request.user.set_password(new_password)
|
request.user.set_password(new_password)
|
||||||
request.user.save(broadcast=False)
|
request.user.save(broadcast=False, update_fields=["password"])
|
||||||
login(request, request.user)
|
login(request, request.user)
|
||||||
return redirect(request.user.local_path)
|
return redirect(request.user.local_path)
|
||||||
|
|
4
bw-dev
4
bw-dev
|
@ -123,6 +123,9 @@ case "$CMD" in
|
||||||
populate_streams)
|
populate_streams)
|
||||||
runweb python manage.py populate_streams
|
runweb python manage.py populate_streams
|
||||||
;;
|
;;
|
||||||
|
populate_suggestions)
|
||||||
|
runweb python manage.py populate_suggestions
|
||||||
|
;;
|
||||||
generate_preview_images)
|
generate_preview_images)
|
||||||
runweb python manage.py generate_preview_images $@
|
runweb python manage.py generate_preview_images $@
|
||||||
;;
|
;;
|
||||||
|
@ -167,6 +170,7 @@ case "$CMD" in
|
||||||
echo " clean"
|
echo " clean"
|
||||||
echo " black"
|
echo " black"
|
||||||
echo " populate_streams"
|
echo " populate_streams"
|
||||||
|
echo " populate_suggestions"
|
||||||
echo " generate_preview_images [--all]"
|
echo " generate_preview_images [--all]"
|
||||||
echo " copy_media_to_s3"
|
echo " copy_media_to_s3"
|
||||||
echo " set_cors_to_s3 [cors file]"
|
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="goodreads_import")
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="preview_images")
|
app.autodiscover_tasks(["bookwyrm"], related_name="preview_images")
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="models.user")
|
app.autodiscover_tasks(["bookwyrm"], related_name="models.user")
|
||||||
|
app.autodiscover_tasks(["bookwyrm"], related_name="suggested_users")
|
||||||
app.autodiscover_tasks(["bookwyrm"], related_name="views.inbox")
|
app.autodiscover_tasks(["bookwyrm"], related_name="views.inbox")
|
||||||
|
|
Loading…
Reference in a new issue