Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2021-06-27 08:05:08 -07:00
commit 8534e49f96
138 changed files with 3161 additions and 2224 deletions

View file

@ -41,3 +41,15 @@ EMAIL_HOST_USER=mail@your.domain.here
EMAIL_HOST_PASSWORD=emailpassword123 EMAIL_HOST_PASSWORD=emailpassword123
EMAIL_USE_TLS=true EMAIL_USE_TLS=true
EMAIL_USE_SSL=false EMAIL_USE_SSL=false
# Preview image generation can be computing and storage intensive
# ENABLE_PREVIEW_IMAGES=True
# Specify RGB tuple or RGB hex strings,
# or use_dominant_color_light / use_dominant_color_dark
PREVIEW_BG_COLOR=use_dominant_color_light
# Change to #FFF if you use use_dominant_color_dark
PREVIEW_TEXT_COLOR="#363636"
PREVIEW_IMG_WIDTH=1200
PREVIEW_IMG_HEIGHT=630
PREVIEW_DEFAULT_COVER_COLOR="#002549"

View file

@ -41,3 +41,15 @@ EMAIL_HOST_USER=mail@your.domain.here
EMAIL_HOST_PASSWORD=emailpassword123 EMAIL_HOST_PASSWORD=emailpassword123
EMAIL_USE_TLS=true EMAIL_USE_TLS=true
EMAIL_USE_SSL=false EMAIL_USE_SSL=false
# Preview image generation can be computing and storage intensive
# ENABLE_PREVIEW_IMAGES=True
# Specify RGB tuple or RGB hex strings,
# or use_dominant_color_light / use_dominant_color_dark
PREVIEW_BG_COLOR=use_dominant_color_light
# Change to #FFF if you use use_dominant_color_dark
PREVIEW_TEXT_COLOR="#363636"
PREVIEW_IMG_WIDTH=1200
PREVIEW_IMG_HEIGHT=630
PREVIEW_DEFAULT_COVER_COLOR="#002549"

View file

@ -1,4 +1,4 @@
name: Lint Python name: Python Formatting (run ./bw-dev black to fix)
on: [push, pull_request] on: [push, pull_request]

View file

@ -20,7 +20,7 @@ jobs:
services: services:
postgres: postgres:
image: postgres:10 image: postgres:12
env: env:
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: hunter2 POSTGRES_PASSWORD: hunter2
@ -64,5 +64,6 @@ jobs:
EMAIL_HOST_USER: "" EMAIL_HOST_USER: ""
EMAIL_HOST_PASSWORD: "" EMAIL_HOST_PASSWORD: ""
EMAIL_USE_TLS: true EMAIL_USE_TLS: true
ENABLE_PREVIEW_IMAGES: true
run: | run: |
python manage.py test pytest

24
.github/workflows/pylint.yml vendored Normal file
View file

@ -0,0 +1,24 @@
name: Pylint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pylint
- name: Analysing the code with pylint
run: |
pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801

3
.gitignore vendored
View file

@ -27,3 +27,6 @@
#nginx #nginx
nginx/default.conf nginx/default.conf
#macOS
**/.DS_Store

View file

@ -37,6 +37,7 @@ class Mention(Link):
@dataclass @dataclass
# pylint: disable=invalid-name
class Signature: class Signature:
"""public key block""" """public key block"""
@ -56,11 +57,11 @@ def naive_parse(activity_objects, activity_json, serializer=None):
activity_type = activity_json.get("type") activity_type = activity_json.get("type")
try: try:
serializer = activity_objects[activity_type] serializer = activity_objects[activity_type]
except KeyError as e: except KeyError as err:
# we know this exists and that we can't handle it # we know this exists and that we can't handle it
if activity_type in ["Question"]: if activity_type in ["Question"]:
return None return None
raise ActivitySerializerError(e) raise ActivitySerializerError(err)
return serializer(activity_objects=activity_objects, **activity_json) return serializer(activity_objects=activity_objects, **activity_json)

View file

@ -6,6 +6,7 @@ from .base_activity import ActivityObject
from .image import Document from .image import Document
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class BookData(ActivityObject): class BookData(ActivityObject):
"""shared fields for all book data and authors""" """shared fields for all book data and authors"""
@ -18,6 +19,7 @@ class BookData(ActivityObject):
lastEditedBy: str = None lastEditedBy: str = None
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Book(BookData): class Book(BookData):
"""serializes an edition or work, abstract""" """serializes an edition or work, abstract"""
@ -40,6 +42,7 @@ class Book(BookData):
type: str = "Book" type: str = "Book"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Edition(Book): class Edition(Book):
"""Edition instance of a book object""" """Edition instance of a book object"""
@ -57,6 +60,7 @@ class Edition(Book):
type: str = "Edition" type: str = "Edition"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Work(Book): class Work(Book):
"""work instance of a book object""" """work instance of a book object"""
@ -66,6 +70,7 @@ class Work(Book):
type: str = "Work" type: str = "Work"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Author(BookData): class Author(BookData):
"""author of a book""" """author of a book"""

View file

@ -19,6 +19,7 @@ class Tombstone(ActivityObject):
return model.find_existing_by_remote_id(self.id) return model.find_existing_by_remote_id(self.id)
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Note(ActivityObject): class Note(ActivityObject):
"""Note activity""" """Note activity"""
@ -52,6 +53,7 @@ class GeneratedNote(Note):
type: str = "GeneratedNote" type: str = "GeneratedNote"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Comment(Note): class Comment(Note):
"""like a note but with a book""" """like a note but with a book"""

View file

@ -5,6 +5,7 @@ from typing import List
from .base_activity import ActivityObject from .base_activity import ActivityObject
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class OrderedCollection(ActivityObject): class OrderedCollection(ActivityObject):
"""structure of an ordered collection activity""" """structure of an ordered collection activity"""
@ -17,6 +18,7 @@ class OrderedCollection(ActivityObject):
type: str = "OrderedCollection" type: str = "OrderedCollection"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class OrderedCollectionPrivate(OrderedCollection): class OrderedCollectionPrivate(OrderedCollection):
"""an ordered collection with privacy settings""" """an ordered collection with privacy settings"""
@ -41,6 +43,7 @@ class BookList(OrderedCollectionPrivate):
type: str = "BookList" type: str = "BookList"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class OrderedCollectionPage(ActivityObject): class OrderedCollectionPage(ActivityObject):
"""structure of an ordered collection activity""" """structure of an ordered collection activity"""

View file

@ -6,6 +6,7 @@ from .base_activity import ActivityObject
from .image import Image from .image import Image
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class PublicKey(ActivityObject): class PublicKey(ActivityObject):
"""public key block""" """public key block"""
@ -15,6 +16,7 @@ class PublicKey(ActivityObject):
type: str = "PublicKey" type: str = "PublicKey"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Person(ActivityObject): class Person(ActivityObject):
"""actor activitypub json""" """actor activitypub json"""

View file

@ -1,3 +1,4 @@
""" ActivityPub-specific json response wrapper """
from django.http import JsonResponse from django.http import JsonResponse
from .base_activity import ActivityEncoder from .base_activity import ActivityEncoder

View file

@ -22,6 +22,7 @@ class Verb(ActivityObject):
self.object.to_model() self.object.to_model()
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Create(Verb): class Create(Verb):
"""Create activity""" """Create activity"""
@ -32,6 +33,7 @@ class Create(Verb):
type: str = "Create" type: str = "Create"
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Delete(Verb): class Delete(Verb):
"""Create activity""" """Create activity"""
@ -57,6 +59,7 @@ class Delete(Verb):
# if we can't find it, we don't need to delete it because we don't have it # if we can't find it, we don't need to delete it because we don't have it
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Update(Verb): class Update(Verb):
"""Update activity""" """Update activity"""
@ -192,6 +195,7 @@ class Like(Verb):
self.to_model() self.to_model()
# pylint: disable=invalid-name
@dataclass(init=False) @dataclass(init=False)
class Announce(Verb): class Announce(Verb):
"""boosting a status""" """boosting a status"""

View file

@ -2,11 +2,10 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import asdict, dataclass from dataclasses import asdict, dataclass
import logging import logging
from urllib3.exceptions import RequestError
from django.db import transaction from django.db import transaction
import requests import requests
from requests.exceptions import SSLError from requests.exceptions import RequestException
from bookwyrm import activitypub, models, settings from bookwyrm import activitypub, models, settings
from .connector_manager import load_more_data, ConnectorException from .connector_manager import load_more_data, ConnectorException
@ -127,8 +126,8 @@ class AbstractConnector(AbstractMinimalConnector):
edition_data = data edition_data = data
try: try:
work_data = self.get_work_from_edition_data(data) work_data = self.get_work_from_edition_data(data)
except (KeyError, ConnectorException) as e: except (KeyError, ConnectorException) as err:
logger.exception(e) logger.exception(err)
work_data = data work_data = data
if not work_data or not edition_data: if not work_data or not edition_data:
@ -237,16 +236,16 @@ def get_data(url, params=None, timeout=10):
}, },
timeout=timeout, timeout=timeout,
) )
except (RequestError, SSLError, ConnectionError) as e: except RequestException as err:
logger.exception(e) logger.exception(err)
raise ConnectorException() raise ConnectorException()
if not resp.ok: if not resp.ok:
raise ConnectorException() raise ConnectorException()
try: try:
data = resp.json() data = resp.json()
except ValueError as e: except ValueError as err:
logger.exception(e) logger.exception(err)
raise ConnectorException() raise ConnectorException()
return data return data
@ -262,8 +261,8 @@ def get_image(url, timeout=10):
}, },
timeout=timeout, timeout=timeout,
) )
except (RequestError, SSLError) as e: except RequestException as err:
logger.exception(e) logger.exception(err)
return None return None
if not resp.ok: if not resp.ok:
return None return None

View file

@ -38,17 +38,17 @@ def search(query, min_confidence=0.1, return_first=False):
# Search on ISBN # Search on ISBN
try: try:
result_set = connector.isbn_search(isbn) result_set = connector.isbn_search(isbn)
except Exception as e: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
logger.exception(e) logger.exception(err)
# if this fails, we can still try regular search # if this fails, we can still try regular search
# if no isbn search results, we fallback to generic search # if no isbn search results, we fallback to generic search
if not result_set: if not result_set:
try: try:
result_set = connector.search(query, min_confidence=min_confidence) result_set = connector.search(query, min_confidence=min_confidence)
except Exception as e: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
# we don't want *any* error to crash the whole search page # we don't want *any* error to crash the whole search page
logger.exception(e) logger.exception(err)
continue continue
if return_first and result_set: if return_first and result_set:

View file

@ -74,7 +74,7 @@ class Connector(AbstractConnector):
**{k: data.get(k) for k in ["uri", "image", "labels", "sitelinks"]}, **{k: data.get(k) for k in ["uri", "image", "labels", "sitelinks"]},
} }
def search(self, query, min_confidence=None): def search(self, query, min_confidence=None): # pylint: disable=arguments-differ
"""overrides default search function with confidence ranking""" """overrides default search function with confidence ranking"""
results = super().search(query) results = super().search(query)
if min_confidence: if min_confidence:

View file

@ -2,7 +2,7 @@
from functools import reduce from functools import reduce
import operator import operator
from django.contrib.postgres.search import SearchRank, SearchVector from django.contrib.postgres.search import SearchRank, SearchQuery
from django.db.models import OuterRef, Subquery, F, Q from django.db.models import OuterRef, Subquery, F, Q
from bookwyrm import models from bookwyrm import models
@ -13,7 +13,7 @@ class Connector(AbstractConnector):
"""instantiate a connector""" """instantiate a connector"""
# pylint: disable=arguments-differ # pylint: disable=arguments-differ
def search(self, query, min_confidence=0.1, raw=False, filters=None): def search(self, query, min_confidence=0, raw=False, filters=None):
"""search your local database""" """search your local database"""
filters = filters or [] filters = filters or []
if not query: if not query:
@ -114,6 +114,7 @@ class Connector(AbstractConnector):
def search_identifiers(query, *filters): def search_identifiers(query, *filters):
"""tries remote_id, isbn; defined as dedupe fields on the model""" """tries remote_id, isbn; defined as dedupe fields on the model"""
# pylint: disable=W0212
or_filters = [ or_filters = [
{f.name: query} {f.name: query}
for f in models.Edition._meta.get_fields() for f in models.Edition._meta.get_fields()
@ -140,16 +141,11 @@ def search_identifiers(query, *filters):
def search_title_author(query, min_confidence, *filters): def search_title_author(query, min_confidence, *filters):
"""searches for title and author""" """searches for title and author"""
vector = ( query = SearchQuery(query, config="simple") | SearchQuery(query, config="english")
SearchVector("title", weight="A")
+ SearchVector("subtitle", weight="B")
+ SearchVector("authors__name", weight="C")
+ SearchVector("series", weight="D")
)
results = ( results = (
models.Edition.objects.annotate(rank=SearchRank(vector, query)) models.Edition.objects.filter(*filters, search_vector=query)
.filter(*filters, rank__gt=min_confidence) .annotate(rank=SearchRank(F("search_vector"), query))
.filter(rank__gt=min_confidence)
.order_by("-rank") .order_by("-rank")
) )

View file

@ -1,10 +1,20 @@
""" customize the info available in context for rendering templates """ """ customize the info available in context for rendering templates """
from bookwyrm import models from bookwyrm import models, settings
def site_settings(request): # pylint: disable=unused-argument def site_settings(request): # pylint: disable=unused-argument
"""include the custom info about the site""" """include the custom info about the site"""
request_protocol = "https://"
if not request.is_secure():
request_protocol = "http://"
return { return {
"site": models.SiteSettings.objects.get(), "site": models.SiteSettings.objects.get(),
"active_announcements": models.Announcement.active_announcements(), "active_announcements": models.Announcement.active_announcements(),
"static_url": settings.STATIC_URL,
"media_url": settings.MEDIA_URL,
"static_path": settings.STATIC_PATH,
"media_path": settings.MEDIA_PATH,
"preview_images_enabled": settings.ENABLE_PREVIEW_IMAGES,
"request_protocol": request_protocol,
} }

View file

@ -22,6 +22,7 @@ class CustomForm(ModelForm):
css_classes["number"] = "input" css_classes["number"] = "input"
css_classes["checkbox"] = "checkbox" css_classes["checkbox"] = "checkbox"
css_classes["textarea"] = "textarea" css_classes["textarea"] = "textarea"
# pylint: disable=super-with-arguments
super(CustomForm, self).__init__(*args, **kwargs) super(CustomForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields(): for visible in self.visible_fields():
if hasattr(visible.field.widget, "input_type"): if hasattr(visible.field.widget, "input_type"):
@ -181,9 +182,8 @@ class EditionForm(CustomForm):
"authors", "authors",
"parent_work", "parent_work",
"shelves", "shelves",
"subjects", # TODO
"subject_places", # TODO
"connector", "connector",
"search_vector",
] ]
@ -195,6 +195,7 @@ class AuthorForm(CustomForm):
"origin_id", "origin_id",
"created_date", "created_date",
"updated_date", "updated_date",
"search_vector",
] ]

View file

@ -67,8 +67,8 @@ def import_data(source, job_id):
for item in job.items.all(): for item in job.items.all():
try: try:
item.resolve() item.resolve()
except Exception as e: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
logger.exception(e) logger.exception(err)
item.fail_reason = "Error loading book" item.fail_reason = "Error loading book"
item.save() item.save()
continue continue

View file

@ -0,0 +1,65 @@
""" Generate preview images """
from django.core.management.base import BaseCommand
from bookwyrm import models, preview_images
# pylint: disable=line-too-long
class Command(BaseCommand):
"""Creates previews for existing objects"""
help = "Generate preview images"
def add_arguments(self, parser):
parser.add_argument(
"--all",
"-a",
action="store_true",
help="Generates images for ALL types: site, users and books. Can use a lot of computing power.",
)
# pylint: disable=no-self-use,unused-argument
def handle(self, *args, **options):
"""generate preview images"""
self.stdout.write(
" | Hello! I will be generating preview images for your instance."
)
if options["all"]:
self.stdout.write(
"🧑‍🎨 ⎨ This might take quite long if your instance has a lot of books and users."
)
self.stdout.write(" | ✧ Thank you for your patience ✧")
else:
self.stdout.write("🧑‍🎨 ⎨ I will only generate the instance preview image.")
self.stdout.write(" | ✧ Be right back! ✧")
# Site
self.stdout.write(" → Site preview image: ", ending="")
preview_images.generate_site_preview_image_task.delay()
self.stdout.write(" OK 🖼")
if options["all"]:
# Users
users = models.User.objects.filter(
local=True,
is_active=True,
)
self.stdout.write(
" → User preview images ({}): ".format(len(users)), ending=""
)
for user in users:
preview_images.generate_user_preview_image_task.delay(user.id)
self.stdout.write(".", ending="")
self.stdout.write(" OK 🖼")
# Books
books = models.Book.objects.select_subclasses().filter()
self.stdout.write(
" → Book preview images ({}): ".format(len(books)), ending=""
)
for book in books:
preview_images.generate_edition_preview_image_task.delay(book.id)
self.stdout.write(".", ending="")
self.stdout.write(" OK 🖼")
self.stdout.write("🧑‍🎨 ⎨ Im all done! ✧ Enjoy ✧")

View file

@ -0,0 +1,32 @@
# Generated by Django 3.2 on 2021-05-26 12:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0075_announcement"),
]
operations = [
migrations.AddField(
model_name="book",
name="preview_image",
field=models.ImageField(
blank=True, null=True, upload_to="previews/covers/"
),
),
migrations.AddField(
model_name="sitesettings",
name="preview_image",
field=models.ImageField(blank=True, null=True, upload_to="previews/logos/"),
),
migrations.AddField(
model_name="user",
name="preview_image",
field=models.ImageField(
blank=True, null=True, upload_to="previews/avatars/"
),
),
]

View file

