Attachments
This commit is contained in:
parent
421b441dbe
commit
c84396e669
50 changed files with 880 additions and 262 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue