Attachments

This commit is contained in:
Eliot Berriot 2019-11-25 09:49:06 +01:00
commit c84396e669
50 changed files with 880 additions and 262 deletions

View file

@ -46,3 +46,28 @@ def test_get_moderation_url(factory_name, factories, expected):
obj = factories[factory_name]()
assert obj.get_moderation_url() == expected.format(obj=obj)
def test_attachment(factories, now):
attachment = factories["common.Attachment"]()
assert attachment.uuid is not None
assert attachment.mimetype == "image/jpeg"
assert attachment.file is not None
assert attachment.url is not None
assert attachment.actor is not None
assert attachment.creation_date > now
assert attachment.last_fetch_date is None
assert attachment.size > 0
@pytest.mark.parametrize("args, expected", [([], [0]), ([True], [0]), ([False], [1])])
def test_attachment_queryset_attached(args, expected, factories, queryset_equal_list):
attachments = [
factories["music.Album"]().attachment_cover,
factories["common.Attachment"](),
]
queryset = attachments[0].__class__.objects.attached(*args).order_by("id")
expected_objs = [attachments[i] for i in expected]
assert queryset == expected_objs

View file

@ -2,11 +2,13 @@ import os
import PIL
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
import django_filters
from funkwhale_api.common import serializers
from funkwhale_api.users import models
from funkwhale_api.federation import utils as federation_utils
class TestActionFilterSet(django_filters.FilterSet):
@ -182,3 +184,71 @@ def test_strip_exif_field():
cleaned = PIL.Image.open(field.to_internal_value(uploaded))
assert cleaned._getexif() is None
def test_attachment_serializer_existing_file(factories, to_api_date):
attachment = factories["common.Attachment"]()
expected = {
"uuid": str(attachment.uuid),
"size": attachment.size,
"mimetype": attachment.mimetype,
"creation_date": to_api_date(attachment.creation_date),
"urls": {
"source": attachment.url,
"original": federation_utils.full_url(attachment.file.url),
"medium_square_crop": federation_utils.full_url(
attachment.file.crop["200x200"].url
),
},
# XXX: BACKWARD COMPATIBILITY
"original": federation_utils.full_url(attachment.file.url),
"medium_square_crop": federation_utils.full_url(
attachment.file.crop["200x200"].url
),
"small_square_crop": federation_utils.full_url(
attachment.file.crop["200x200"].url
),
"square_crop": federation_utils.full_url(attachment.file.crop["200x200"].url),
}
serializer = serializers.AttachmentSerializer(attachment)
assert serializer.data == expected
def test_attachment_serializer_remote_file(factories, to_api_date):
attachment = factories["common.Attachment"](file=None)
proxy_url = reverse("api:v1:attachments-proxy", kwargs={"uuid": attachment.uuid})
expected = {
"uuid": str(attachment.uuid),
"size": attachment.size,
"mimetype": attachment.mimetype,
"creation_date": to_api_date(attachment.creation_date),
# everything is the same, except for the urls field because:
# - the file isn't available on the local pod
# - we need to return different URLs so that the client can trigger
# a fetch and get redirected to the desired version
#
"urls": {
"source": attachment.url,
"original": federation_utils.full_url(proxy_url + "?next=original"),
"medium_square_crop": federation_utils.full_url(
proxy_url + "?next=medium_square_crop"
),
},
# XXX: BACKWARD COMPATIBILITY
"original": federation_utils.full_url(proxy_url + "?next=original"),
"medium_square_crop": federation_utils.full_url(
proxy_url + "?next=medium_square_crop"
),
"square_crop": federation_utils.full_url(
proxy_url + "?next=medium_square_crop"
),
"small_square_crop": federation_utils.full_url(
proxy_url + "?next=medium_square_crop"
),
}
serializer = serializers.AttachmentSerializer(attachment)
assert serializer.data == expected

View file

@ -1,4 +1,5 @@
import pytest
import datetime
from funkwhale_api.common import serializers
from funkwhale_api.common import signals
@ -63,3 +64,25 @@ def test_cannot_apply_already_applied_migration(factories):
mutation = factories["common.Mutation"](payload={}, is_applied=True)
with pytest.raises(mutation.__class__.DoesNotExist):
tasks.apply_mutation(mutation_id=mutation.pk)
def test_prune_unattached_attachments(factories, settings, now):
settings.ATTACHMENTS_UNATTACHED_PRUNE_DELAY = 5
attachments = [
# attached, kept
factories["music.Album"]().attachment_cover,
# recent, kept
factories["common.Attachment"](),
# too old, pruned
factories["common.Attachment"](
creation_date=now
- datetime.timedelta(seconds=settings.ATTACHMENTS_UNATTACHED_PRUNE_DELAY)
),
]
tasks.prune_unattached_attachments()
attachments[0].refresh_from_db()
attachments[1].refresh_from_db()
with pytest.raises(attachments[2].DoesNotExist):
attachments[2].refresh_from_db()

View file

@ -1,4 +1,6 @@
import io
import pytest
from django.urls import reverse
from funkwhale_api.common import serializers
@ -181,3 +183,69 @@ def test_rate_limit(logged_in_api_client, now_time, settings, mocker):
assert response.status_code == 200
assert response.data == expected
get_status.assert_called_once_with(expected_ident, now_time)
@pytest.mark.parametrize(
"next, expected",
[
("original", "original"),
("medium_square_crop", "medium_square_crop"),
("unknown", "original"),
],
)
def test_attachment_proxy_redirects_original(
next, expected, factories, logged_in_api_client, mocker, avatar, r_mock, now
):
attachment = factories["common.Attachment"](file=None)
avatar_content = avatar.read()
fetch_remote_attachment = mocker.spy(tasks, "fetch_remote_attachment")
m = r_mock.get(attachment.url, body=io.BytesIO(avatar_content))
proxy_url = reverse("api:v1:attachments-proxy", kwargs={"uuid": attachment.uuid})
response = logged_in_api_client.get(proxy_url, {"next": next})
attachment.refresh_from_db()
urls = serializers.AttachmentSerializer(attachment).data["urls"]
assert attachment.file.read() == avatar_content
assert attachment.last_fetch_date == now
fetch_remote_attachment.assert_called_once_with(attachment)
assert len(m.request_history) == 1
assert response.status_code == 302
assert response["Location"] == urls[expected]
def test_attachment_create(logged_in_api_client, avatar):
actor = logged_in_api_client.user.create_actor()
url = reverse("api:v1:attachments-list")
content = avatar.read()
avatar.seek(0)
payload = {"file": avatar}
response = logged_in_api_client.post(url, payload)
assert response.status_code == 201
attachment = actor.attachments.latest("id")
assert attachment.file.read() == content
assert attachment.file.size == len(content)
def test_attachment_destroy(factories, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
attachment = factories["common.Attachment"](actor=actor)
url = reverse("api:v1:attachments-detail", kwargs={"uuid": attachment.uuid})
response = logged_in_api_client.delete(url)
assert response.status_code == 204
with pytest.raises(attachment.DoesNotExist):
attachment.refresh_from_db()
def test_attachment_destroy_not_owner(factories, logged_in_api_client):
logged_in_api_client.user.create_actor()
attachment = factories["common.Attachment"]()
url = reverse("api:v1:attachments-detail", kwargs={"uuid": attachment.uuid})
response = logged_in_api_client.delete(url)
assert response.status_code == 403
attachment.refresh_from_db()