@ -0,0 +1,126 @@
# Generated by Django 3.2.4 on 2021-06-23 21:55
import django.contrib.postgres.indexes
import django.contrib.postgres.search
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0076_preview_images"),
]
operations = [
migrations.AddField(
model_name="author",
name="search_vector",
field=django.contrib.postgres.search.SearchVectorField(null=True),
),
migrations.AddField(
model_name="book",
name="search_vector",
field=django.contrib.postgres.search.SearchVectorField(null=True),
),
migrations.AddIndex(
model_name="author",
index=django.contrib.postgres.indexes.GinIndex(
fields=["search_vector"], name="bookwyrm_au_search__b050a8_gin"
),
),
migrations.AddIndex(
model_name="book",
index=django.contrib.postgres.indexes.GinIndex(
fields=["search_vector"], name="bookwyrm_bo_search__51beb3_gin"
),
),
migrations.RunSQL(
sql="""
CREATE FUNCTION book_trigger() RETURNS trigger AS $$
begin
new.search_vector :=
coalesce(
NULLIF(setweight(to_tsvector('english', coalesce(new.title, '')), 'A'), ''),
setweight(to_tsvector('simple', coalesce(new.title, '')), 'A')
) ||
setweight(to_tsvector('english', coalesce(new.subtitle, '')), 'B') ||
(SELECT setweight(to_tsvector('simple', coalesce(array_to_string(array_agg(bookwyrm_author.name), ' '), '')), 'C')
FROM bookwyrm_book
LEFT OUTER JOIN bookwyrm_book_authors
ON bookwyrm_book.id = bookwyrm_book_authors.book_id
LEFT OUTER JOIN bookwyrm_author
ON bookwyrm_book_authors.author_id = bookwyrm_author.id
WHERE bookwyrm_book.id = new.id
) ||
setweight(to_tsvector('english', coalesce(new.series, '')), 'D');
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER search_vector_trigger
BEFORE INSERT OR UPDATE OF title, subtitle, series, search_vector
ON bookwyrm_book
FOR EACH ROW EXECUTE FUNCTION book_trigger();
UPDATE bookwyrm_book SET search_vector = NULL;
""",
reverse_sql="""
DROP TRIGGER IF EXISTS search_vector_trigger
ON bookwyrm_book;
DROP FUNCTION IF EXISTS book_trigger;
""",
),
# when an author is edited
migrations.RunSQL(
sql="""
CREATE FUNCTION author_trigger() RETURNS trigger AS $$
begin
WITH book AS (
SELECT bookwyrm_book.id as row_id
FROM bookwyrm_author
LEFT OUTER JOIN bookwyrm_book_authors
ON bookwyrm_book_authors.id = new.id
LEFT OUTER JOIN bookwyrm_book
ON bookwyrm_book.id = bookwyrm_book_authors.book_id
)
UPDATE bookwyrm_book SET search_vector = ''
FROM book
WHERE id = book.row_id;
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER author_search_vector_trigger
AFTER UPDATE OF name
ON bookwyrm_author
FOR EACH ROW EXECUTE FUNCTION author_trigger();
""",
reverse_sql="""
DROP TRIGGER IF EXISTS author_search_vector_trigger
ON bookwyrm_author;
DROP FUNCTION IF EXISTS author_trigger;
""",
),
# when an author is added to or removed from a book
migrations.RunSQL(
sql="""
CREATE FUNCTION book_authors_trigger() RETURNS trigger AS $$
begin
UPDATE bookwyrm_book SET search_vector = ''
WHERE id = coalesce(new.book_id, old.book_id);
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER book_authors_search_vector_trigger
AFTER INSERT OR DELETE
ON bookwyrm_book_authors
FOR EACH ROW EXECUTE FUNCTION book_authors_trigger();
""",
reverse_sql="""
DROP TRIGGER IF EXISTS book_authors_search_vector_trigger
ON bookwyrm_book_authors;
DROP FUNCTION IF EXISTS book_authors_trigger;
""",
),
]

View file

@ -1,4 +1,5 @@
""" database schema for info about authors """ """ database schema for info about authors """
from django.contrib.postgres.indexes import GinIndex
from django.db import models from django.db import models
from bookwyrm import activitypub from bookwyrm import activitypub
@ -37,3 +38,8 @@ class Author(BookDataModel):
return "https://%s/author/%s" % (DOMAIN, self.id) return "https://%s/author/%s" % (DOMAIN, self.id)
activity_serializer = activitypub.Author activity_serializer = activitypub.Author
class Meta:
"""sets up postgres GIN index field"""
indexes = (GinIndex(fields=["search_vector"]),)

View file

@ -1,11 +1,16 @@
""" database schema for books and shelves """ """ database schema for books and shelves """
import re import re
from django.contrib.postgres.search import SearchVectorField
from django.contrib.postgres.indexes import GinIndex
from django.db import models from django.db import models
from django.dispatch import receiver
from model_utils import FieldTracker
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN, DEFAULT_LANGUAGE from bookwyrm.preview_images import generate_edition_preview_image_task
from bookwyrm.settings import DOMAIN, DEFAULT_LANGUAGE, ENABLE_PREVIEW_IMAGES
from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -31,6 +36,7 @@ class BookDataModel(ObjectMixin, BookWyrmModel):
bnf_id = fields.CharField( # Bibliothèque nationale de France bnf_id = fields.CharField( # Bibliothèque nationale de France
max_length=255, blank=True, null=True, deduplication_field=True max_length=255, blank=True, null=True, deduplication_field=True
) )
search_vector = SearchVectorField(null=True)
last_edited_by = fields.ForeignKey( last_edited_by = fields.ForeignKey(
"User", "User",
@ -82,10 +88,14 @@ class Book(BookDataModel):
cover = fields.ImageField( cover = fields.ImageField(
upload_to="covers/", blank=True, null=True, alt_field="alt_text" upload_to="covers/", blank=True, null=True, alt_field="alt_text"
) )
preview_image = models.ImageField(
upload_to="previews/covers/", blank=True, null=True
)
first_published_date = fields.DateTimeField(blank=True, null=True) first_published_date = fields.DateTimeField(blank=True, null=True)
published_date = fields.DateTimeField(blank=True, null=True) published_date = fields.DateTimeField(blank=True, null=True)
objects = InheritanceManager() objects = InheritanceManager()
field_tracker = FieldTracker(fields=["authors", "title", "subtitle", "cover"])
@property @property
def author_text(self): def author_text(self):
@ -135,6 +145,11 @@ class Book(BookDataModel):
self.title, self.title,
) )
class Meta:
"""sets up postgres GIN index field"""
indexes = (GinIndex(fields=["search_vector"]),)
class Work(OrderedCollectionPageMixin, Book): class Work(OrderedCollectionPageMixin, Book):
"""a work (an abstract concept of a book that manifests in an edition)""" """a work (an abstract concept of a book that manifests in an edition)"""
@ -293,3 +308,17 @@ def isbn_13_to_10(isbn_13):
if checkdigit == 10: if checkdigit == 10:
checkdigit = "X" checkdigit = "X"
return converted + str(checkdigit) return converted + str(checkdigit)
# pylint: disable=unused-argument
@receiver(models.signals.post_save, sender=Edition)
def preview_image(instance, *args, **kwargs):
"""create preview image on book create"""
if not ENABLE_PREVIEW_IMAGES:
return
changed_fields = {}
if instance.field_tracker:
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
generate_edition_preview_image_task.delay(instance.id)

View file

@ -202,6 +202,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
*args, max_length=255, choices=PrivacyLevels.choices, default="public" *args, max_length=255, choices=PrivacyLevels.choices, default="public"
) )
# pylint: disable=invalid-name
def set_field_from_activity(self, instance, data): def set_field_from_activity(self, instance, data):
to = data.to to = data.to
cc = data.cc cc = data.cc
@ -220,6 +221,7 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
if hasattr(instance, "mention_users"): if hasattr(instance, "mention_users"):
mentions = [u.remote_id for u in instance.mention_users.all()] mentions = [u.remote_id for u in instance.mention_users.all()]
# this is a link to the followers list # this is a link to the followers list
# pylint: disable=protected-access
followers = instance.user.__class__._meta.get_field( followers = instance.user.__class__._meta.get_field(
"followers" "followers"
).field_to_activity(instance.user.followers) ).field_to_activity(instance.user.followers)
@ -406,7 +408,8 @@ class ImageField(ActivitypubFieldMixin, models.ImageField):
return None return None
image_content = ContentFile(response.content) image_content = ContentFile(response.content)
image_name = str(uuid4()) + "." + imghdr.what(None, image_content.read()) extension = imghdr.what(None, image_content.read()) or ""
image_name = "{:s}.{:s}".format(str(uuid4()), extension)
return [image_name, image_content] return [image_name, image_content]
def formfield(self, **kwargs): def formfield(self, **kwargs):

View file

@ -93,7 +93,8 @@ class ListItem(CollectionItemMixin, BookWyrmModel):
) )
class Meta: class Meta:
# A book may only be placed into a list once, and each order in the list may be used only """A book may only be placed into a list once,
# once and each order in the list may be used only once"""
unique_together = (("book", "book_list"), ("order", "book_list")) unique_together = (("book", "book_list"), ("order", "book_list"))
ordering = ("-created_date",) ordering = ("-created_date",)

View file

@ -99,7 +99,7 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship):
status = "follow_request" status = "follow_request"
activity_serializer = activitypub.Follow activity_serializer = activitypub.Follow
def save(self, *args, broadcast=True, **kwargs): def save(self, *args, broadcast=True, **kwargs): # pylint: disable=arguments-differ
"""make sure the follow or block relationship doesn't already exist""" """make sure the follow or block relationship doesn't already exist"""
# if there's a request for a follow that already exists, accept it # if there's a request for a follow that already exists, accept it
# without changing the local database state # without changing the local database state

View file

@ -4,9 +4,12 @@ import datetime
from Crypto import Random from Crypto import Random
from django.db import models, IntegrityError from django.db import models, IntegrityError
from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from model_utils import FieldTracker
from bookwyrm.settings import DOMAIN from bookwyrm.preview_images import generate_site_preview_image_task
from bookwyrm.settings import DOMAIN, ENABLE_PREVIEW_IMAGES
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from .user import User from .user import User
@ -35,6 +38,9 @@ class SiteSettings(models.Model):
logo = models.ImageField(upload_to="logos/", null=True, blank=True) logo = models.ImageField(upload_to="logos/", null=True, blank=True)
logo_small = models.ImageField(upload_to="logos/", null=True, blank=True) logo_small = models.ImageField(upload_to="logos/", null=True, blank=True)
favicon = models.ImageField(upload_to="logos/", null=True, blank=True) favicon = models.ImageField(upload_to="logos/", null=True, blank=True)
preview_image = models.ImageField(
upload_to="previews/logos/", null=True, blank=True
)
# footer # footer
support_link = models.CharField(max_length=255, null=True, blank=True) support_link = models.CharField(max_length=255, null=True, blank=True)
@ -42,6 +48,8 @@ class SiteSettings(models.Model):
admin_email = models.EmailField(max_length=255, null=True, blank=True) admin_email = models.EmailField(max_length=255, null=True, blank=True)
footer_item = models.TextField(null=True, blank=True) footer_item = models.TextField(null=True, blank=True)
field_tracker = FieldTracker(fields=["name", "instance_tagline", "logo"])
@classmethod @classmethod
def get(cls): def get(cls):
"""gets the site settings db entry or defaults""" """gets the site settings db entry or defaults"""
@ -119,3 +127,15 @@ class PasswordReset(models.Model):
def link(self): def link(self):
"""formats the invite link""" """formats the invite link"""
return "https://{}/password-reset/{}".format(DOMAIN, self.code) return "https://{}/password-reset/{}".format(DOMAIN, self.code)
# pylint: disable=unused-argument
@receiver(models.signals.post_save, sender=SiteSettings)
def preview_image(instance, *args, **kwargs):
"""Update image preview for the default site image"""
if not ENABLE_PREVIEW_IMAGES:
return
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
generate_site_preview_image_task.delay()

View file

@ -5,11 +5,15 @@ import re
from django.apps import apps from django.apps import apps
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.dispatch import receiver
from django.template.loader import get_template from django.template.loader import get_template
from django.utils import timezone from django.utils import timezone
from model_utils import FieldTracker
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.preview_images import generate_edition_preview_image_task
from bookwyrm.settings import ENABLE_PREVIEW_IMAGES
from .activitypub_mixin import ActivitypubMixin, ActivityMixin from .activitypub_mixin import ActivitypubMixin, ActivityMixin
from .activitypub_mixin import OrderedCollectionPageMixin from .activitypub_mixin import OrderedCollectionPageMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
@ -304,6 +308,8 @@ class Review(Status):
max_digits=3, max_digits=3,
) )
field_tracker = FieldTracker(fields=["rating"])
@property @property
def pure_name(self): def pure_name(self):
"""clarify review names for mastodon serialization""" """clarify review names for mastodon serialization"""
@ -398,3 +404,17 @@ class Boost(ActivityMixin, Status):
# This constraint can't work as it would cross tables. # This constraint can't work as it would cross tables.
# class Meta: # class Meta:
# unique_together = ('user', 'boosted_status') # unique_together = ('user', 'boosted_status')
# pylint: disable=unused-argument
@receiver(models.signals.post_save)
def preview_image(instance, sender, *args, **kwargs):
"""Updates book previews if the rating has changed"""
if not ENABLE_PREVIEW_IMAGES or sender not in (Review, ReviewRating):
return
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
edition = instance.book
generate_edition_preview_image_task.delay(edition.id)

View file

@ -6,15 +6,18 @@ from django.apps import apps
from django.contrib.auth.models import AbstractUser, Group from django.contrib.auth.models import AbstractUser, Group
from django.contrib.postgres.fields import CICharField from django.contrib.postgres.fields import CICharField
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.dispatch import receiver
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from model_utils import FieldTracker
import pytz import pytz
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.connectors import get_data, ConnectorException from bookwyrm.connectors import get_data, ConnectorException
from bookwyrm.models.shelf import Shelf from bookwyrm.models.shelf import Shelf
from bookwyrm.models.status import Status, Review from bookwyrm.models.status import Status, Review
from bookwyrm.settings import DOMAIN from bookwyrm.preview_images import generate_user_preview_image_task
from bookwyrm.settings import DOMAIN, ENABLE_PREVIEW_IMAGES
from bookwyrm.signatures import create_key_pair from bookwyrm.signatures import create_key_pair
from bookwyrm.tasks import app from bookwyrm.tasks import app
from bookwyrm.utils import regex from bookwyrm.utils import regex
@ -70,6 +73,9 @@ class User(OrderedCollectionPageMixin, AbstractUser):
activitypub_field="icon", activitypub_field="icon",
alt_field="alt_text", alt_field="alt_text",
) )
preview_image = models.ImageField(
upload_to="previews/avatars/", blank=True, null=True
)
followers = fields.ManyToManyField( followers = fields.ManyToManyField(
"self", "self",
link_only=True, link_only=True,
@ -117,6 +123,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
name_field = "username" name_field = "username"
property_fields = [("following_link", "following")] property_fields = [("following_link", "following")]
field_tracker = FieldTracker(fields=["name", "avatar"])
@property @property
def following_link(self): def following_link(self):
@ -232,7 +239,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""populate fields for new local users""" """populate fields for new local users"""
created = not bool(self.id) created = not bool(self.id)
if not self.local and not re.match(regex.full_username, self.username): if not self.local and not re.match(regex.FULL_USERNAME, self.username):
# 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)
@ -356,7 +363,7 @@ class AnnualGoal(BookWyrmModel):
def get_remote_id(self): def get_remote_id(self):
"""put the year in the path""" """put the year in the path"""
return "%s/goal/%d" % (self.user.remote_id, self.year) return "{:s}/goal/{:d}".format(self.user.remote_id, self.year)
@property @property
def books(self): def books(self):
@ -443,3 +450,15 @@ def get_remote_reviews(outbox):
if not activity["type"] == "Review": if not activity["type"] == "Review":
continue continue
activitypub.Review(**activity).to_model() activitypub.Review(**activity).to_model()
# pylint: disable=unused-argument
@receiver(models.signals.post_save, sender=User)
def preview_image(instance, *args, **kwargs):
"""create preview images when user is updated"""
if not ENABLE_PREVIEW_IMAGES:
return
changed_fields = instance.field_tracker.changed()
if len(changed_fields) > 0:
generate_user_preview_image_task.delay(instance.id)

424
bookwyrm/preview_images.py Normal file
View file

@ -0,0 +1,424 @@
""" Generate social media preview images for twitter/mastodon/etc """
import math
import os
import textwrap
from io import BytesIO
from uuid import uuid4
import colorsys
from colorthief import ColorThief
from PIL import Image, ImageDraw, ImageFont, ImageOps, ImageColor
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db.models import Avg
from bookwyrm import models, settings
from bookwyrm.tasks import app
IMG_WIDTH = settings.PREVIEW_IMG_WIDTH
IMG_HEIGHT = settings.PREVIEW_IMG_HEIGHT
BG_COLOR = settings.PREVIEW_BG_COLOR
TEXT_COLOR = settings.PREVIEW_TEXT_COLOR
DEFAULT_COVER_COLOR = settings.PREVIEW_DEFAULT_COVER_COLOR
TRANSPARENT_COLOR = (0, 0, 0, 0)
margin = math.floor(IMG_HEIGHT / 10)
gutter = math.floor(margin / 2)
inner_img_height = math.floor(IMG_HEIGHT * 0.8)
inner_img_width = math.floor(inner_img_height * 0.7)
font_dir = os.path.join(settings.STATIC_ROOT, "fonts/public_sans")
def get_font(font_name, size=28):
"""Loads custom font"""
if font_name == "light":
font_path = os.path.join(font_dir, "PublicSans-Light.ttf")
if font_name == "regular":
font_path = os.path.join(font_dir, "PublicSans-Regular.ttf")
elif font_name == "bold":
font_path = os.path.join(font_dir, "PublicSans-Bold.ttf")
try:
font = ImageFont.truetype(font_path, size)
except OSError:
font = ImageFont.load_default()
return font
def generate_texts_layer(texts, content_width):
"""Adds text for images"""
font_text_zero = get_font("bold", size=20)
font_text_one = get_font("bold", size=48)
font_text_two = get_font("bold", size=40)
font_text_three = get_font("regular", size=40)
text_layer = Image.new("RGBA", (content_width, IMG_HEIGHT), color=TRANSPARENT_COLOR)
text_layer_draw = ImageDraw.Draw(text_layer)
text_y = 0
if "text_zero" in texts and texts["text_zero"]:
# Text one (Book title)
text_zero = textwrap.fill(texts["text_zero"], width=72)
text_layer_draw.multiline_text(
(0, text_y), text_zero, font=font_text_zero, fill=TEXT_COLOR
)
try:
text_y = text_y + font_text_zero.getsize_multiline(text_zero)[1] + 16
except (AttributeError, IndexError):
text_y = text_y + 26
if "text_one" in texts and texts["text_one"]:
# Text one (Book title)
text_one = textwrap.fill(texts["text_one"], width=28)
text_layer_draw.multiline_text(
(0, text_y), text_one, font=font_text_one, fill=TEXT_COLOR
)
try:
text_y = text_y + font_text_one.getsize_multiline(text_one)[1] + 16
except (AttributeError, IndexError):
text_y = text_y + 26
if "text_two" in texts and texts["text_two"]:
# Text one (Book subtitle)
text_two = textwrap.fill(texts["text_two"], width=36)
text_layer_draw.multiline_text(
(0, text_y), text_two, font=font_text_two, fill=TEXT_COLOR
)
try:
text_y = text_y + font_text_one.getsize_multiline(text_two)[1] + 16
except (AttributeError, IndexError):
text_y = text_y + 26
if "text_three" in texts and texts["text_three"]:
# Text three (Book authors)
text_three = textwrap.fill(texts["text_three"], width=36)
text_layer_draw.multiline_text(
(0, text_y), text_three, font=font_text_three, fill=TEXT_COLOR
)
text_layer_box = text_layer.getbbox()
return text_layer.crop(text_layer_box)
def generate_instance_layer(content_width):
"""Places components for instance preview"""
font_instance = get_font("light", size=28)
site = models.SiteSettings.objects.get()
if site.logo_small:
logo_img = Image.open(site.logo_small)
else:
try:
static_path = os.path.join(settings.STATIC_ROOT, "images/logo-small.png")
logo_img = Image.open(static_path)
except FileNotFoundError:
logo_img = None
instance_layer = Image.new("RGBA", (content_width, 62), color=TRANSPARENT_COLOR)
instance_text_x = 0
if logo_img:
logo_img.thumbnail((50, 50), Image.ANTIALIAS)
instance_layer.paste(logo_img, (0, 0))
instance_text_x = instance_text_x + 60
instance_layer_draw = ImageDraw.Draw(instance_layer)
instance_layer_draw.text(
(instance_text_x, 10), site.name, font=font_instance, fill=TEXT_COLOR
)
line_width = 50 + 10 + font_instance.getsize(site.name)[0]
line_layer = Image.new(
"RGBA", (line_width, 2), color=(*(ImageColor.getrgb(TEXT_COLOR)), 50)
)
instance_layer.alpha_composite(line_layer, (0, 60))
return instance_layer
def generate_rating_layer(rating, content_width):
"""Places components for rating preview"""
try:
icon_star_full = Image.open(
os.path.join(settings.STATIC_ROOT, "images/icons/star-full.png")
)
icon_star_empty = Image.open(
os.path.join(settings.STATIC_ROOT, "images/icons/star-empty.png")
)
icon_star_half = Image.open(
os.path.join(settings.STATIC_ROOT, "images/icons/star-half.png")
)
except FileNotFoundError:
return None
icon_size = 64
icon_margin = 10
rating_layer_base = Image.new(
"RGBA", (content_width, icon_size), color=TRANSPARENT_COLOR
)
rating_layer_color = Image.new("RGBA", (content_width, icon_size), color=TEXT_COLOR)
rating_layer_mask = Image.new(
"RGBA", (content_width, icon_size), color=TRANSPARENT_COLOR
)
position_x = 0
for _ in range(math.floor(rating)):
rating_layer_mask.alpha_composite(icon_star_full, (position_x, 0))
position_x = position_x + icon_size + icon_margin
if math.floor(rating) != math.ceil(rating):
rating_layer_mask.alpha_composite(icon_star_half, (position_x, 0))
position_x = position_x + icon_size + icon_margin
for _ in range(5 - math.ceil(rating)):
rating_layer_mask.alpha_composite(icon_star_empty, (position_x, 0))
position_x = position_x + icon_size + icon_margin
rating_layer_mask = rating_layer_mask.getchannel("A")
rating_layer_mask = ImageOps.invert(rating_layer_mask)
rating_layer_composite = Image.composite(
rating_layer_base, rating_layer_color, rating_layer_mask
)
return rating_layer_composite
def generate_default_inner_img():
"""Adds cover image"""
font_cover = get_font("light", size=28)
default_cover = Image.new(
"RGB", (inner_img_width, inner_img_height), color=DEFAULT_COVER_COLOR
)
default_cover_draw = ImageDraw.Draw(default_cover)
text = "no image :("
text_dimensions = font_cover.getsize(text)
text_coords = (
math.floor((inner_img_width - text_dimensions[0]) / 2),
math.floor((inner_img_height - text_dimensions[1]) / 2),
)
default_cover_draw.text(text_coords, text, font=font_cover, fill="white")
return default_cover
# pylint: disable=too-many-locals
def generate_preview_image(
texts=None, picture=None, rating=None, show_instance_layer=True
):
"""Puts everything together"""
texts = texts or {}
# Cover
try:
inner_img_layer = Image.open(picture)
inner_img_layer.thumbnail((inner_img_width, inner_img_height), Image.ANTIALIAS)
color_thief = ColorThief(picture)
dominant_color = color_thief.get_color(quality=1)
except: # pylint: disable=bare-except
inner_img_layer = generate_default_inner_img()
dominant_color = ImageColor.getrgb(DEFAULT_COVER_COLOR)
# Color
if BG_COLOR in ["use_dominant_color_light", "use_dominant_color_dark"]:
image_bg_color = "rgb(%s, %s, %s)" % dominant_color
# Adjust color
image_bg_color_rgb = [x / 255.0 for x in ImageColor.getrgb(image_bg_color)]
image_bg_color_hls = colorsys.rgb_to_hls(*image_bg_color_rgb)
if BG_COLOR == "use_dominant_color_light":
lightness = max(0.9, image_bg_color_hls[1])
else:
lightness = min(0.15, image_bg_color_hls[1])
image_bg_color_hls = (
image_bg_color_hls[0],
lightness,
image_bg_color_hls[2],
)
image_bg_color = tuple(
math.ceil(x * 255) for x in colorsys.hls_to_rgb(*image_bg_color_hls)
)
else:
image_bg_color = BG_COLOR
# Background (using the color)
img = Image.new("RGBA", (IMG_WIDTH, IMG_HEIGHT), color=image_bg_color)
# Contents
inner_img_x = margin + inner_img_width - inner_img_layer.width
inner_img_y = math.floor((IMG_HEIGHT - inner_img_layer.height) / 2)
content_x = margin + inner_img_width + gutter
content_width = IMG_WIDTH - content_x - margin
contents_layer = Image.new(
"RGBA", (content_width, IMG_HEIGHT), color=TRANSPARENT_COLOR
)
contents_composite_y = 0
if show_instance_layer:
instance_layer = generate_instance_layer(content_width)
contents_layer.alpha_composite(instance_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + instance_layer.height + gutter
texts_layer = generate_texts_layer(texts, content_width)
contents_layer.alpha_composite(texts_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + texts_layer.height + gutter
if rating:
# Add some more margin
contents_composite_y = contents_composite_y + gutter
rating_layer = generate_rating_layer(rating, content_width)
if rating_layer:
contents_layer.alpha_composite(rating_layer, (0, contents_composite_y))
contents_composite_y = contents_composite_y + rating_layer.height + gutter
contents_layer_box = contents_layer.getbbox()
contents_layer_height = contents_layer_box[3] - contents_layer_box[1]
contents_y = math.floor((IMG_HEIGHT - contents_layer_height) / 2)
if show_instance_layer:
# Remove Instance Layer from centering calculations
contents_y = contents_y - math.floor((instance_layer.height + gutter) / 2)
contents_y = max(contents_y, margin)
# Composite layers
img.paste(
inner_img_layer, (inner_img_x, inner_img_y), inner_img_layer.convert("RGBA")
)
img.alpha_composite(contents_layer, (content_x, contents_y))
return img.convert("RGB")
def save_and_cleanup(image, instance=None):
"""Save and close the file"""
if not isinstance(instance, (models.Book, models.User, models.SiteSettings)):
return False
file_name = "%s-%s.jpg" % (str(instance.id), str(uuid4()))
image_buffer = BytesIO()
try:
try:
old_path = instance.preview_image.path
except ValueError:
old_path = ""
# Save
image.save(image_buffer, format="jpeg", quality=75)
instance.preview_image = InMemoryUploadedFile(
ContentFile(image_buffer.getvalue()),
"preview_image",
file_name,
"image/jpg",
image_buffer.tell(),
None,
)
save_without_broadcast = isinstance(instance, (models.Book, models.User))
if save_without_broadcast:
instance.save(broadcast=False)
else:
instance.save()
# Clean up old file after saving
if os.path.exists(old_path):
os.remove(old_path)
finally:
image_buffer.close()
return True
# pylint: disable=invalid-name
@app.task
def generate_site_preview_image_task():
"""generate preview_image for the website"""
if not settings.ENABLE_PREVIEW_IMAGES:
return
site = models.SiteSettings.objects.get()
if site.logo:
logo = site.logo
else:
logo = os.path.join(settings.STATIC_ROOT, "images/logo.png")
texts = {
"text_zero": settings.DOMAIN,
"text_one": site.name,
"text_three": site.instance_tagline,
}
image = generate_preview_image(texts=texts, picture=logo, show_instance_layer=False)
save_and_cleanup(image, instance=site)
# pylint: disable=invalid-name
@app.task
def generate_edition_preview_image_task(book_id):
"""generate preview_image for a book"""
if not settings.ENABLE_PREVIEW_IMAGES:
return
book = models.Book.objects.select_subclasses().get(id=book_id)
rating = models.Review.objects.filter(
privacy="public",
deleted=False,
book__in=[book_id],
).aggregate(Avg("rating"))["rating__avg"]
texts = {
"text_one": book.title,
"text_two": book.subtitle,
"text_three": book.author_text,
}
image = generate_preview_image(texts=texts, picture=book.cover, rating=rating)
save_and_cleanup(image, instance=book)
@app.task
def generate_user_preview_image_task(user_id):
"""generate preview_image for a book"""
if not settings.ENABLE_PREVIEW_IMAGES:
return
user = models.User.objects.get(id=user_id)
texts = {
"text_one": user.display_name,
"text_three": "@{}@{}".format(user.localname, settings.DOMAIN),
}
if user.avatar:
avatar = user.avatar
else:
avatar = os.path.join(settings.STATIC_ROOT, "images/default_avi.jpg")
image = generate_preview_image(texts=texts, picture=avatar)
save_and_cleanup(image, instance=user)

View file

@ -42,6 +42,14 @@ LOCALE_PATHS = [
DEFAULT_AUTO_FIELD = "django.db.models.AutoField" DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
# Preview image
ENABLE_PREVIEW_IMAGES = env.bool("ENABLE_PREVIEW_IMAGES", False)
PREVIEW_BG_COLOR = env.str("PREVIEW_BG_COLOR", "use_dominant_color_light")
PREVIEW_TEXT_COLOR = env.str("PREVIEW_TEXT_COLOR", "#363636")
PREVIEW_IMG_WIDTH = env.int("PREVIEW_IMG_WIDTH", 1200)
PREVIEW_IMG_HEIGHT = env.int("PREVIEW_IMG_HEIGHT", 630)
PREVIEW_DEFAULT_COVER_COLOR = env.str("PREVIEW_DEFAULT_COVER_COLOR", "#002549")
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
@ -175,8 +183,10 @@ USE_TZ = True
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__)) PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_URL = "/static/" STATIC_URL = "/static/"
STATIC_PATH = "%s/%s" % (DOMAIN, env("STATIC_ROOT", "static"))
STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static")) STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static"))
MEDIA_URL = "/images/" MEDIA_URL = "/images/"
MEDIA_PATH = "%s/%s" % (DOMAIN, env("MEDIA_ROOT", "images"))
MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images")) MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images"))
USER_AGENT = "%s (BookWyrm/%s; +https://%s/)" % ( USER_AGENT = "%s (BookWyrm/%s; +https://%s/)" % (

View file

@ -73,6 +73,7 @@ class Signature:
self.headers = headers self.headers = headers
self.signature = signature self.signature = signature
# pylint: disable=invalid-name
@classmethod @classmethod
def parse(cls, request): def parse(cls, request):
"""extract and parse a signature from an http request""" """extract and parse a signature from an http request"""

View file

@ -0,0 +1,94 @@
Copyright (c) 2015, Pablo Impallari, Rodrigo Fuenzalida
(Modified by Dan O. Williams and USWDS) (https://github.com/uswds/public-sans)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,8 +1,12 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}{% load bookwyrm_tags %}{% load humanize %}{% load utilities %} {% load i18n %}{% load bookwyrm_tags %}{% load humanize %}{% load utilities %}{% load layout %}
{% block title %}{{ book|book_title }}{% endblock %} {% block title %}{{ book|book_title }}{% endblock %}
{% block opengraph_images %}
{% include 'snippets/opengraph_images.html' with image=book.preview_image %}
{% endblock %}
{% block content %} {% block content %}
{% with user_authenticated=request.user.is_authenticated can_edit_book=perms.bookwyrm.edit_book %} {% with user_authenticated=request.user.is_authenticated can_edit_book=perms.bookwyrm.edit_book %}
<div class="block" itemscope itemtype="https://schema.org/Book"> <div class="block" itemscope itemtype="https://schema.org/Book">

View file

@ -8,16 +8,21 @@
<link rel="stylesheet" href="/static/css/vendor/icons.css"> <link rel="stylesheet" href="/static/css/vendor/icons.css">
<link rel="stylesheet" href="/static/css/bookwyrm.css"> <link rel="stylesheet" href="/static/css/bookwyrm.css">
<link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}/images/{{ site.favicon }}{% else %}/static/images/favicon.ico{% endif %}"> <link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}{{ media_url }}{{ site.favicon }}{% else %}/static/images/favicon.ico{% endif %}">
{% if preview_images_enabled is True %}
<meta name="twitter:card" content="summary_large_image">
{% else %}
<meta name="twitter:card" content="summary"> <meta name="twitter:card" content="summary">
{% endif %}
<meta name="twitter:title" content="{% if title %}{{ title }} | {% endif %}{{ site.name }}"> <meta name="twitter:title" content="{% if title %}{{ title }} | {% endif %}{{ site.name }}">
<meta name="og:title" content="{% if title %}{{ title }} | {% endif %}{{ site.name }}"> <meta name="og:title" content="{% if title %}{{ title }} | {% endif %}{{ site.name }}">
<meta name="twitter:description" content="{{ site.instance_tagline }}"> <meta name="twitter:description" content="{{ site.instance_tagline }}">
<meta name="og:description" content="{{ site.instance_tagline }}"> <meta name="og:description" content="{{ site.instance_tagline }}">
<meta name="twitter:image" content="{% if site.logo %}/images/{{ site.logo }}{% else %}/static/images/logo.png{% endif %}"> {% block opengraph_images %}
<meta name="og:image" content="{% if site.logo %}/images/{{ site.logo }}{% else %}/static/images/logo.png{% endif %}"> {% include 'snippets/opengraph_images.html' %}
{% endblock %}
<meta name="twitter:image:alt" content="BookWyrm Logo"> <meta name="twitter:image:alt" content="BookWyrm Logo">
</head> </head>
<body> <body>
@ -25,7 +30,7 @@
<div class="container"> <div class="container">
<div class="navbar-brand"> <div class="navbar-brand">
<a class="navbar-item" href="/"> <a class="navbar-item" href="/">
<img class="image logo" src="{% if site.logo_small %}/images/{{ site.logo_small }}{% else %}/static/images/logo-small.png{% endif %}" alt="Home page"> <img class="image logo" src="{% if site.logo_small %}{{ media_url }}{{ site.logo_small }}{% else %}/static/images/logo-small.png{% endif %}" alt="Home page">
</a> </a>
<form class="navbar-item column" action="/search/"> <form class="navbar-item column" action="/search/">
<div class="field has-addons"> <div class="field has-addons">

View file

@ -1,6 +1,6 @@
<div class="columns"> <div class="columns">
<div class="column is-narrow is-hidden-mobile"> <div class="column is-narrow is-hidden-mobile">
<figure class="block"> <figure class="block is-w-xl">
<img src="{% if site.logo %}/images/{{ site.logo }}{% else %}/static/images/logo.png{% endif %}" alt="BookWyrm logo"> <img src="{% if site.logo %}/images/{{ site.logo }}{% else %}/static/images/logo.png{% endif %}" alt="BookWyrm logo">
</figure> </figure>
</div> </div>

View file

@ -0,0 +1,12 @@
{% if preview_images_enabled is True %}
{% if image %}
<meta name="twitter:image" content="{{ request.scheme }}://{{ media_path }}{{ image }}">
<meta name="og:image" content="{{ request.scheme }}://{{ media_path }}{{ image }}">
{% else %}
<meta name="twitter:image" content="{{ request.scheme }}://{{ media_path }}{{ site.preview_image }}">
<meta name="og:image" content="{{ request.scheme }}://{{ media_path }}{{ site.preview_image }}">
{% endif %}
{% else %}
<meta name="twitter:image" content="{{ request.scheme }}://{% if site.logo %}{{ media_path }}{{ site.logo }}{% else %}{{ static_path }}/images/logo.png{% endif %}">
<meta name="og:image" content="{{ request.scheme }}://{% if site.logo %}{{ media_path }}{{ site.logo }}{% else %}{{ static_path }}/images/logo.png{% endif %}">
{% endif %}

View file

@ -3,9 +3,14 @@
{% load humanize %} {% load humanize %}
{% load utilities %} {% load utilities %}
{% load markdown %} {% load markdown %}
{% load layout %}
{% block title %}{{ user.display_name }}{% endblock %} {% block title %}{{ user.display_name }}{% endblock %}
{% block opengraph_images %}
{% include 'snippets/opengraph_images.html' with image=user.preview_image %}
{% endblock %}
{% block content %} {% block content %}
<header class="block"> <header class="block">
{% block header %} {% block header %}

View file

@ -1,7 +1,6 @@
""" template filters used for creating the layout""" """ template filters used for creating the layout"""
from django import template, utils from django import template, utils
register = template.Library() register = template.Library()

View file

@ -1,15 +1,17 @@
import datetime import datetime
from unittest.mock import patch
from django.test import TestCase from django.test import TestCase
from bookwyrm import models from bookwyrm import models
class Author(TestCase): class Author(TestCase):
def setUp(self): def setUp(self):
self.book = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", title="Example Edition",
) remote_id="https://example.com/book/1",
)
self.author = models.Author.objects.create( self.author = models.Author.objects.create(
name="Author fullname", name="Author fullname",
aliases=["One", "Two"], aliases=["One", "Two"],

View file

@ -25,24 +25,25 @@ class BaseActivity(TestCase):
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"""
self.user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" self.user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
self.user.remote_id = "http://example.com/a/b" )
self.user.save(broadcast=False) self.user.remote_id = "http://example.com/a/b"
self.user.save(broadcast=False)
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())
# don't try to load the user icon # don't try to load the user icon
del self.userdata["icon"] del self.userdata["icon"]
image_file = pathlib.Path(__file__).parent.joinpath( image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg" "../../static/images/default_avi.jpg"
) )
image = Image.open(image_file) image = Image.open(image_file)
output = BytesIO() output = BytesIO()
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"""
@ -97,10 +98,11 @@ class BaseActivity(TestCase):
status=200, status=200,
) )
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
result = resolve_remote_id( with patch("bookwyrm.models.user.set_remote_server.delay"):
"https://example.com/user/mouse", model=models.User result = resolve_remote_id(
) "https://example.com/user/mouse", model=models.User
)
self.assertIsInstance(result, models.User) self.assertIsInstance(result, models.User)
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!!")
@ -139,8 +141,9 @@ class BaseActivity(TestCase):
self.user.avatar.file # pylint: disable=pointless-statement self.user.avatar.file # pylint: disable=pointless-statement
# this would trigger a broadcast because it's a local user # this would trigger a broadcast because it's a local user
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
activity.to_model(model=models.User, instance=self.user) with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
activity.to_model(model=models.User, instance=self.user)
self.assertIsNotNone(self.user.avatar.file) self.assertIsNotNone(self.user.avatar.file)
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")
@ -152,9 +155,10 @@ class BaseActivity(TestCase):
content="test status", content="test status",
user=self.user, user=self.user,
) )
book = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test Edition", remote_id="http://book.com/book" book = models.Edition.objects.create(
) title="Test Edition", remote_id="http://book.com/book"
)
update_data = activitypub.Note( update_data = activitypub.Note(
id=status.remote_id, id=status.remote_id,
content=status.content, content=status.content,

View file

@ -20,8 +20,9 @@ class Person(TestCase):
def test_user_to_model(self): def test_user_to_model(self):
activity = activitypub.Person(**self.user_data) activity = activitypub.Person(**self.user_data)
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
user = activity.to_model(model=models.User) with patch("bookwyrm.models.user.set_remote_server.delay"):
user = activity.to_model(model=models.User)
self.assertEqual(user.username, "mouse@example.com") self.assertEqual(user.username, "mouse@example.com")
self.assertEqual(user.remote_id, "https://example.com/user/mouse") self.assertEqual(user.remote_id, "https://example.com/user/mouse")
self.assertFalse(user.local) self.assertFalse(user.local)

View file

@ -12,20 +12,22 @@ class Quotation(TestCase):
def setUp(self): def setUp(self):
"""model objects we'll need""" """model objects we'll need"""
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
self.user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"mouse", self.user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse",
"mouseword", "mouse@mouse.mouse",
local=False, "mouseword",
inbox="https://example.com/user/mouse/inbox", local=False,
outbox="https://example.com/user/mouse/outbox", inbox="https://example.com/user/mouse/inbox",
remote_id="https://example.com/user/mouse", outbox="https://example.com/user/mouse/outbox",
remote_id="https://example.com/user/mouse",
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create(
title="Example Edition",
remote_id="https://example.com/book/1",
) )
self.book = models.Edition.objects.create(
title="Example Edition",
remote_id="https://example.com/book/1",
)
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_quotation.json") datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_quotation.json")
self.status_data = json.loads(datafile.read_bytes()) self.status_data = json.loads(datafile.read_bytes())

View file

@ -74,11 +74,12 @@ class AbstractConnector(TestCase):
Mapping("openlibraryKey"), Mapping("openlibraryKey"),
] ]
self.book = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test Book", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1234", title="Test Book",
openlibrary_key="OL1234M", remote_id="https://example.com/book/1234",
) openlibrary_key="OL1234M",
)
def test_abstract_connector_init(self): def test_abstract_connector_init(self):
"""barebones connector for search with defaults""" """barebones connector for search with defaults"""
@ -110,8 +111,11 @@ class AbstractConnector(TestCase):
responses.add( responses.add(
responses.GET, "https://example.com/book/abcd", json=self.edition_data responses.GET, "https://example.com/book/abcd", json=self.edition_data
) )
with patch("bookwyrm.connectors.abstract_connector.load_more_data.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
result = self.connector.get_or_create_book("https://example.com/book/abcd") with patch("bookwyrm.connectors.abstract_connector.load_more_data.delay"):
result = self.connector.get_or_create_book(
"https://example.com/book/abcd"
)
self.assertEqual(result, self.book) self.assertEqual(result, self.book)
self.assertEqual(models.Edition.objects.count(), 1) self.assertEqual(models.Edition.objects.count(), 1)
self.assertEqual(models.Edition.objects.count(), 1) self.assertEqual(models.Edition.objects.count(), 1)

View file

@ -1,4 +1,5 @@
""" testing book data connectors """ """ testing book data connectors """
from unittest.mock import patch
import json import json
import pathlib import pathlib
from django.test import TestCase from django.test import TestCase
@ -25,8 +26,9 @@ class BookWyrmConnector(TestCase):
def test_get_or_create_book_existing(self): def test_get_or_create_book_existing(self):
"""load book activity""" """load book activity"""
work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
book = models.Edition.objects.create(title="Test Edition", parent_work=work) work = models.Work.objects.create(title="Test Work")
book = models.Edition.objects.create(title="Test Edition", parent_work=work)
result = self.connector.get_or_create_book(book.remote_id) result = self.connector.get_or_create_book(book.remote_id)
self.assertEqual(book, result) self.assertEqual(book, result)

View file

@ -1,4 +1,5 @@
""" interface between the app and various connectors """ """ interface between the app and various connectors """
from unittest.mock import patch
from django.test import TestCase from django.test import TestCase
import responses import responses
@ -13,14 +14,15 @@ class ConnectorManager(TestCase):
def setUp(self): def setUp(self):
"""we'll need some books and a connector info entry""" """we'll need some books and a connector info entry"""
self.work = models.Work.objects.create(title="Example Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.work = models.Work.objects.create(title="Example Work")
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work, isbn_10="0000000000" title="Example Edition", parent_work=self.work, isbn_10="0000000000"
) )
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Another Edition", parent_work=self.work, isbn_10="1111111111" title="Another Edition", parent_work=self.work, isbn_10="1111111111"
) )
self.connector = models.Connector.objects.create( self.connector = models.Connector.objects.create(
identifier="test_connector", identifier="test_connector",

View file

@ -178,20 +178,26 @@ class Openlibrary(TestCase):
@responses.activate @responses.activate
def test_expand_book_data(self): def test_expand_book_data(self):
"""given a book, get more editions""" """given a book, get more editions"""
work = models.Work.objects.create(title="Test Work", openlibrary_key="OL1234W") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
edition = models.Edition.objects.create(title="Test Edition", parent_work=work) work = models.Work.objects.create(
title="Test Work", openlibrary_key="OL1234W"
)
edition = models.Edition.objects.create(
title="Test Edition", parent_work=work
)
responses.add( responses.add(
responses.GET, responses.GET,
"https://openlibrary.org/works/OL1234W/editions", "https://openlibrary.org/works/OL1234W/editions",
json={"entries": []}, json={"entries": []},
) )
with patch( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"bookwyrm.connectors.abstract_connector.AbstractConnector." with patch(
"create_edition_from_data" "bookwyrm.connectors.abstract_connector.AbstractConnector."
): "create_edition_from_data"
self.connector.expand_book_data(edition) ):
self.connector.expand_book_data(work) self.connector.expand_book_data(edition)
self.connector.expand_book_data(work)
def test_get_description(self): def test_get_description(self):
"""should do some cleanup on the description data""" """should do some cleanup on the description data"""
@ -224,11 +230,14 @@ class Openlibrary(TestCase):
json={"hi": "there"}, json={"hi": "there"},
status=200, status=200,
) )
with patch( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
"bookwyrm.connectors.openlibrary.Connector." "get_authors_from_data" with patch(
) as mock: "bookwyrm.connectors.openlibrary.Connector." "get_authors_from_data"
mock.return_value = [] ) as mock:
result = self.connector.create_edition_from_data(work, self.edition_data) mock.return_value = []
result = self.connector.create_edition_from_data(
work, self.edition_data
)
self.assertEqual(result.parent_work, work) self.assertEqual(result.parent_work, work)
self.assertEqual(result.title, "Sabriel") self.assertEqual(result.title, "Sabriel")
self.assertEqual(result.isbn_10, "0060273224") self.assertEqual(result.isbn_10, "0060273224")

View file

@ -1,4 +1,5 @@
""" testing book data connectors """ """ testing book data connectors """
from unittest.mock import patch
import datetime import datetime
from django.test import TestCase from django.test import TestCase
from django.utils import timezone from django.utils import timezone
@ -29,19 +30,21 @@ class SelfConnector(TestCase):
def test_format_search_result(self): def test_format_search_result(self):
"""create a SearchResult""" """create a SearchResult"""
author = models.Author.objects.create(name="Anonymous") author = models.Author.objects.create(name="Anonymous")
edition = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Edition of Example Work", edition = models.Edition.objects.create(
published_date=datetime.datetime(1980, 5, 10, tzinfo=timezone.utc), title="Edition of Example Work",
) published_date=datetime.datetime(1980, 5, 10, tzinfo=timezone.utc),
edition.authors.add(author) )
result = self.connector.search("Edition of Example")[0] edition.authors.add(author)
result = self.connector.search("Edition of Example")[0]
self.assertEqual(result.title, "Edition of Example Work") self.assertEqual(result.title, "Edition of Example Work")
self.assertEqual(result.key, edition.remote_id) self.assertEqual(result.key, edition.remote_id)
self.assertEqual(result.author, "Anonymous") self.assertEqual(result.author, "Anonymous")
self.assertEqual(result.year, 1980) self.assertEqual(result.year, 1980)
self.assertEqual(result.connector, self.connector) self.assertEqual(result.connector, self.connector)
def test_search_rank(self): @patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay")
def test_search_rank(self, _):
"""prioritize certain results""" """prioritize certain results"""
author = models.Author.objects.create(name="Anonymous") author = models.Author.objects.create(name="Anonymous")
edition = models.Edition.objects.create( edition = models.Edition.objects.create(
@ -49,7 +52,7 @@ class SelfConnector(TestCase):
published_date=datetime.datetime(1980, 5, 10, tzinfo=timezone.utc), published_date=datetime.datetime(1980, 5, 10, tzinfo=timezone.utc),
parent_work=models.Work.objects.create(title=""), parent_work=models.Work.objects.create(title=""),
) )
# author text is rank C # author text is rank B
edition.authors.add(author) edition.authors.add(author)
# series is rank D # series is rank D
@ -67,17 +70,19 @@ class SelfConnector(TestCase):
# title is rank A # title is rank A
models.Edition.objects.create(title="Anonymous") models.Edition.objects.create(title="Anonymous")
# doesn't rank in this search # doesn't rank in this search
edition = models.Edition.objects.create( models.Edition.objects.create(
title="An Edition", parent_work=models.Work.objects.create(title="") title="An Edition", parent_work=models.Work.objects.create(title="")
) )
results = self.connector.search("Anonymous") results = self.connector.search("Anonymous")
self.assertEqual(len(results), 3) self.assertEqual(len(results), 4)
self.assertEqual(results[0].title, "Anonymous") self.assertEqual(results[0].title, "Anonymous")
self.assertEqual(results[1].title, "More Editions") self.assertEqual(results[1].title, "More Editions")
self.assertEqual(results[2].title, "Edition of Example Work") self.assertEqual(results[2].title, "Edition of Example Work")
self.assertEqual(results[3].title, "Another Edition")
def test_search_multiple_editions(self): @patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay")
def test_search_multiple_editions(self, _):
"""it should get rid of duplicate editions for the same work""" """it should get rid of duplicate editions for the same work"""
work = models.Work.objects.create(title="Work Title") work = models.Work.objects.create(title="Work Title")
edition_1 = models.Edition.objects.create( edition_1 = models.Edition.objects.create(
@ -86,7 +91,7 @@ class SelfConnector(TestCase):
edition_2 = models.Edition.objects.create( edition_2 = models.Edition.objects.create(
title="Edition 2 Title", title="Edition 2 Title",
parent_work=work, parent_work=work,
edition_rank=20, # that's default babey isbn_13="123456789", # this is now the defualt edition
) )
edition_3 = models.Edition.objects.create(title="Fish", parent_work=work) edition_3 = models.Edition.objects.create(title="Fish", parent_work=work)

View file

@ -21,9 +21,10 @@ 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)
self.user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "password", local=True self.user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "password", local=True
)
models.Connector.objects.create( models.Connector.objects.create(
identifier=DOMAIN, identifier=DOMAIN,
@ -36,12 +37,13 @@ class GoodreadsImport(TestCase):
search_url="https://%s/search?q=" % DOMAIN, search_url="https://%s/search?q=" % DOMAIN,
priority=1, priority=1,
) )
work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create( work = models.Work.objects.create(title="Test Work")
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", title="Example Edition",
parent_work=work, remote_id="https://example.com/book/1",
) 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"""
@ -92,7 +94,8 @@ class GoodreadsImport(TestCase):
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") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
book = models.Edition.objects.create(title="Test Book")
with patch( with patch(
"bookwyrm.models.import_job.ImportItem.get_book_from_isbn" "bookwyrm.models.import_job.ImportItem.get_book_from_isbn"
@ -119,10 +122,11 @@ class GoodreadsImport(TestCase):
) )
break break
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
)
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -183,13 +187,14 @@ class GoodreadsImport(TestCase):
) )
break break
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
handle_imported_book( )
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
)
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -216,10 +221,11 @@ class GoodreadsImport(TestCase):
job_id=import_job.id, index=0, data=entry, book=self.book job_id=import_job.id, index=0, data=entry, book=self.book
) )
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, True, "unlisted" handle_imported_book(
) self.importer.service, self.user, import_item, True, "unlisted"
)
review = models.Review.objects.get(book=self.book, user=self.user) review = models.Review.objects.get(book=self.book, user=self.user)
self.assertEqual(review.content, "mixed feelings") self.assertEqual(review.content, "mixed feelings")
self.assertEqual(review.rating, 2) self.assertEqual(review.rating, 2)
@ -242,10 +248,11 @@ class GoodreadsImport(TestCase):
job_id=import_job.id, index=0, data=entry, book=self.book job_id=import_job.id, index=0, data=entry, book=self.book
) )
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, True, "unlisted" handle_imported_book(
) self.importer.service, self.user, import_item, True, "unlisted"
)
review = models.ReviewRating.objects.get(book=self.book, user=self.user) review = models.ReviewRating.objects.get(book=self.book, user=self.user)
self.assertIsInstance(review, models.ReviewRating) self.assertIsInstance(review, models.ReviewRating)
self.assertEqual(review.rating, 2) self.assertEqual(review.rating, 2)
@ -265,10 +272,11 @@ class GoodreadsImport(TestCase):
job_id=import_job.id, index=0, data=entry, book=self.book job_id=import_job.id, index=0, data=entry, book=self.book
) )
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "unlisted" handle_imported_book(
) self.importer.service, self.user, import_item, False, "unlisted"
)
self.assertFalse( self.assertFalse(
models.Review.objects.filter(book=self.book, user=self.user).exists() models.Review.objects.filter(book=self.book, user=self.user).exists()
) )

View file

@ -22,9 +22,10 @@ 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)
self.user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mmai", "mmai@mmai.mmai", "password", local=True self.user = models.User.objects.create_user(
) "mmai", "mmai@mmai.mmai", "password", local=True
)
models.Connector.objects.create( models.Connector.objects.create(
identifier=DOMAIN, identifier=DOMAIN,
@ -37,12 +38,13 @@ class LibrarythingImport(TestCase):
search_url="https://%s/search?q=" % DOMAIN, search_url="https://%s/search?q=" % DOMAIN,
priority=1, priority=1,
) )
work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create( work = models.Work.objects.create(title="Test Work")
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", title="Example Edition",
parent_work=work, remote_id="https://example.com/book/1",
) 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"""
@ -82,7 +84,8 @@ class LibrarythingImport(TestCase):
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") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
book = models.Edition.objects.create(title="Test Book")
with patch( with patch(
"bookwyrm.models.import_job.ImportItem.get_book_from_isbn" "bookwyrm.models.import_job.ImportItem.get_book_from_isbn"
@ -111,10 +114,11 @@ class LibrarythingImport(TestCase):
) )
break break
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
)
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -147,10 +151,11 @@ class LibrarythingImport(TestCase):
) )
break break
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
)
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -179,13 +184,14 @@ class LibrarythingImport(TestCase):
) )
break break
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
handle_imported_book( )
self.importer.service, self.user, import_item, False, "public" handle_imported_book(
) self.importer.service, self.user, import_item, False, "public"
)
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -212,10 +218,11 @@ class LibrarythingImport(TestCase):
job_id=import_job.id, index=0, data=entry, book=self.book job_id=import_job.id, index=0, data=entry, book=self.book
) )
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, True, "unlisted" handle_imported_book(
) self.importer.service, self.user, import_item, True, "unlisted"
)
review = models.Review.objects.get(book=self.book, user=self.user) review = models.Review.objects.get(book=self.book, user=self.user)
self.assertEqual(review.content, "chef d'oeuvre") self.assertEqual(review.content, "chef d'oeuvre")
self.assertEqual(review.rating, 5) self.assertEqual(review.rating, 5)
@ -235,10 +242,11 @@ class LibrarythingImport(TestCase):
job_id=import_job.id, index=0, data=entry, book=self.book job_id=import_job.id, index=0, data=entry, book=self.book
) )
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
handle_imported_book( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
self.importer.service, self.user, import_item, False, "unlisted" handle_imported_book(
) self.importer.service, self.user, import_item, False, "unlisted"
)
self.assertFalse( self.assertFalse(
models.Review.objects.filter(book=self.book, user=self.user).exists() models.Review.objects.filter(book=self.book, user=self.user).exists()
) )

View file

@ -12,23 +12,29 @@ class Activitystreams(TestCase):
def setUp(self): def setUp(self):
"""we need some stuff""" """we need some stuff"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
self.another_user = models.User.objects.create_user(
"nutria", "nutria@nutria.nutria", "password", local=True, localname="nutria"
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.book = models.Edition.objects.create(title="test book") self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create(title="test book")
def test_populate_streams(self, _): def test_populate_streams(self, _):
"""make sure the function on the redis manager gets called""" """make sure the function on the redis manager gets called"""

View file

@ -19,21 +19,22 @@ class ActivitypubMixins(TestCase):
def setUp(self): def setUp(self):
"""shared data""" """shared data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
self.local_user.remote_id = "http://example.com/a/b"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.local_user.remote_id = "http://example.com/a/b"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
self.object_mock = { self.object_mock = {
"to": "to field", "to": "to field",
@ -70,9 +71,10 @@ class ActivitypubMixins(TestCase):
"""attempt to match a remote id to an object in the db""" """attempt to match a remote id to an object in the db"""
# uses a different remote id scheme # uses a different remote id scheme
# this isn't really part of this test directly but it's helpful to state # this isn't really part of this test directly but it's helpful to state
book = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test Edition", remote_id="http://book.com/book" book = models.Edition.objects.create(
) title="Test Edition", remote_id="http://book.com/book"
)
self.assertEqual(book.origin_id, "http://book.com/book") self.assertEqual(book.origin_id, "http://book.com/book")
self.assertNotEqual(book.remote_id, "http://book.com/book") self.assertNotEqual(book.remote_id, "http://book.com/book")
@ -101,10 +103,11 @@ class ActivitypubMixins(TestCase):
def test_find_existing(self, _): def test_find_existing(self, _):
"""match a blob of data to a model""" """match a blob of data to a model"""
book = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test edition", book = models.Edition.objects.create(
openlibrary_key="OL1234", title="Test edition",
) openlibrary_key="OL1234",
)
result = models.Edition.find_existing({"openlibraryKey": "OL1234"}) result = models.Edition.find_existing({"openlibraryKey": "OL1234"})
self.assertEqual(result, book) self.assertEqual(result, book)
@ -140,16 +143,17 @@ class ActivitypubMixins(TestCase):
MockSelf = namedtuple("Self", ("privacy", "user")) MockSelf = namedtuple("Self", ("privacy", "user"))
mock_self = MockSelf("public", self.local_user) mock_self = MockSelf("public", self.local_user)
self.local_user.followers.add(self.remote_user) self.local_user.followers.add(self.remote_user)
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
another_remote_user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"nutria", another_remote_user = models.User.objects.create_user(
"nutria@nutria.com", "nutria",
"nutriaword", "nutria@nutria.com",
local=False, "nutriaword",
remote_id="https://example.com/users/nutria", local=False,
inbox="https://example.com/users/nutria/inbox", remote_id="https://example.com/users/nutria",
outbox="https://example.com/users/nutria/outbox", inbox="https://example.com/users/nutria/inbox",
) outbox="https://example.com/users/nutria/outbox",
)
MockSelf = namedtuple("Self", ("privacy", "user", "recipients")) MockSelf = namedtuple("Self", ("privacy", "user", "recipients"))
mock_self = MockSelf("public", self.local_user, [another_remote_user]) mock_self = MockSelf("public", self.local_user, [another_remote_user])
@ -163,16 +167,17 @@ class ActivitypubMixins(TestCase):
MockSelf = namedtuple("Self", ("privacy", "user")) MockSelf = namedtuple("Self", ("privacy", "user"))
mock_self = MockSelf("public", self.local_user) mock_self = MockSelf("public", self.local_user)
self.local_user.followers.add(self.remote_user) self.local_user.followers.add(self.remote_user)
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
another_remote_user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"nutria", another_remote_user = models.User.objects.create_user(
"nutria@nutria.com", "nutria",
"nutriaword", "nutria@nutria.com",
local=False, "nutriaword",
remote_id="https://example.com/users/nutria", local=False,
inbox="https://example.com/users/nutria/inbox", remote_id="https://example.com/users/nutria",
outbox="https://example.com/users/nutria/outbox", inbox="https://example.com/users/nutria/inbox",
) outbox="https://example.com/users/nutria/outbox",
)
MockSelf = namedtuple("Self", ("privacy", "user", "recipients")) MockSelf = namedtuple("Self", ("privacy", "user", "recipients"))
mock_self = MockSelf("direct", self.local_user, [another_remote_user]) mock_self = MockSelf("direct", self.local_user, [another_remote_user])
@ -184,17 +189,18 @@ class ActivitypubMixins(TestCase):
"""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)
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
another_remote_user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"nutria", another_remote_user = models.User.objects.create_user(
"nutria@nutria.com", "nutria",
"nutriaword", "nutria@nutria.com",
local=False, "nutriaword",
remote_id="https://example.com/users/nutria", local=False,
inbox="https://example.com/users/nutria/inbox", remote_id="https://example.com/users/nutria",
shared_inbox="http://example.com/inbox", inbox="https://example.com/users/nutria/inbox",
outbox="https://example.com/users/nutria/outbox", shared_inbox="http://example.com/inbox",
) outbox="https://example.com/users/nutria/outbox",
)
MockSelf = namedtuple("Self", ("privacy", "user")) MockSelf = namedtuple("Self", ("privacy", "user"))
mock_self = MockSelf("public", self.local_user) mock_self = MockSelf("public", self.local_user)
self.local_user.followers.add(self.remote_user) self.local_user.followers.add(self.remote_user)
@ -206,17 +212,18 @@ class ActivitypubMixins(TestCase):
def test_get_recipients_software(self, _): def test_get_recipients_software(self, _):
"""should differentiate between bookwyrm and other remote users""" """should differentiate between bookwyrm and other remote users"""
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
another_remote_user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"nutria", another_remote_user = models.User.objects.create_user(
"nutria@nutria.com", "nutria",
"nutriaword", "nutria@nutria.com",
local=False, "nutriaword",
remote_id="https://example.com/users/nutria", local=False,
inbox="https://example.com/users/nutria/inbox", remote_id="https://example.com/users/nutria",
outbox="https://example.com/users/nutria/outbox", inbox="https://example.com/users/nutria/inbox",
bookwyrm_user=False, outbox="https://example.com/users/nutria/outbox",
) bookwyrm_user=False,
)
MockSelf = namedtuple("Self", ("privacy", "user")) MockSelf = namedtuple("Self", ("privacy", "user"))
mock_self = MockSelf("public", self.local_user) mock_self = MockSelf("public", self.local_user)
self.local_user.followers.add(self.remote_user) self.local_user.followers.add(self.remote_user)

View file

@ -12,19 +12,20 @@ class BaseModel(TestCase):
def setUp(self): def setUp(self):
"""shared data""" """shared data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
class BookWyrmTestModel(base_model.BookWyrmModel): class BookWyrmTestModel(base_model.BookWyrmModel):
"""just making it not abstract""" """just making it not abstract"""

View file

@ -1,4 +1,6 @@
""" testing models """ """ testing models """
from unittest.mock import patch
from dateutil.parser import parse from dateutil.parser import parse
from django.test import TestCase from django.test import TestCase
from django.utils import timezone from django.utils import timezone
@ -12,17 +14,18 @@ class Book(TestCase):
def setUp(self): def setUp(self):
"""we'll need some books""" """we'll need some books"""
self.work = models.Work.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Example Work", remote_id="https://example.com/book/1" self.work = models.Work.objects.create(
) title="Example Work", remote_id="https://example.com/book/1"
self.first_edition = models.Edition.objects.create( )
title="Example Edition", self.first_edition = models.Edition.objects.create(
parent_work=self.work, title="Example Edition",
) parent_work=self.work,
self.second_edition = models.Edition.objects.create( )
title="Another Example Edition", self.second_edition = models.Edition.objects.create(
parent_work=self.work, title="Another Example Edition",
) parent_work=self.work,
)
def test_remote_id(self): def test_remote_id(self):
"""fanciness with remote/origin ids""" """fanciness with remote/origin ids"""
@ -56,7 +59,8 @@ class Book(TestCase):
def test_get_edition_info(self): def test_get_edition_info(self):
"""text slug about an edition""" """text slug about an edition"""
book = models.Edition.objects.create(title="Test Edition") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
book = models.Edition.objects.create(title="Test Edition")
self.assertEqual(book.edition_info, "") self.assertEqual(book.edition_info, "")
book.physical_format = "worm" book.physical_format = "worm"

View file

@ -11,29 +11,30 @@ class FederatedServer(TestCase):
def setUp(self): def setUp(self):
"""we'll need a user""" """we'll need a user"""
self.server = models.FederatedServer.objects.create(server_name="test.server") self.server = models.FederatedServer.objects.create(server_name="test.server")
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
self.remote_user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"rat", self.remote_user = models.User.objects.create_user(
"rat@rat.com", "rat",
"ratword", "rat@rat.com",
federated_server=self.server, "ratword",
local=False, federated_server=self.server,
remote_id="https://example.com/users/rat", local=False,
inbox="https://example.com/users/rat/inbox", remote_id="https://example.com/users/rat",
outbox="https://example.com/users/rat/outbox", inbox="https://example.com/users/rat/inbox",
) outbox="https://example.com/users/rat/outbox",
self.inactive_remote_user = models.User.objects.create_user( )
"nutria", self.inactive_remote_user = models.User.objects.create_user(
"nutria@nutria.com", "nutria",
"nutriaword", "nutria@nutria.com",
federated_server=self.server, "nutriaword",
local=False, federated_server=self.server,
remote_id="https://example.com/users/nutria", local=False,
inbox="https://example.com/users/nutria/inbox", remote_id="https://example.com/users/nutria",
outbox="https://example.com/users/nutria/outbox", inbox="https://example.com/users/nutria/inbox",
is_active=False, outbox="https://example.com/users/nutria/outbox",
deactivation_reason="self_deletion", is_active=False,
) deactivation_reason="self_deletion",
)
def test_block_unblock(self): def test_block_unblock(self):
"""block a server and all users on it""" """block a server and all users on it"""

View file

@ -188,9 +188,10 @@ class ActivitypubFields(TestCase):
@patch("bookwyrm.activitystreams.ActivityStream.add_status") @patch("bookwyrm.activitystreams.ActivityStream.add_status")
def test_privacy_field_set_activity_from_field(self, *_): def test_privacy_field_set_activity_from_field(self, *_):
"""translate between to/cc fields and privacy""" """translate between to/cc fields and privacy"""
user = User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"rat", "rat@rat.rat", "ratword", local=True, localname="rat" user = User.objects.create_user(
) "rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
public = "https://www.w3.org/ns/activitystreams#Public" public = "https://www.w3.org/ns/activitystreams#Public"
followers = "%s/followers" % user.remote_id followers = "%s/followers" % user.remote_id
@ -248,16 +249,20 @@ class ActivitypubFields(TestCase):
del userdata["icon"] del userdata["icon"]
# it shouldn't match with this unrelated user: # it shouldn't match with this unrelated user:
unrelated_user = User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"rat", "rat@rat.rat", "ratword", local=True, localname="rat" unrelated_user = User.objects.create_user(
) "rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
# test receiving an unknown remote id and loading data # test receiving an unknown remote id and loading data
responses.add( responses.add(
responses.GET, "https://example.com/user/mouse", json=userdata, status=200 responses.GET,
) "https://example.com/user/mouse",
with patch("bookwyrm.models.user.set_remote_server.delay"): json=userdata,
value = instance.field_from_activity("https://example.com/user/mouse") status=200,
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
value = instance.field_from_activity("https://example.com/user/mouse")
self.assertIsInstance(value, User) self.assertIsInstance(value, User)
self.assertNotEqual(value, unrelated_user) self.assertNotEqual(value, unrelated_user)
self.assertEqual(value.remote_id, "https://example.com/user/mouse") self.assertEqual(value.remote_id, "https://example.com/user/mouse")
@ -272,11 +277,12 @@ class ActivitypubFields(TestCase):
del userdata["icon"] del userdata["icon"]
# it shouldn't match with this unrelated user: # it shouldn't match with this unrelated user:
unrelated_user = User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"rat", "rat@rat.rat", "ratword", local=True, localname="rat" unrelated_user = User.objects.create_user(
) "rat", "rat@rat.rat", "ratword", local=True, localname="rat"
with patch("bookwyrm.models.user.set_remote_server.delay"): )
value = instance.field_from_activity(activitypub.Person(**userdata)) with patch("bookwyrm.models.user.set_remote_server.delay"):
value = instance.field_from_activity(activitypub.Person(**userdata))
self.assertIsInstance(value, User) self.assertIsInstance(value, User)
self.assertNotEqual(value, unrelated_user) self.assertNotEqual(value, unrelated_user)
self.assertEqual(value.remote_id, "https://example.com/user/mouse") self.assertEqual(value.remote_id, "https://example.com/user/mouse")
@ -288,28 +294,31 @@ class ActivitypubFields(TestCase):
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")
userdata = json.loads(datafile.read_bytes()) userdata = json.loads(datafile.read_bytes())
user = User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" user = User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
user.remote_id = "https://example.com/user/mouse" )
user.save(broadcast=False) user.remote_id = "https://example.com/user/mouse"
User.objects.create_user( user.save(broadcast=False)
"rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
with patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast"): User.objects.create_user(
value = instance.field_from_activity(activitypub.Person(**userdata)) "rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
with patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast"):
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( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" user = User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
User.objects.create_user( )
"rat", "rat@rat.rat", "ratword", local=True, localname="rat" User.objects.create_user(
) "rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
value = instance.field_from_activity(user.remote_id) value = instance.field_from_activity(user.remote_id)
self.assertEqual(value, user) self.assertEqual(value, user)
@ -351,10 +360,11 @@ class ActivitypubFields(TestCase):
responses.add( responses.add(
responses.GET, "https://example.com/user/mouse", json=userdata, status=200 responses.GET, "https://example.com/user/mouse", json=userdata, status=200
) )
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
value = instance.field_from_activity( with patch("bookwyrm.models.user.set_remote_server.delay"):
["https://example.com/user/mouse", "bleh"] value = instance.field_from_activity(
) ["https://example.com/user/mouse", "bleh"]
)
self.assertIsInstance(value, list) self.assertIsInstance(value, list)
self.assertEqual(len(value), 1) self.assertEqual(len(value), 1)
self.assertIsInstance(value[0], User) self.assertIsInstance(value[0], User)
@ -386,16 +396,17 @@ class ActivitypubFields(TestCase):
@patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast") @patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast")
def test_image_field(self, _): def test_image_field(self, _):
"""storing images""" """storing images"""
user = User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" user = User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
image_file = pathlib.Path(__file__).parent.joinpath( )
"../../static/images/default_avi.jpg" image_file = pathlib.Path(__file__).parent.joinpath(
) "../../static/images/default_avi.jpg"
image = Image.open(image_file) )
output = BytesIO() image = Image.open(image_file)
image.save(output, format=image.format) output = BytesIO()
user.avatar.save("test.jpg", ContentFile(output.getvalue())) image.save(output, format=image.format)
user.avatar.save("test.jpg", ContentFile(output.getvalue()))
output = fields.image_serializer(user.avatar, alt="alt text") output = fields.image_serializer(user.avatar, alt="alt text")
self.assertIsNotNone( self.assertIsNotNone(

View file

@ -59,9 +59,10 @@ 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"] = ""
user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
)
job = models.ImportJob.objects.create(user=user) job = models.ImportJob.objects.create(user=user)
self.item_1 = models.ImportItem.objects.create( self.item_1 = models.ImportItem.objects.create(
job=job, index=1, data=currently_reading_data job=job, index=1, data=currently_reading_data
@ -174,6 +175,9 @@ class ImportJob(TestCase):
with patch( with patch(
"bookwyrm.connectors.openlibrary.Connector." "get_authors_from_data" "bookwyrm.connectors.openlibrary.Connector." "get_authors_from_data"
): ):
book = self.item_1.get_book_from_isbn() with patch(
"bookwyrm.preview_images.generate_edition_preview_image_task.delay"
):
book = self.item_1.get_book_from_isbn()
self.assertEqual(book.title, "Sabriel") self.assertEqual(book.title, "Sabriel")

View file

@ -11,11 +11,13 @@ class List(TestCase):
def setUp(self): def setUp(self):
"""look, a list""" """look, a list"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
work = models.Work.objects.create(title="hello") )
self.book = models.Edition.objects.create(title="hi", parent_work=work) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
work = models.Work.objects.create(title="hello")
self.book = models.Edition.objects.create(title="hi", parent_work=work)
def test_remote_id(self, _): def test_remote_id(self, _):
"""shelves use custom remote ids""" """shelves use custom remote ids"""

View file

@ -1,6 +1,7 @@
""" testing models """ """ testing models """
from django.test import TestCase from django.test import TestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from unittest.mock import patch
from bookwyrm import models from bookwyrm import models
@ -10,15 +11,16 @@ class ReadThrough(TestCase):
def setUp(self): def setUp(self):
"""look, a shelf""" """look, a shelf"""
self.user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" self.user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
)
self.work = models.Work.objects.create(title="Example Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.work = models.Work.objects.create(title="Example Work")
self.edition = models.Edition.objects.create( self.edition = models.Edition.objects.create(
title="Example Edition", parent_work=self.work title="Example Edition", parent_work=self.work
) )
self.readthrough = models.ReadThrough.objects.create( self.readthrough = models.ReadThrough.objects.create(
user=self.user, book=self.edition user=self.user, book=self.edition

View file

@ -10,21 +10,22 @@ class Relationship(TestCase):
def setUp(self): def setUp(self):
"""we need some users for this""" """we need some users for this"""
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
self.remote_user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"rat", self.remote_user = models.User.objects.create_user(
"rat@rat.com", "rat",
"ratword", "rat@rat.com",
local=False, "ratword",
remote_id="https://example.com/users/rat", local=False,
inbox="https://example.com/users/rat/inbox", remote_id="https://example.com/users/rat",
outbox="https://example.com/users/rat/outbox", inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
self.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse"
) )
self.local_user = models.User.objects.create_user( self.local_user.remote_id = "http://local.com/user/mouse"
"mouse", "mouse@mouse.com", "mouseword", local=True, localname="mouse" self.local_user.save(broadcast=False)
)
self.local_user.remote_id = "http://local.com/user/mouse"
self.local_user.save(broadcast=False)
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"""

View file

@ -12,11 +12,15 @@ class Shelf(TestCase):
def setUp(self): def setUp(self):
"""look, a shelf""" """look, a shelf"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
work = models.Work.objects.create(title="Test Work") )
self.book = models.Edition.objects.create(title="test book", parent_work=work) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
work = models.Work.objects.create(title="Test Work")
self.book = models.Edition.objects.create(
title="test book", parent_work=work
)
def test_remote_id(self): def test_remote_id(self):
"""shelves use custom remote ids""" """shelves use custom remote ids"""

View file

@ -22,29 +22,31 @@ class Status(TestCase):
def setUp(self): def setUp(self):
"""useful things for creating a status""" """useful things for creating a status"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.book = models.Edition.objects.create(title="Test Edition") with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create(title="Test Edition")
image_file = pathlib.Path(__file__).parent.joinpath( image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg" "../../static/images/default_avi.jpg"
) )
image = Image.open(image_file) image = Image.open(image_file)
output = BytesIO() output = BytesIO()
with patch("bookwyrm.models.Status.broadcast"): with patch("bookwyrm.models.Status.broadcast"):
image.save(output, format=image.format) image.save(output, format=image.format)
self.book.cover.save("test.jpg", ContentFile(output.getvalue())) self.book.cover.save("test.jpg", ContentFile(output.getvalue()))
def test_status_generated_fields(self, *_): def test_status_generated_fields(self, *_):
"""setting remote id""" """setting remote id"""
@ -59,9 +61,10 @@ class Status(TestCase):
child = models.Status.objects.create( child = models.Status.objects.create(
content="hello", reply_parent=parent, user=self.local_user content="hello", reply_parent=parent, user=self.local_user
) )
models.Review.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
content="hey", reply_parent=parent, user=self.local_user, book=self.book models.Review.objects.create(
) content="hey", reply_parent=parent, user=self.local_user, book=self.book
)
models.Status.objects.create( models.Status.objects.create(
content="hi hello", reply_parent=child, user=self.local_user content="hi hello", reply_parent=child, user=self.local_user
) )
@ -93,9 +96,10 @@ class Status(TestCase):
child = models.Status.objects.create( child = models.Status.objects.create(
content="hello", reply_parent=parent, user=self.local_user content="hello", reply_parent=parent, user=self.local_user
) )
models.Review.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
content="hey", reply_parent=parent, user=self.local_user, book=self.book models.Review.objects.create(
) content="hey", reply_parent=parent, user=self.local_user, book=self.book
)
models.Status.objects.create( models.Status.objects.create(
content="hi hello", reply_parent=child, user=self.local_user content="hi hello", reply_parent=child, user=self.local_user
) )
@ -252,14 +256,15 @@ class Status(TestCase):
def test_review_to_activity(self, *_): def test_review_to_activity(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.Review.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
name="Review name", status = models.Review.objects.create(
content="test content", name="Review name",
rating=3.0, content="test content",
user=self.local_user, rating=3.0,
book=self.book, user=self.local_user,
) book=self.book,
activity = status.to_activity() )
activity = status.to_activity()
self.assertEqual(activity["id"], status.remote_id) self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(activity["type"], "Review") self.assertEqual(activity["type"], "Review")
self.assertEqual(activity["rating"], 3) self.assertEqual(activity["rating"], 3)
@ -269,14 +274,15 @@ class Status(TestCase):
def test_review_to_pure_activity(self, *_): def test_review_to_pure_activity(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.Review.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
name="Review's name", status = models.Review.objects.create(
content="test content", name="Review's name",
rating=3.0, content="test content",
user=self.local_user, rating=3.0,
book=self.book, user=self.local_user,
) book=self.book,
activity = status.to_activity(pure=True) )
activity = status.to_activity(pure=True)
self.assertEqual(activity["id"], status.remote_id) self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(activity["type"], "Article") self.assertEqual(activity["type"], "Article")
self.assertEqual( self.assertEqual(
@ -293,13 +299,14 @@ class Status(TestCase):
def test_review_to_pure_activity_no_rating(self, *_): def test_review_to_pure_activity_no_rating(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.Review.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
name="Review name", status = models.Review.objects.create(
content="test content", name="Review name",
user=self.local_user, content="test content",
book=self.book, user=self.local_user,
) book=self.book,
activity = status.to_activity(pure=True) )
activity = status.to_activity(pure=True)
self.assertEqual(activity["id"], status.remote_id) self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(activity["type"], "Article") self.assertEqual(activity["type"], "Article")
self.assertEqual( self.assertEqual(
@ -315,12 +322,13 @@ class Status(TestCase):
def test_reviewrating_to_pure_activity(self, *_): def test_reviewrating_to_pure_activity(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.ReviewRating.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
rating=3.0, status = models.ReviewRating.objects.create(
user=self.local_user, rating=3.0,
book=self.book, user=self.local_user,
) book=self.book,
activity = status.to_activity(pure=True) )
activity = status.to_activity(pure=True)
self.assertEqual(activity["id"], status.remote_id) self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(activity["type"], "Note") self.assertEqual(activity["type"], "Note")
self.assertEqual( self.assertEqual(
@ -349,11 +357,12 @@ class Status(TestCase):
status = models.Status.objects.create( status = models.Status.objects.create(
content="test content", user=self.local_user content="test content", user=self.local_user
) )
fav = models.Favorite.objects.create(status=status, user=self.local_user) with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
fav = models.Favorite.objects.create(status=status, user=self.local_user)
# can't fav a status twice # can't fav a status twice
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
models.Favorite.objects.create(status=status, user=self.local_user) models.Favorite.objects.create(status=status, user=self.local_user)
activity = fav.to_activity() activity = fav.to_activity()
self.assertEqual(activity["type"], "Like") self.assertEqual(activity["type"], "Like")

View file

@ -11,15 +11,16 @@ 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):
self.user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@%s" % DOMAIN, self.user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@%s" % DOMAIN,
"mouseword", "mouse@mouse.mouse",
local=True, "mouseword",
localname="mouse", local=True,
name="hi", localname="mouse",
bookwyrm_user=False, name="hi",
) bookwyrm_user=False,
)
def test_computed_fields(self): def test_computed_fields(self):
"""username instead of id here""" """username instead of id here"""
@ -34,15 +35,16 @@ class User(TestCase):
self.assertIsNotNone(self.user.key_pair.public_key) self.assertIsNotNone(self.user.key_pair.public_key)
def test_remote_user(self): def test_remote_user(self):
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
user = models.User.objects.create_user( with patch("bookwyrm.models.user.set_remote_server.delay"):
"rat", user = models.User.objects.create_user(
"rat@rat.rat", "rat",
"ratword", "rat@rat.rat",
local=False, "ratword",
remote_id="https://example.com/dfjkg", local=False,
bookwyrm_user=False, remote_id="https://example.com/dfjkg",
) bookwyrm_user=False,
)
self.assertEqual(user.username, "rat@example.com") self.assertEqual(user.username, "rat@example.com")
def test_user_shelves(self): def test_user_shelves(self):

View file

@ -11,23 +11,29 @@ class Activitystreams(TestCase):
def setUp(self): def setUp(self):
"""use a test csv""" """use a test csv"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" self.local_user = models.User.objects.create_user(
) "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse"
self.another_user = models.User.objects.create_user(
"nutria", "nutria@nutria.nutria", "password", local=True, localname="nutria"
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.book = models.Edition.objects.create(title="test book") self.another_user = models.User.objects.create_user(
"nutria",
"nutria@nutria.nutria",
"password",
local=True,
localname="nutria",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create(title="test book")
class TestStream(activitystreams.ActivityStream): class TestStream(activitystreams.ActivityStream):
"""test stream, don't have to do anything here""" """test stream, don't have to do anything here"""

View file

@ -15,14 +15,16 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
models.SiteSettings.objects.create() )
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_invite_email(self, email_mock): def test_invite_email(self, email_mock):
"""load the invite email""" """load the invite email"""

View file

@ -0,0 +1,77 @@
""" django configuration of postgres """
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import models
@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay")
@patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay")
class PostgresTriggers(TestCase):
"""special migrations, fancy stuff ya know"""
def test_search_vector_on_create(self, *_):
"""make sure that search_vector is being set correctly on create"""
book = models.Edition.objects.create(title="The Long Goodbye")
book.refresh_from_db()
self.assertEqual(book.search_vector, "'goodby':3A 'long':2A")
def test_search_vector_on_update(self, *_):
"""make sure that search_vector is being set correctly on edit"""
book = models.Edition.objects.create(title="The Long Goodbye")
book.title = "The Even Longer Goodbye"
book.save(broadcast=False)
book.refresh_from_db()
self.assertEqual(book.search_vector, "'even':2A 'goodby':4A 'longer':3A")
def test_search_vector_fields(self, *_):
"""use multiple fields to create search vector"""
author = models.Author.objects.create(name="The Rays")
book = models.Edition.objects.create(
title="The Long Goodbye",
subtitle="wow cool",
series="series name",
languages=["irrelevent"],
)
book.authors.add(author)
book.refresh_from_db()
self.assertEqual(
book.search_vector,
"'cool':5B 'goodby':3A 'long':2A 'name':9 'rays':7C 'seri':8 'the':6C 'wow':4B",
)
def test_seach_vector_on_author_update(self, *_):
"""update search when an author name changes"""
author = models.Author.objects.create(name="The Rays")
book = models.Edition.objects.create(
title="The Long Goodbye",
)
book.authors.add(author)
author.name = "Jeremy"
author.save(broadcast=False)
book.refresh_from_db()
self.assertEqual(book.search_vector, "'goodby':3A 'jeremy':4C 'long':2A")
def test_seach_vector_on_author_delete(self, *_):
"""update search when an author name changes"""
author = models.Author.objects.create(name="Jeremy")
book = models.Edition.objects.create(
title="The Long Goodbye",
)
book.authors.add(author)
book.refresh_from_db()
self.assertEqual(book.search_vector, "'goodby':3A 'jeremy':4C 'long':2A")
book.authors.remove(author)
book.refresh_from_db()
self.assertEqual(book.search_vector, "'goodby':3A 'long':2A")
def test_search_vector_stop_word_fallback(self, *_):
"""use a fallback when removing stop words leads to an empty vector"""
book = models.Edition.objects.create(
title="there there",
)
book.refresh_from_db()
self.assertEqual(book.search_vector, "'there':1A,2A")

View file

@ -0,0 +1,119 @@
""" test generating preview images """
import pathlib
from unittest.mock import patch
from PIL import Image
from django.test import TestCase
from django.test.client import RequestFactory
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models.fields.files import ImageFieldFile
from bookwyrm import models, settings
from bookwyrm.preview_images import (
generate_site_preview_image_task,
generate_edition_preview_image_task,
generate_user_preview_image_task,
generate_preview_image,
save_and_cleanup,
)
# pylint: disable=unused-argument
# pylint: disable=missing-function-docstring
class PreviewImages(TestCase):
"""every response to a get request, html or json"""
def setUp(self):
"""we need basic test data and mocks"""
self.factory = RequestFactory()
with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
avatar_file = pathlib.Path(__file__).parent.joinpath(
"../static/images/no_cover.jpg"
)
self.local_user = models.User.objects.create_user(
"possum@local.com",
"possum@possum.possum",
"password",
local=True,
localname="possum",
avatar=SimpleUploadedFile(
avatar_file,
open(avatar_file, "rb").read(),
content_type="image/jpeg",
),
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.work = models.Work.objects.create(title="Test Work")
self.edition = models.Edition.objects.create(
title="Example Edition",
remote_id="https://example.com/book/1",
parent_work=self.work,
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
self.site = models.SiteSettings.objects.create()
def test_generate_preview_image(self, *args, **kwargs):
image_file = pathlib.Path(__file__).parent.joinpath(
"../static/images/no_cover.jpg"
)
texts = {
"text_one": "Awesome Possum",
"text_three": "@possum@local.com",
}
result = generate_preview_image(texts=texts, picture=image_file, rating=5)
self.assertIsInstance(result, Image.Image)
self.assertEqual(
result.size, (settings.PREVIEW_IMG_WIDTH, settings.PREVIEW_IMG_HEIGHT)
)
def test_store_preview_image(self, *args, **kwargs):
image = Image.new("RGB", (200, 200), color="#F00")
result = save_and_cleanup(image, instance=self.local_user)
self.assertTrue(result)
self.local_user.refresh_from_db()
self.assertIsInstance(self.local_user.preview_image, ImageFieldFile)
self.assertIsNotNone(self.local_user.preview_image)
self.assertEqual(self.local_user.preview_image.width, 200)
self.assertEqual(self.local_user.preview_image.height, 200)
def test_site_preview(self, *args, **kwargs):
"""generate site preview"""
generate_site_preview_image_task()
self.site.refresh_from_db()
self.assertIsInstance(self.site.preview_image, ImageFieldFile)
self.assertIsNotNone(self.site.preview_image)
self.assertEqual(self.site.preview_image.width, settings.PREVIEW_IMG_WIDTH)
self.assertEqual(self.site.preview_image.height, settings.PREVIEW_IMG_HEIGHT)
def test_edition_preview(self, *args, **kwargs):
"""generate edition preview"""
generate_edition_preview_image_task(self.edition.id)
self.edition.refresh_from_db()
self.assertIsInstance(self.edition.preview_image, ImageFieldFile)
self.assertIsNotNone(self.edition.preview_image)
self.assertEqual(self.edition.preview_image.width, settings.PREVIEW_IMG_WIDTH)
self.assertEqual(self.edition.preview_image.height, settings.PREVIEW_IMG_HEIGHT)
def test_user_preview(self, *args, **kwargs):
"""generate user preview"""
generate_user_preview_image_task(self.local_user.id)
self.local_user.refresh_from_db()
self.assertIsInstance(self.local_user.preview_image, ImageFieldFile)
self.assertIsNotNone(self.local_user.preview_image)
self.assertEqual(
self.local_user.preview_image.width, settings.PREVIEW_IMG_WIDTH
)
self.assertEqual(
self.local_user.preview_image.height, settings.PREVIEW_IMG_HEIGHT
)

View file

@ -37,15 +37,20 @@ class Signature(TestCase):
def setUp(self): def setUp(self):
"""create users and test data""" """create users and test data"""
self.mouse = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@%s" % DOMAIN, "mouse@example.com", "", local=True, localname="mouse" self.mouse = models.User.objects.create_user(
) "mouse@%s" % DOMAIN,
self.rat = models.User.objects.create_user( "mouse@example.com",
"rat@%s" % DOMAIN, "rat@example.com", "", local=True, localname="rat" "",
) local=True,
self.cat = models.User.objects.create_user( localname="mouse",
"cat@%s" % DOMAIN, "cat@example.com", "", local=True, localname="cat" )
) self.rat = models.User.objects.create_user(
"rat@%s" % DOMAIN, "rat@example.com", "", local=True, localname="rat"
)
self.cat = models.User.objects.create_user(
"cat@%s" % DOMAIN, "cat@example.com", "", local=True, localname="cat"
)
private_key, public_key = create_key_pair() private_key, public_key = create_key_pair()
@ -53,7 +58,8 @@ class Signature(TestCase):
"http://localhost/user/remote", KeyPair(private_key, public_key) "http://localhost/user/remote", KeyPair(private_key, public_key)
) )
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def send(self, signature, now, data, digest): def send(self, signature, now, data, digest):
"""test request""" """test request"""
@ -113,9 +119,10 @@ class Signature(TestCase):
status=200, status=200,
) )
with patch("bookwyrm.models.user.get_remote_reviews.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
response = self.send_test_request(sender=self.fake_remote) with patch("bookwyrm.models.user.get_remote_reviews.delay"):
self.assertEqual(response.status_code, 200) response = self.send_test_request(sender=self.fake_remote)
self.assertEqual(response.status_code, 200)
@responses.activate @responses.activate
def test_key_needs_refresh(self): def test_key_needs_refresh(self):
@ -136,22 +143,23 @@ class Signature(TestCase):
data["publicKey"]["publicKeyPem"] = key_pair.public_key data["publicKey"]["publicKeyPem"] = key_pair.public_key
responses.add(responses.GET, self.fake_remote.remote_id, json=data, status=200) responses.add(responses.GET, self.fake_remote.remote_id, json=data, status=200)
with patch("bookwyrm.models.user.get_remote_reviews.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
# Key correct: with patch("bookwyrm.models.user.get_remote_reviews.delay"):
response = self.send_test_request(sender=self.fake_remote) # Key correct:
self.assertEqual(response.status_code, 200) response = self.send_test_request(sender=self.fake_remote)
self.assertEqual(response.status_code, 200)
# Old key is cached, so still works: # Old key is cached, so still works:
response = self.send_test_request(sender=self.fake_remote) response = self.send_test_request(sender=self.fake_remote)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# Try with new key: # Try with new key:
response = self.send_test_request(sender=new_sender) response = self.send_test_request(sender=new_sender)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# Now the old key will fail: # Now the old key will fail:
response = self.send_test_request(sender=self.fake_remote) response = self.send_test_request(sender=self.fake_remote)
self.assertEqual(response.status_code, 401) self.assertEqual(response.status_code, 401)
@responses.activate @responses.activate
def test_nonexistent_signer(self): def test_nonexistent_signer(self):

View file

@ -22,27 +22,30 @@ class TemplateTags(TestCase):
def setUp(self): def setUp(self):
"""create some filler objects""" """create some filler objects"""
self.user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@example.com",
"mouseword", "mouse@mouse.mouse",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.rat",
"ratword",
remote_id="http://example.com/rat",
local=False,
) )
self.book = models.Edition.objects.create(title="Test Book") with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.rat",
"ratword",
remote_id="http://example.com/rat",
local=False,
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create(title="Test Book")
def test_get_user_rating(self, *_): def test_get_user_rating(self, *_):
"""get a user's most recent rating of a book""" """get a user's most recent rating of a book"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
models.Review.objects.create(user=self.user, book=self.book, rating=3) with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
models.Review.objects.create(user=self.user, book=self.book, rating=3)
self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 3) self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 3)
def test_get_user_rating_doesnt_exist(self, *_): def test_get_user_rating_doesnt_exist(self, *_):
@ -63,9 +66,12 @@ class TemplateTags(TestCase):
def test_get_replies(self, *_): def test_get_replies(self, *_):
"""direct replies to a status""" """direct replies to a status"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
parent = models.Review.objects.create( with patch(
user=self.user, book=self.book, content="hi" "bookwyrm.preview_images.generate_edition_preview_image_task.delay"
) ):
parent = models.Review.objects.create(
user=self.user, book=self.book, content="hi"
)
first_child = models.Status.objects.create( first_child = models.Status.objects.create(
reply_parent=parent, user=self.user, content="hi" reply_parent=parent, user=self.user, content="hi"
) )
@ -91,9 +97,12 @@ class TemplateTags(TestCase):
def test_get_parent(self, *_): def test_get_parent(self, *_):
"""get the reply parent of a status""" """get the reply parent of a status"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
parent = models.Review.objects.create( with patch(
user=self.user, book=self.book, content="hi" "bookwyrm.preview_images.generate_edition_preview_image_task.delay"
) ):
parent = models.Review.objects.create(
user=self.user, book=self.book, content="hi"
)
child = models.Status.objects.create( child = models.Status.objects.create(
reply_parent=parent, user=self.user, content="hi" reply_parent=parent, user=self.user, content="hi"
) )
@ -104,7 +113,8 @@ class TemplateTags(TestCase):
def test_get_user_liked(self, *_): def test_get_user_liked(self, *_):
"""did a user like a status""" """did a user like a status"""
status = models.Review.objects.create(user=self.remote_user, book=self.book) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
status = models.Review.objects.create(user=self.remote_user, book=self.book)
self.assertFalse(interaction.get_user_liked(self.user, status)) self.assertFalse(interaction.get_user_liked(self.user, status))
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
@ -113,7 +123,8 @@ class TemplateTags(TestCase):
def test_get_user_boosted(self, *_): def test_get_user_boosted(self, *_):
"""did a user boost a status""" """did a user boost a status"""
status = models.Review.objects.create(user=self.remote_user, book=self.book) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
status = models.Review.objects.create(user=self.remote_user, book=self.book)
self.assertFalse(interaction.get_user_boosted(self.user, status)) self.assertFalse(interaction.get_user_boosted(self.user, status))
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
@ -122,28 +133,34 @@ class TemplateTags(TestCase):
def test_get_boosted(self, *_): def test_get_boosted(self, *_):
"""load a boosted status""" """load a boosted status"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
status = models.Review.objects.create(user=self.remote_user, book=self.book) with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
boost = models.Boost.objects.create(user=self.user, boosted_status=status) status = models.Review.objects.create(
user=self.remote_user, book=self.book
)
boost = models.Boost.objects.create(
user=self.user, boosted_status=status
)
boosted = status_display.get_boosted(boost) boosted = status_display.get_boosted(boost)
self.assertIsInstance(boosted, models.Review) self.assertIsInstance(boosted, models.Review)
self.assertEqual(boosted, status) self.assertEqual(boosted, status)
def test_get_book_description(self, *_): def test_get_book_description(self, *_):
"""grab it from the edition or the parent""" """grab it from the edition or the parent"""
work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book.parent_work = work work = models.Work.objects.create(title="Test Work")
self.book.save() self.book.parent_work = work
self.book.save()
self.assertIsNone(bookwyrm_tags.get_book_description(self.book)) self.assertIsNone(bookwyrm_tags.get_book_description(self.book))
work.description = "hi" work.description = "hi"
work.save() work.save()
self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hi") self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hi")
self.book.description = "hello" self.book.description = "hello"
self.book.save() self.book.save()
self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hello") self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hello")
def test_get_uuid(self, *_): def test_get_uuid(self, *_):
"""uuid functionality""" """uuid functionality"""

View file

@ -18,25 +18,27 @@ class Inbox(TestCase):
"""basic user and book data""" """basic user and book data"""
self.client = Client() self.client = Client()
self.factory = RequestFactory() self.factory = RequestFactory()
local_user = models.User.objects.create_user(
"mouse@example.com", with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@mouse.com", local_user = models.User.objects.create_user(
"mouseword", "mouse@example.com",
local=True, "mouse@mouse.com",
localname="mouse", "mouseword",
) local=True,
local_user.remote_id = "https://example.com/user/mouse" localname="mouse",
local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
local_user.remote_id = "https://example.com/user/mouse"
local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
self.create_json = { self.create_json = {
"id": "hi", "id": "hi",
"type": "Create", "type": "Create",
@ -45,7 +47,8 @@ class Inbox(TestCase):
"cc": ["https://example.com/user/mouse/followers"], "cc": ["https://example.com/user/mouse/followers"],
"object": {}, "object": {},
} }
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_inbox_invalid_get(self): def test_inbox_invalid_get(self):
"""shouldn't try to handle if the user is not found""" """shouldn't try to handle if the user is not found"""

View file

@ -13,33 +13,35 @@ class InboxAdd(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
local_user.remote_id = "https://example.com/user/mouse"
local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
work = models.Work.objects.create(title="work title") local_user.remote_id = "https://example.com/user/mouse"
self.book = models.Edition.objects.create( local_user.save(broadcast=False)
title="Test", with patch("bookwyrm.models.user.set_remote_server.delay"):
remote_id="https://example.com/book/37292", self.remote_user = models.User.objects.create_user(
parent_work=work, "rat",
) "rat@rat.com",
"ratword",
models.SiteSettings.objects.create() local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
work = models.Work.objects.create(title="work title")
self.book = models.Edition.objects.create(
title="Test",
remote_id="https://example.com/book/37292",
parent_work=work,
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
@responses.activate @responses.activate
def test_handle_add_book_to_shelf(self): def test_handle_add_book_to_shelf(self):

View file

@ -13,32 +13,34 @@ class InboxActivities(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): self.local_user.remote_id = "https://example.com/user/mouse"
with patch("bookwyrm.activitystreams.ActivityStream.add_status"): self.local_user.save(broadcast=False)
self.status = models.Status.objects.create( with patch("bookwyrm.models.user.set_remote_server.delay"):
user=self.local_user, self.remote_user = models.User.objects.create_user(
content="Test status", "rat",
remote_id="https://example.com/status/1", "rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
self.status = models.Status.objects.create(
user=self.local_user,
content="Test status",
remote_id="https://example.com/status/1",
)
self.create_json = { self.create_json = {
"id": "hi", "id": "hi",
@ -48,7 +50,8 @@ class InboxActivities(TestCase):
"cc": ["https://example.com/user/mouse/followers"], "cc": ["https://example.com/user/mouse/followers"],
"object": {}, "object": {},
} }
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
@patch("bookwyrm.activitystreams.ActivityStream.add_status") @patch("bookwyrm.activitystreams.ActivityStream.add_status")
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") @patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
@ -85,12 +88,13 @@ class InboxActivities(TestCase):
@patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores") @patch("bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores")
def test_boost_remote_status(self, redis_mock, _): def test_boost_remote_status(self, redis_mock, _):
"""boost a status from a remote server""" """boost a status from a remote server"""
work = models.Work.objects.create(title="work title") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
book = models.Edition.objects.create( work = models.Work.objects.create(title="work title")
title="Test", book = models.Edition.objects.create(
remote_id="https://bookwyrm.social/book/37292", title="Test",
parent_work=work, remote_id="https://bookwyrm.social/book/37292",
) parent_work=work,
)
self.assertEqual(models.Notification.objects.count(), 0) self.assertEqual(models.Notification.objects.count(), 0)
activity = { activity = {
"type": "Announce", "type": "Announce",

View file

@ -12,27 +12,28 @@ class InboxBlock(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.local_user.remote_id = "https://example.com/user/mouse"
models.SiteSettings.objects.create() self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_handle_blocks(self): def test_handle_blocks(self):
"""create a "block" database entry from an activity""" """create a "block" database entry from an activity"""

View file

@ -15,15 +15,26 @@ class InboxCreate(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse" )
self.local_user.save(broadcast=False) self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
with patch("bookwyrm.activitystreams.ActivityStream.add_status"): with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
self.status = models.Status.objects.create( self.status = models.Status.objects.create(
@ -31,16 +42,6 @@ class InboxCreate(TestCase):
content="Test status", content="Test status",
remote_id="https://example.com/status/1", remote_id="https://example.com/status/1",
) )
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
self.create_json = { self.create_json = {
"id": "hi", "id": "hi",
@ -50,7 +51,8 @@ class InboxCreate(TestCase):
"cc": ["https://example.com/user/mouse/followers"], "cc": ["https://example.com/user/mouse/followers"],
"object": {}, "object": {},
} }
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_create_status(self): def test_create_status(self):
"""the "it justs works" mode""" """the "it justs works" mode"""
@ -60,9 +62,11 @@ class InboxCreate(TestCase):
"../../data/ap_quotation.json" "../../data/ap_quotation.json"
) )
status_data = json.loads(datafile.read_bytes()) status_data = json.loads(datafile.read_bytes())
models.Edition.objects.create(
title="Test Book", remote_id="https://example.com/book/1" with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
) models.Edition.objects.create(
title="Test Book", remote_id="https://example.com/book/1"
)
activity = self.create_json activity = self.create_json
activity["object"] = status_data activity["object"] = status_data
@ -129,9 +133,10 @@ class InboxCreate(TestCase):
def test_create_rating(self): def test_create_rating(self):
"""a remote rating activity""" """a remote rating activity"""
book = models.Edition.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test Book", remote_id="https://example.com/book/1" book = models.Edition.objects.create(
) title="Test Book", remote_id="https://example.com/book/1"
)
activity = self.create_json activity = self.create_json
activity["object"] = { activity["object"] = {
"id": "https://example.com/user/mouse/reviewrating/12", "id": "https://example.com/user/mouse/reviewrating/12",
@ -157,9 +162,12 @@ class InboxCreate(TestCase):
"rating": 3, "rating": 3,
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
} }
with patch("bookwyrm.activitystreams.ActivityStream.add_status") as redis_mock: with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
views.inbox.activity_task(activity) with patch(
self.assertTrue(redis_mock.called) "bookwyrm.activitystreams.ActivityStream.add_status"
) as redis_mock:
views.inbox.activity_task(activity)
self.assertTrue(redis_mock.called)
rating = models.ReviewRating.objects.first() rating = models.ReviewRating.objects.first()
self.assertEqual(rating.book, book) self.assertEqual(rating.book, book)
self.assertEqual(rating.rating, 3.0) self.assertEqual(rating.rating, 3.0)

View file

@ -13,25 +13,26 @@ class InboxActivities(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.activitystreams.ActivityStream.add_status"): with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
self.status = models.Status.objects.create( self.status = models.Status.objects.create(
user=self.remote_user, user=self.remote_user,
@ -47,7 +48,8 @@ class InboxActivities(TestCase):
"cc": ["https://example.com/user/mouse/followers"], "cc": ["https://example.com/user/mouse/followers"],
"object": {}, "object": {},
} }
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_delete_status(self): def test_delete_status(self):
"""remove a status""" """remove a status"""
@ -117,7 +119,8 @@ class InboxActivities(TestCase):
"object": self.remote_user.remote_id, "object": self.remote_user.remote_id,
} }
views.inbox.activity_task(activity) with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
views.inbox.activity_task(activity)
self.assertFalse(models.User.objects.get(username="rat@example.com").is_active) self.assertFalse(models.User.objects.get(username="rat@example.com").is_active)
def test_delete_user_unknown(self): def test_delete_user_unknown(self):

View file

@ -13,27 +13,29 @@ class InboxRelationships(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_follow(self): def test_follow(self):
"""remote user wants to follow local user""" """remote user wants to follow local user"""

View file

@ -12,25 +12,27 @@ class InboxActivities(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
with patch("bookwyrm.activitystreams.ActivityStream.add_status"): with patch("bookwyrm.activitystreams.ActivityStream.add_status"):
self.status = models.Status.objects.create( self.status = models.Status.objects.create(
@ -47,7 +49,9 @@ class InboxActivities(TestCase):
"cc": ["https://example.com/user/mouse/followers"], "cc": ["https://example.com/user/mouse/followers"],
"object": {}, "object": {},
} }
models.SiteSettings.objects.create()
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_handle_favorite(self): def test_handle_favorite(self):
"""fav a status""" """fav a status"""

View file

@ -12,33 +12,37 @@ class InboxRemove(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.work = models.Work.objects.create(title="work title") self.local_user.remote_id = "https://example.com/user/mouse"
self.book = models.Edition.objects.create( self.local_user.save(broadcast=False)
title="Test", with patch("bookwyrm.models.user.set_remote_server.delay"):
remote_id="https://bookwyrm.social/book/37292", self.remote_user = models.User.objects.create_user(
parent_work=self.work, "rat",
) "rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.work = models.Work.objects.create(title="work title")
self.book = models.Edition.objects.create(
title="Test",
remote_id="https://bookwyrm.social/book/37292",
parent_work=self.work,
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_handle_unshelve_book(self): def test_handle_unshelve_book(self):
"""remove a book from a shelf""" """remove a book from a shelf"""

View file

@ -14,25 +14,26 @@ class InboxUpdate(TestCase):
def setUp(self): def setUp(self):
"""basic user and book data""" """basic user and book data"""
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@example.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@example.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
) localname="mouse",
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
self.local_user.remote_id = "https://example.com/user/mouse"
self.local_user.save(broadcast=False)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
self.create_json = { self.create_json = {
"id": "hi", "id": "hi",
@ -42,7 +43,8 @@ class InboxUpdate(TestCase):
"cc": ["https://example.com/user/mouse/followers"], "cc": ["https://example.com/user/mouse/followers"],
"object": {}, "object": {},
} }
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_update_list(self): def test_update_list(self):
"""a new list""" """a new list"""
@ -96,16 +98,18 @@ class InboxUpdate(TestCase):
del userdata["icon"] del userdata["icon"]
self.assertIsNone(self.remote_user.name) self.assertIsNone(self.remote_user.name)
self.assertFalse(self.remote_user.discoverable) self.assertFalse(self.remote_user.discoverable)
views.inbox.activity_task(
{ with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"type": "Update", views.inbox.activity_task(
"to": [], {
"cc": [], "type": "Update",
"actor": "hi", "to": [],
"id": "sdkjf", "cc": [],
"object": userdata, "actor": "hi",
} "id": "sdkjf",
) "object": userdata,
}
)
user = models.User.objects.get(id=self.remote_user.id) user = models.User.objects.get(id=self.remote_user.id)
self.assertEqual(user.name, "RAT???") self.assertEqual(user.name, "RAT???")
self.assertEqual(user.username, "rat@example.com") self.assertEqual(user.username, "rat@example.com")
@ -120,27 +124,28 @@ class InboxUpdate(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_edition.json") datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_edition.json")
bookdata = json.loads(datafile.read_bytes()) bookdata = json.loads(datafile.read_bytes())
models.Work.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test Work", remote_id="https://bookwyrm.social/book/5988" models.Work.objects.create(
) title="Test Work", remote_id="https://bookwyrm.social/book/5988"
book = models.Edition.objects.create(
title="Test Book", remote_id="https://bookwyrm.social/book/5989"
)
del bookdata["authors"]
self.assertEqual(book.title, "Test Book")
with patch("bookwyrm.activitypub.base_activity.set_related_field.delay"):
views.inbox.activity_task(
{
"type": "Update",
"to": [],
"cc": [],
"actor": "hi",
"id": "sdkjf",
"object": bookdata,
}
) )
book = models.Edition.objects.create(
title="Test Book", remote_id="https://bookwyrm.social/book/5989"
)
del bookdata["authors"]
self.assertEqual(book.title, "Test Book")
with patch("bookwyrm.activitypub.base_activity.set_related_field.delay"):
views.inbox.activity_task(
{
"type": "Update",
"to": [],
"cc": [],
"actor": "hi",
"id": "sdkjf",
"object": bookdata,
}
)
book = models.Edition.objects.get(id=book.id) book = models.Edition.objects.get(id=book.id)
self.assertEqual(book.title, "Piranesi") self.assertEqual(book.title, "Piranesi")
self.assertEqual(book.last_edited_by, self.remote_user) self.assertEqual(book.last_edited_by, self.remote_user)
@ -150,9 +155,10 @@ class InboxUpdate(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_work.json") datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_work.json")
bookdata = json.loads(datafile.read_bytes()) bookdata = json.loads(datafile.read_bytes())
book = models.Work.objects.create( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Test Book", remote_id="https://bookwyrm.social/book/5988" book = models.Work.objects.create(
) title="Test Book", remote_id="https://bookwyrm.social/book/5988"
)
del bookdata["authors"] del bookdata["authors"]
self.assertEqual(book.title, "Test Book") self.assertEqual(book.title, "Test Book")

View file

@ -2,6 +2,7 @@
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
from unittest.mock import patch
from bookwyrm import forms, models, views from bookwyrm import forms, models, views
@ -12,14 +13,16 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
models.SiteSettings.objects.create() )
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_announcements_page(self): def test_announcements_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 file

@ -19,16 +19,18 @@ class AuthenticationViews(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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"password", "mouse@mouse.com",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
)
self.anonymous_user = AnonymousUser self.anonymous_user = AnonymousUser
self.anonymous_user.is_authenticated = False self.anonymous_user.is_authenticated = False
self.settings = models.SiteSettings.objects.create(id=1) with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
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"""
@ -58,8 +60,9 @@ class AuthenticationViews(TestCase):
"email": "aa@bb.cccc", "email": "aa@bb.cccc",
}, },
) )
with patch("bookwyrm.views.authentication.login"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
response = view(request) with patch("bookwyrm.views.authentication.login"):
response = view(request)
self.assertEqual(models.User.objects.count(), 2) self.assertEqual(models.User.objects.count(), 2)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
nutria = models.User.objects.last() nutria = models.User.objects.last()
@ -74,8 +77,9 @@ class AuthenticationViews(TestCase):
"register/", "register/",
{"localname": "nutria ", "password": "mouseword", "email": "aa@bb.ccc"}, {"localname": "nutria ", "password": "mouseword", "email": "aa@bb.ccc"},
) )
with patch("bookwyrm.views.authentication.login"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
response = view(request) with patch("bookwyrm.views.authentication.login"):
response = view(request)
self.assertEqual(models.User.objects.count(), 2) self.assertEqual(models.User.objects.count(), 2)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
nutria = models.User.objects.last() nutria = models.User.objects.last()
@ -153,8 +157,9 @@ class AuthenticationViews(TestCase):
"invite_code": "testcode", "invite_code": "testcode",
}, },
) )
with patch("bookwyrm.views.authentication.login"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
response = view(request) with patch("bookwyrm.views.authentication.login"):
response = view(request)
self.assertEqual(models.User.objects.count(), 2) self.assertEqual(models.User.objects.count(), 2)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(models.SiteInvite.objects.get().times_used, 1) self.assertEqual(models.SiteInvite.objects.get().times_used, 1)

View file

@ -17,14 +17,15 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
remote_id="https://example.com/users/mouse", localname="mouse",
) remote_id="https://example.com/users/mouse",
)
self.group = Group.objects.create(name="editor") self.group = Group.objects.create(name="editor")
self.group.permissions.add( self.group.permissions.add(
Permission.objects.create( Permission.objects.create(
@ -33,13 +34,15 @@ class AuthorViews(TestCase):
content_type=ContentType.objects.get_for_model(models.User), content_type=ContentType.objects.get_for_model(models.User),
).id ).id
) )
self.work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create( self.work = models.Work.objects.create(title="Test Work")
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", title="Example Edition",
parent_work=self.work, remote_id="https://example.com/book/1",
) parent_work=self.work,
models.SiteSettings.objects.create() )
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_author_page(self): def test_author_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 file

@ -14,24 +14,26 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
models.SiteSettings.objects.create() with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_block_get(self, _): def test_block_get(self, _):
"""there are so many views, this just makes sure it LOADS""" """there are so many views, this just makes sure it LOADS"""

View file

@ -23,14 +23,15 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
remote_id="https://example.com/users/mouse", localname="mouse",
) remote_id="https://example.com/users/mouse",
)
self.group = Group.objects.create(name="editor") self.group = Group.objects.create(name="editor")
self.group.permissions.add( self.group.permissions.add(
Permission.objects.create( Permission.objects.create(
@ -39,13 +40,15 @@ class BookViews(TestCase):
content_type=ContentType.objects.get_for_model(models.User), content_type=ContentType.objects.get_for_model(models.User),
).id ).id
) )
self.work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create( self.work = models.Work.objects.create(title="Test Work")
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", title="Example Edition",
parent_work=self.work, remote_id="https://example.com/book/1",
) parent_work=self.work,
models.SiteSettings.objects.create() )
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_book_page(self): def test_book_page(self):
"""there are so many views, this just makes sure it LOADS""" """there are so many views, this just makes sure it LOADS"""
@ -71,7 +74,8 @@ class BookViews(TestCase):
request = self.factory.get("") request = self.factory.get("")
request.user = self.local_user request.user = self.local_user
request.user.is_superuser = True request.user.is_superuser = True
result = view(request, self.book.id) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
result = view(request, self.book.id)
self.assertIsInstance(result, TemplateResponse) self.assertIsInstance(result, TemplateResponse)
result.render() result.render()
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
@ -85,8 +89,11 @@ class BookViews(TestCase):
form.data["last_edited_by"] = self.local_user.id form.data["last_edited_by"] = self.local_user.id
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
view(request, self.book.id) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
view(request, self.book.id)
self.book.refresh_from_db() self.book.refresh_from_db()
self.assertEqual(self.book.title, "New Title") self.assertEqual(self.book.title, "New Title")
@ -101,8 +108,9 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
result = view(request, self.book.id) with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
result.render() result = view(request, self.book.id)
result.render()
# the changes haven't been saved yet # the changes haven't been saved yet
self.book.refresh_from_db() self.book.refresh_from_db()
@ -120,8 +128,9 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
view(request, self.book.id) with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
view(request, self.book.id)
self.book.refresh_from_db() self.book.refresh_from_db()
self.assertEqual(self.book.title, "New Title") self.assertEqual(self.book.title, "New Title")
@ -141,8 +150,9 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
view(request, self.book.id) with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
view(request, self.book.id)
self.book.refresh_from_db() self.book.refresh_from_db()
self.assertEqual(self.book.title, "New Title") self.assertEqual(self.book.title, "New Title")
self.assertFalse(self.book.authors.exists()) self.assertFalse(self.book.authors.exists())
@ -157,7 +167,8 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
view(request) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
view(request)
book = models.Edition.objects.get(title="New Title") book = models.Edition.objects.get(title="New Title")
self.assertEqual(book.parent_work.title, "New Title") self.assertEqual(book.parent_work.title, "New Title")
@ -172,7 +183,8 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
view(request) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
view(request)
book = models.Edition.objects.get(title="New Title") book = models.Edition.objects.get(title="New Title")
self.assertEqual(book.parent_work, self.work) self.assertEqual(book.parent_work, self.work)
@ -188,7 +200,8 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
view(request) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
view(request)
book = models.Edition.objects.get(title="New Title") book = models.Edition.objects.get(title="New Title")
self.assertEqual(book.parent_work.title, "New Title") self.assertEqual(book.parent_work.title, "New Title")
self.assertEqual(book.authors.first().name, "Sappho") self.assertEqual(book.authors.first().name, "Sappho")
@ -197,8 +210,11 @@ class BookViews(TestCase):
def test_switch_edition(self): 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) with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
edition2 = models.Edition.objects.create(title="second ed", parent_work=work) edition1 = models.Edition.objects.create(title="first ed", parent_work=work)
edition2 = models.Edition.objects.create(
title="second ed", parent_work=work
)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user) shelf = models.Shelf.objects.create(name="Test Shelf", user=self.local_user)
models.ShelfBook.objects.create( models.ShelfBook.objects.create(
@ -251,11 +267,12 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
with patch( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
"bookwyrm.models.activitypub_mixin.broadcast_task.delay" with patch(
) as delay_mock: "bookwyrm.models.activitypub_mixin.broadcast_task.delay"
views.upload_cover(request, self.book.id) ) as delay_mock:
self.assertEqual(delay_mock.call_count, 1) views.upload_cover(request, self.book.id)
self.assertEqual(delay_mock.call_count, 1)
self.book.refresh_from_db() self.book.refresh_from_db()
self.assertTrue(self.book.cover) self.assertTrue(self.book.cover)
@ -283,11 +300,12 @@ class BookViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
with patch( with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
"bookwyrm.models.activitypub_mixin.broadcast_task.delay" with patch(
) as delay_mock: "bookwyrm.models.activitypub_mixin.broadcast_task.delay"
views.upload_cover(request, self.book.id) ) as delay_mock:
self.assertEqual(delay_mock.call_count, 1) views.upload_cover(request, self.book.id)
self.assertEqual(delay_mock.call_count, 1)
self.book.refresh_from_db() self.book.refresh_from_db()
self.assertTrue(self.book.cover) self.assertTrue(self.book.cover)

View file

@ -2,6 +2,7 @@
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
from unittest.mock import patch
from bookwyrm import models, views from bookwyrm import models, views
@ -12,24 +13,26 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
remote_id="https://example.com/users/mouse", localname="mouse",
) remote_id="https://example.com/users/mouse",
self.rat = models.User.objects.create_user( )
"rat@local.com", self.rat = models.User.objects.create_user(
"rat@rat.com", "rat@local.com",
"ratword", "rat@rat.com",
local=True, "ratword",
localname="rat", local=True,
remote_id="https://example.com/users/rat", localname="rat",
discoverable=True, remote_id="https://example.com/users/rat",
) discoverable=True,
models.SiteSettings.objects.create() )
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_directory_page(self): def test_directory_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 file

@ -21,25 +21,29 @@ class EditUserViews(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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
self.rat = models.User.objects.create_user( )
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat" self.rat = models.User.objects.create_user(
) "rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
self.book = models.Edition.objects.create(title="test")
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
models.ShelfBook.objects.create(
book=self.book,
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
) )
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create(title="test")
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
models.ShelfBook.objects.create(
book=self.book,
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
self.anonymous_user = AnonymousUser self.anonymous_user = AnonymousUser
self.anonymous_user.is_authenticated = False self.anonymous_user.is_authenticated = False
@ -64,11 +68,12 @@ class EditUserViews(TestCase):
request.user = self.local_user request.user = self.local_user
self.assertIsNone(self.local_user.name) self.assertIsNone(self.local_user.name)
with patch( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"bookwyrm.models.activitypub_mixin.broadcast_task.delay" with patch(
) as delay_mock: "bookwyrm.models.activitypub_mixin.broadcast_task.delay"
view(request) ) as delay_mock:
self.assertEqual(delay_mock.call_count, 1) view(request)
self.assertEqual(delay_mock.call_count, 1)
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")
@ -88,11 +93,12 @@ class EditUserViews(TestCase):
request = self.factory.post("", form.data) request = self.factory.post("", form.data)
request.user = self.local_user request.user = self.local_user
with patch( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"bookwyrm.models.activitypub_mixin.broadcast_task.delay" with patch(
) as delay_mock: "bookwyrm.models.activitypub_mixin.broadcast_task.delay"
view(request) ) as delay_mock:
self.assertEqual(delay_mock.call_count, 1) view(request)
self.assertEqual(delay_mock.call_count, 1)
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")
self.assertIsNotNone(self.local_user.avatar) self.assertIsNotNone(self.local_user.avatar)

View file

@ -15,24 +15,26 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
models.SiteSettings.objects.create() with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_federation_page(self): def test_federation_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 file

@ -22,19 +22,22 @@ class FeedViews(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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
self.book = models.Edition.objects.create( )
parent_work=models.Work.objects.create(title="hi"), with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", parent_work=models.Work.objects.create(title="hi"),
) title="Example Edition",
models.SiteSettings.objects.create() remote_id="https://example.com/book/1",
)
with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
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"""
@ -89,11 +92,14 @@ class FeedViews(TestCase):
output = BytesIO() output = BytesIO()
image.save(output, format=image.format) image.save(output, format=image.format)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
status = models.Review.objects.create( with patch(
content="hi", "bookwyrm.preview_images.generate_edition_preview_image_task.delay"
user=self.local_user, ):
book=self.book, status = models.Review.objects.create(
) content="hi",
user=self.local_user,
book=self.book,
)
attachment = models.Image.objects.create( attachment = models.Image.objects.create(
status=status, caption="alt text here" status=status, caption="alt text here"
) )
@ -146,12 +152,13 @@ class FeedViews(TestCase):
def test_get_suggested_book(self, *_): def test_get_suggested_book(self, *_):
"""gets books the ~*~ algorithm ~*~ thinks you want to post about""" """gets books the ~*~ algorithm ~*~ thinks you want to post about"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
models.ShelfBook.objects.create( with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
book=self.book, models.ShelfBook.objects.create(
user=self.local_user, book=self.book,
shelf=self.local_user.shelf_set.get(identifier="reading"), user=self.local_user,
) shelf=self.local_user.shelf_set.get(identifier="reading"),
)
suggestions = views.feed.get_suggested_books(self.local_user) suggestions = views.feed.get_suggested_books(self.local_user)
self.assertEqual(suggestions[0]["name"], "Currently Reading") self.assertEqual(suggestions[0]["name"], "Currently Reading")
self.assertEqual(suggestions[0]["books"][0], self.book) self.assertEqual(suggestions[0]["books"][0], self.book)

View file

@ -16,24 +16,25 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
remote_id="https://example.com/users/mouse", localname="mouse",
) remote_id="https://example.com/users/mouse",
with patch("bookwyrm.models.user.set_remote_server"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@email.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
) )
with patch("bookwyrm.models.user.set_remote_server"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@email.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
self.group = Group.objects.create(name="editor") self.group = Group.objects.create(name="editor")
self.group.permissions.add( self.group.permissions.add(
Permission.objects.create( Permission.objects.create(
@ -42,12 +43,13 @@ class BookViews(TestCase):
content_type=ContentType.objects.get_for_model(models.User), content_type=ContentType.objects.get_for_model(models.User),
).id ).id
) )
self.work = models.Work.objects.create(title="Test Work") with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.book = models.Edition.objects.create( self.work = models.Work.objects.create(title="Test Work")
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", title="Example Edition",
parent_work=self.work, remote_id="https://example.com/book/1",
) parent_work=self.work,
)
def test_handle_follow_remote(self): def test_handle_follow_remote(self):
"""send a follow request""" """send a follow request"""
@ -66,15 +68,16 @@ 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"""
rat = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"rat@local.com", rat = models.User.objects.create_user(
"rat@rat.com", "rat@local.com",
"ratword", "rat@rat.com",
local=True, "ratword",
localname="rat", local=True,
remote_id="https://example.com/users/rat", localname="rat",
manually_approves_followers=True, remote_id="https://example.com/users/rat",
) manually_approves_followers=True,
)
request = self.factory.post("", {"user": rat}) request = self.factory.post("", {"user": rat})
request.user = self.local_user request.user = self.local_user
self.assertEqual(models.UserFollowRequest.objects.count(), 0) self.assertEqual(models.UserFollowRequest.objects.count(), 0)
@ -89,14 +92,15 @@ class BookViews(TestCase):
def test_handle_follow_local(self): def test_handle_follow_local(self):
"""send a follow request""" """send a follow request"""
rat = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"rat@local.com", rat = models.User.objects.create_user(
"rat@rat.com", "rat@local.com",
"ratword", "rat@rat.com",
local=True, "ratword",
localname="rat", local=True,
remote_id="https://example.com/users/rat", localname="rat",
) remote_id="https://example.com/users/rat",
)
request = self.factory.post("", {"user": rat}) request = self.factory.post("", {"user": rat})
request.user = self.local_user request.user = self.local_user
self.assertEqual(models.UserFollowRequest.objects.count(), 0) self.assertEqual(models.UserFollowRequest.objects.count(), 0)

View file

@ -13,22 +13,25 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.mouse", "mouse@local.com",
"password", "mouse@mouse.mouse",
local=True, "password",
localname="mouse", local=True,
) localname="mouse",
self.book = models.Edition.objects.create( )
parent_work=models.Work.objects.create(title="hi"), with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
title="Example Edition", self.book = models.Edition.objects.create(
remote_id="https://example.com/book/1", parent_work=models.Work.objects.create(title="hi"),
) title="Example Edition",
remote_id="https://example.com/book/1",
)
models.Connector.objects.create( models.Connector.objects.create(
identifier="self", connector_file="self_connector", local=True identifier="self", connector_file="self_connector", local=True
) )
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_profile_view(self): def test_profile_view(self):
"""there are so many views, this just makes sure it LOADS""" """there are so many views, this just makes sure it LOADS"""
@ -52,11 +55,12 @@ class GetStartedViews(TestCase):
request.user = self.local_user request.user = self.local_user
self.assertIsNone(self.local_user.name) self.assertIsNone(self.local_user.name)
with patch( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"bookwyrm.models.activitypub_mixin.broadcast_task.delay" with patch(
) as delay_mock: "bookwyrm.models.activitypub_mixin.broadcast_task.delay"
view(request) ) as delay_mock:
self.assertEqual(delay_mock.call_count, 1) view(request)
self.assertEqual(delay_mock.call_count, 1)
self.assertEqual(self.local_user.name, "New Name") self.assertEqual(self.local_user.name, "New Name")
self.assertTrue(self.local_user.discoverable) self.assertTrue(self.local_user.discoverable)

View file

@ -16,29 +16,32 @@ 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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
localname="mouse", local=True,
remote_id="https://example.com/users/mouse", localname="mouse",
) remote_id="https://example.com/users/mouse",
self.rat = models.User.objects.create_user( )
"rat@local.com", self.rat = models.User.objects.create_user(
"rat@rat.com", "rat@local.com",
"ratword", "rat@rat.com",
local=True, "ratword",
localname="rat", local=True,
remote_id="https://example.com/users/rat", localname="rat",
) remote_id="https://example.com/users/rat",
self.book = models.Edition.objects.create( )
title="Example Edition", with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
remote_id="https://example.com/book/1", self.book = models.Edition.objects.create(
) title="Example Edition",
remote_id="https://example.com/book/1",
)
self.anonymous_user = AnonymousUser self.anonymous_user = AnonymousUser
self.anonymous_user.is_authenticated = False self.anonymous_user.is_authenticated = False
models.SiteSettings.objects.create() with patch("bookwyrm.preview_images.generate_site_preview_image_task.delay"):
models.SiteSettings.objects.create()
def test_goal_page_no_goal(self): def test_goal_page_no_goal(self):
"""view a reading goal page for another's unset goal""" """view a reading goal page for another's unset goal"""

View file

@ -19,31 +19,33 @@ class ViewsHelpers(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()
self.local_user = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"mouse@local.com", self.local_user = models.User.objects.create_user(
"mouse@mouse.com", "mouse@local.com",
"mouseword", "mouse@mouse.com",
local=True, "mouseword",
discoverable=True, local=True,
localname="mouse",
remote_id="https://example.com/users/mouse",
)
self.work = models.Work.objects.create(title="Test Work")
self.book = models.Edition.objects.create(
title="Test Book",
remote_id="https://example.com/book/1",
parent_work=self.work,
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
discoverable=True, discoverable=True,
inbox="https://example.com/users/rat/inbox", localname="mouse",
outbox="https://example.com/users/rat/outbox", remote_id="https://example.com/users/mouse",
)
with patch("bookwyrm.models.user.set_remote_server.delay"):
self.remote_user = models.User.objects.create_user(
"rat",
"rat@rat.com",
"ratword",
local=False,
remote_id="https://example.com/users/rat",
discoverable=True,
inbox="https://example.com/users/rat/inbox",
outbox="https://example.com/users/rat/outbox",
)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
self.work = models.Work.objects.create(title="Test Work")
self.book = models.Edition.objects.create(
title="Test Book",
remote_id="https://example.com/book/1",
parent_work=self.work,
) )
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())
@ -142,10 +144,11 @@ class ViewsHelpers(TestCase):
json=self.userdata, json=self.userdata,
status=200, status=200,
) )
with patch("bookwyrm.models.user.set_remote_server.delay"): with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
result = views.helpers.handle_remote_webfinger("@mouse@example.com") with patch("bookwyrm.models.user.set_remote_server.delay"):
self.assertIsInstance(result, models.User) result = views.helpers.handle_remote_webfinger("@mouse@example.com")
self.assertEqual(result.username, "mouse@example.com") self.assertIsInstance(result, models.User)
self.assertEqual(result.username, "mouse@example.com")
def test_user_on_blocked_server(self, _): def test_user_on_blocked_server(self, _):
"""find a remote user using webfinger""" """find a remote user using webfinger"""
@ -202,21 +205,22 @@ class ViewsHelpers(TestCase):
def test_get_annotated_users(self, _): def test_get_annotated_users(self, _):
"""list of people you might know""" """list of people you might know"""
user_1 = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"nutria@local.com", user_1 = models.User.objects.create_user(
"nutria@nutria.com", "nutria@local.com",
"nutriaword", "nutria@nutria.com",
local=True, "nutriaword",
localname="nutria", local=True,
discoverable=True, localname="nutria",
) discoverable=True,
user_2 = models.User.objects.create_user( )
"fish@local.com", user_2 = models.User.objects.create_user(
"fish@fish.com", "fish@local.com",
"fishword", "fish@fish.com",
local=True, "fishword",
localname="fish", local=True,
) localname="fish",
)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
# 1 shared follow # 1 shared follow
self.local_user.following.add(user_2) self.local_user.following.add(user_2)
@ -249,39 +253,42 @@ class ViewsHelpers(TestCase):
def test_get_annotated_users_counts(self, _): def test_get_annotated_users_counts(self, _):
"""correct counting for multiple shared attributed""" """correct counting for multiple shared attributed"""
user_1 = models.User.objects.create_user( with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
"nutria@local.com", user_1 = models.User.objects.create_user(
"nutria@nutria.com", "nutria@local.com",
"nutriaword", "nutria@nutria.com",
local=True, "nutriaword",
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, local=True,
localname=i, localname="nutria",
discoverable=True,
) )
user.following.add(user_1) with patch("bookwyrm.preview_images.generate_user_preview_image_task.delay"):
user.followers.add(self.local_user)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
for i in range(3): for i in range(3):
book = models.Edition.objects.create( user = models.User.objects.create_user(
title=i, "{:d}@local.com".format(i),
parent_work=models.Work.objects.create(title=i), "{:d}@nutria.com".format(i),
) "password",
models.ShelfBook.objects.create( local=True,
user=self.local_user, localname=i,
book=book,
shelf=self.local_user.shelf_set.first(),
)
models.ShelfBook.objects.create(
user=user_1, book=book, shelf=user_1.shelf_set.first()
) )
user.following.add(user_1)
user.followers.add(self.local_user)
with patch("bookwyrm.preview_images.generate_edition_preview_image_task.delay"):
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( result = views.helpers.get_annotated_users(
self.local_user, self.local_user,

Some files were not shown because too many files have changed in this diff Show more