kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
362 wiersze
14 KiB
Python
362 wiersze
14 KiB
Python
![]() |
import pytest
|
||
|
|
||
|
from rest_framework import serializers
|
||
|
|
||
|
from funkwhale_api.federation import contexts
|
||
|
from funkwhale_api.federation import jsonld
|
||
|
|
||
|
|
||
|
def test_expand_no_external_request():
|
||
|
payload = {
|
||
|
"id": "https://noop/federation/actors/demo",
|
||
|
"outbox": "https://noop/federation/actors/demo/outbox",
|
||
|
"inbox": "https://noop/federation/actors/demo/inbox",
|
||
|
"preferredUsername": "demo",
|
||
|
"type": "Person",
|
||
|
"name": "demo",
|
||
|
"followers": "https://noop/federation/actors/demo/followers",
|
||
|
"following": "https://noop/federation/actors/demo/following",
|
||
|
"manuallyApprovesFollowers": False,
|
||
|
"@context": [
|
||
|
"https://www.w3.org/ns/activitystreams",
|
||
|
"https://w3id.org/security/v1",
|
||
|
{},
|
||
|
],
|
||
|
"publicKey": {
|
||
|
"owner": "https://noop/federation/actors/demo",
|
||
|
"publicKeyPem": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxPDd/oXx0ClJ2BuBZ937AiERjvoroEpNebg34Cdl6FYsb2Auib8b\nCQjdjLjK/1ag35lmqmsECqtoDYWOo4tGilZJW47TWmXfcvCMH2Sw9FqdOlzpV1RI\nm8kc0Lu1CC2xOTctqIwSH7kDDnS4+S5hSxRdMTeNQNoirncY1CXa9TmJR1lE2HWz\n+B05ewzMrSen3l3fJLQFoI2GVbbjj+tvILKBL1oG5MtYieYqjt2sqtqy/OpWUAC7\nlRERRzd4t5xPBKykWkBCAOh80pvPue5V4s+xUMr7ioKTcm6pq+pNBta5w0hUYIcT\nMefQOnNuR4J0meIqiDLcrglGAmM6AVFwYwIDAQAB\n-----END RSA PUBLIC KEY-----\n", # noqa
|
||
|
"id": "https://noop/federation/actors/demo#main-key",
|
||
|
},
|
||
|
"endpoints": {"sharedInbox": "https://noop/federation/shared/inbox"},
|
||
|
}
|
||
|
|
||
|
expected = {
|
||
|
contexts.AS.endpoints: [
|
||
|
{contexts.AS.sharedInbox: [{"@id": "https://noop/federation/shared/inbox"}]}
|
||
|
],
|
||
|
contexts.AS.followers: [
|
||
|
{"@id": "https://noop/federation/actors/demo/followers"}
|
||
|
],
|
||
|
contexts.AS.following: [
|
||
|
{"@id": "https://noop/federation/actors/demo/following"}
|
||
|
],
|
||
|
"@id": "https://noop/federation/actors/demo",
|
||
|
"http://www.w3.org/ns/ldp#inbox": [
|
||
|
{"@id": "https://noop/federation/actors/demo/inbox"}
|
||
|
],
|
||
|
contexts.AS.manuallyApprovesFollowers: [{"@value": False}],
|
||
|
contexts.AS.name: [{"@value": "demo"}],
|
||
|
contexts.AS.outbox: [{"@id": "https://noop/federation/actors/demo/outbox"}],
|
||
|
contexts.AS.preferredUsername: [{"@value": "demo"}],
|
||
|
contexts.SEC.publicKey: [
|
||
|
{
|
||
|
"@id": "https://noop/federation/actors/demo#main-key",
|
||
|
contexts.SEC.owner: [{"@id": "https://noop/federation/actors/demo"}],
|
||
|
contexts.SEC.publicKeyPem: [
|
||
|
{
|
||
|
"@value": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxPDd/oXx0ClJ2BuBZ937AiERjvoroEpNebg34Cdl6FYsb2Auib8b\nCQjdjLjK/1ag35lmqmsECqtoDYWOo4tGilZJW47TWmXfcvCMH2Sw9FqdOlzpV1RI\nm8kc0Lu1CC2xOTctqIwSH7kDDnS4+S5hSxRdMTeNQNoirncY1CXa9TmJR1lE2HWz\n+B05ewzMrSen3l3fJLQFoI2GVbbjj+tvILKBL1oG5MtYieYqjt2sqtqy/OpWUAC7\nlRERRzd4t5xPBKykWkBCAOh80pvPue5V4s+xUMr7ioKTcm6pq+pNBta5w0hUYIcT\nMefQOnNuR4J0meIqiDLcrglGAmM6AVFwYwIDAQAB\n-----END RSA PUBLIC KEY-----\n" # noqa
|
||
|
}
|
||
|
],
|
||
|
}
|
||
|
],
|
||
|
"@type": [contexts.AS.Person],
|
||
|
}
|
||
|
|
||
|
doc = jsonld.expand(payload)
|
||
|
|
||
|
assert doc == expected
|
||
|
|
||
|
|
||
|
def test_expand_remote_doc(r_mock):
|
||
|
url = "https://noop/federation/actors/demo"
|
||
|
payload = {
|
||
|
"id": url,
|
||
|
"outbox": "https://noop/federation/actors/demo/outbox",
|
||
|
"inbox": "https://noop/federation/actors/demo/inbox",
|
||
|
"preferredUsername": "demo",
|
||
|
"type": "Person",
|
||
|
"name": "demo",
|
||
|
"followers": "https://noop/federation/actors/demo/followers",
|
||
|
"following": "https://noop/federation/actors/demo/following",
|
||
|
"manuallyApprovesFollowers": False,
|
||
|
"@context": [
|
||
|
"https://www.w3.org/ns/activitystreams",
|
||
|
"https://w3id.org/security/v1",
|
||
|
{},
|
||
|
],
|
||
|
"publicKey": {
|
||
|
"owner": "https://noop/federation/actors/demo",
|
||
|
"publicKeyPem": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxPDd/oXx0ClJ2BuBZ937AiERjvoroEpNebg34Cdl6FYsb2Auib8b\nCQjdjLjK/1ag35lmqmsECqtoDYWOo4tGilZJW47TWmXfcvCMH2Sw9FqdOlzpV1RI\nm8kc0Lu1CC2xOTctqIwSH7kDDnS4+S5hSxRdMTeNQNoirncY1CXa9TmJR1lE2HWz\n+B05ewzMrSen3l3fJLQFoI2GVbbjj+tvILKBL1oG5MtYieYqjt2sqtqy/OpWUAC7\nlRERRzd4t5xPBKykWkBCAOh80pvPue5V4s+xUMr7ioKTcm6pq+pNBta5w0hUYIcT\nMefQOnNuR4J0meIqiDLcrglGAmM6AVFwYwIDAQAB\n-----END RSA PUBLIC KEY-----\n", # noqa
|
||
|
"id": "https://noop/federation/actors/demo#main-key",
|
||
|
},
|
||
|
"endpoints": {"sharedInbox": "https://noop/federation/shared/inbox"},
|
||
|
}
|
||
|
r_mock.get(url, json=payload)
|
||
|
|
||
|
expected = {
|
||
|
contexts.AS.endpoints: [
|
||
|
{contexts.AS.sharedInbox: [{"@id": "https://noop/federation/shared/inbox"}]}
|
||
|
],
|
||
|
contexts.AS.followers: [
|
||
|
{"@id": "https://noop/federation/actors/demo/followers"}
|
||
|
],
|
||
|
contexts.AS.following: [
|
||
|
{"@id": "https://noop/federation/actors/demo/following"}
|
||
|
],
|
||
|
"@id": "https://noop/federation/actors/demo",
|
||
|
"http://www.w3.org/ns/ldp#inbox": [
|
||
|
{"@id": "https://noop/federation/actors/demo/inbox"}
|
||
|
],
|
||
|
contexts.AS.manuallyApprovesFollowers: [{"@value": False}],
|
||
|
contexts.AS.name: [{"@value": "demo"}],
|
||
|
contexts.AS.outbox: [{"@id": "https://noop/federation/actors/demo/outbox"}],
|
||
|
contexts.AS.preferredUsername: [{"@value": "demo"}],
|
||
|
contexts.SEC.publicKey: [
|
||
|
{
|
||
|
"@id": "https://noop/federation/actors/demo#main-key",
|
||
|
contexts.SEC.owner: [{"@id": "https://noop/federation/actors/demo"}],
|
||
|
contexts.SEC.publicKeyPem: [
|
||
|
{
|
||
|
"@value": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxPDd/oXx0ClJ2BuBZ937AiERjvoroEpNebg34Cdl6FYsb2Auib8b\nCQjdjLjK/1ag35lmqmsECqtoDYWOo4tGilZJW47TWmXfcvCMH2Sw9FqdOlzpV1RI\nm8kc0Lu1CC2xOTctqIwSH7kDDnS4+S5hSxRdMTeNQNoirncY1CXa9TmJR1lE2HWz\n+B05ewzMrSen3l3fJLQFoI2GVbbjj+tvILKBL1oG5MtYieYqjt2sqtqy/OpWUAC7\nlRERRzd4t5xPBKykWkBCAOh80pvPue5V4s+xUMr7ioKTcm6pq+pNBta5w0hUYIcT\nMefQOnNuR4J0meIqiDLcrglGAmM6AVFwYwIDAQAB\n-----END RSA PUBLIC KEY-----\n" # noqa
|
||
|
}
|
||
|
],
|
||
|
}
|
||
|
],
|
||
|
"@type": [contexts.AS.Person],
|
||
|
}
|
||
|
|
||
|
doc = jsonld.expand(url)
|
||
|
|
||
|
assert doc == expected
|
||
|
|
||
|
|
||
|
async def test_fetch_many(a_responses):
|
||
|
doc = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop/federation/actors/demo",
|
||
|
"type": "Person",
|
||
|
"followers": "https://noop/federation/actors/demo/followers",
|
||
|
}
|
||
|
followers_doc = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop/federation/actors/demo/followers",
|
||
|
"type": "Collection",
|
||
|
}
|
||
|
|
||
|
a_responses.get(doc["id"], payload=doc)
|
||
|
a_responses.get(followers_doc["id"], payload=followers_doc)
|
||
|
fetched = await jsonld.fetch_many(doc["id"], followers_doc["id"])
|
||
|
assert fetched == {followers_doc["id"]: followers_doc, doc["id"]: doc}
|
||
|
|
||
|
|
||
|
def test_dereference():
|
||
|
|
||
|
followers_doc = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop/federation/actors/demo/followers",
|
||
|
"type": "Collection",
|
||
|
}
|
||
|
|
||
|
actor_doc = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop/federation/actors/demo",
|
||
|
"type": "Person",
|
||
|
"followers": "https://noop/federation/actors/demo/followers",
|
||
|
}
|
||
|
|
||
|
store = {followers_doc["id"]: followers_doc, actor_doc["id"]: actor_doc}
|
||
|
|
||
|
payload = {
|
||
|
"followers": {"@id": followers_doc["id"]},
|
||
|
"actor": [
|
||
|
{"@id": actor_doc["id"], "hello": "world"},
|
||
|
{"somethingElse": [{"@id": actor_doc["id"]}]},
|
||
|
],
|
||
|
}
|
||
|
expected = {
|
||
|
"followers": followers_doc,
|
||
|
"actor": [actor_doc, {"somethingElse": [actor_doc]}],
|
||
|
}
|
||
|
|
||
|
assert jsonld.dereference(payload, store) == expected
|
||
|
|
||
|
|
||
|
def test_prepare_for_serializer():
|
||
|
config = {
|
||
|
"followers": {
|
||
|
"property": contexts.AS.followers,
|
||
|
"keep": "first",
|
||
|
"attr": "@id",
|
||
|
},
|
||
|
"name": {"property": contexts.AS.name, "keep": "first", "attr": "@value"},
|
||
|
"keys": {"property": contexts.SEC.publicKey, "type": "raw"},
|
||
|
}
|
||
|
|
||
|
payload = {
|
||
|
"@id": "https://noop/federation/actors/demo",
|
||
|
"@type": [contexts.AS.Person],
|
||
|
contexts.AS.followers: [
|
||
|
{"@id": "https://noop/federation/actors/demo/followers"}
|
||
|
],
|
||
|
contexts.AS.name: [{"@value": "demo"}],
|
||
|
contexts.SEC.publicKey: [
|
||
|
{"@id": "https://noop/federation/actors/demo#main-key1"},
|
||
|
{"@id": "https://noop/federation/actors/demo#main-key2"},
|
||
|
],
|
||
|
}
|
||
|
|
||
|
expected = {
|
||
|
"id": "https://noop/federation/actors/demo",
|
||
|
"type": contexts.AS.Person,
|
||
|
"followers": "https://noop/federation/actors/demo/followers",
|
||
|
"name": "demo",
|
||
|
"keys": [
|
||
|
{"@id": "https://noop/federation/actors/demo#main-key1"},
|
||
|
{"@id": "https://noop/federation/actors/demo#main-key2"},
|
||
|
],
|
||
|
}
|
||
|
|
||
|
assert jsonld.prepare_for_serializer(payload, config) == expected
|
||
|
|
||
|
|
||
|
def test_prepare_for_serializer_fallback():
|
||
|
config = {
|
||
|
"name": {"property": contexts.AS.name, "keep": "first", "attr": "@value"},
|
||
|
"album": {"property": contexts.FW.Album, "keep": "first"},
|
||
|
"noop_album": {"property": contexts.NOOP.Album, "keep": "first"},
|
||
|
}
|
||
|
fallbacks = {"album": ["noop_album"]}
|
||
|
|
||
|
payload = {
|
||
|
"@id": "https://noop/federation/actors/demo",
|
||
|
"@type": [contexts.AS.Person],
|
||
|
contexts.AS.name: [{"@value": "demo"}],
|
||
|
contexts.NOOP.Album: [{"@id": "https://noop/federation/album/demo"}],
|
||
|
}
|
||
|
|
||
|
expected = {
|
||
|
"id": "https://noop/federation/actors/demo",
|
||
|
"type": contexts.AS.Person,
|
||
|
"name": "demo",
|
||
|
"album": {"@id": "https://noop/federation/album/demo"},
|
||
|
"noop_album": {"@id": "https://noop/federation/album/demo"},
|
||
|
}
|
||
|
|
||
|
assert (
|
||
|
jsonld.prepare_for_serializer(payload, config, fallbacks=fallbacks) == expected
|
||
|
)
|
||
|
|
||
|
|
||
|
def test_jsonld_serializer_fallback():
|
||
|
class TestSerializer(jsonld.JsonLdSerializer):
|
||
|
id = serializers.URLField()
|
||
|
type = serializers.CharField()
|
||
|
name = serializers.CharField()
|
||
|
username = serializers.CharField()
|
||
|
total = serializers.IntegerField()
|
||
|
|
||
|
class Meta:
|
||
|
jsonld_fallbacks = {"total": ["total_fallback"]}
|
||
|
jsonld_mapping = {
|
||
|
"name": {
|
||
|
"property": contexts.AS.name,
|
||
|
"keep": "first",
|
||
|
"attr": "@value",
|
||
|
},
|
||
|
"username": {
|
||
|
"property": contexts.AS.preferredUsername,
|
||
|
"keep": "first",
|
||
|
"attr": "@value",
|
||
|
},
|
||
|
"total": {
|
||
|
"property": contexts.AS.totalItems,
|
||
|
"keep": "first",
|
||
|
"attr": "@value",
|
||
|
},
|
||
|
"total_fallback": {
|
||
|
"property": contexts.NOOP.count,
|
||
|
"keep": "first",
|
||
|
"attr": "@value",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
payload = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop.url/federation/actors/demo",
|
||
|
"type": "Person",
|
||
|
"name": "Hello",
|
||
|
"preferredUsername": "World",
|
||
|
"count": 42,
|
||
|
}
|
||
|
|
||
|
serializer = TestSerializer(data=payload)
|
||
|
assert serializer.is_valid(raise_exception=True)
|
||
|
|
||
|
assert serializer.validated_data == {
|
||
|
"type": contexts.AS.Person,
|
||
|
"id": payload["id"],
|
||
|
"name": payload["name"],
|
||
|
"username": payload["preferredUsername"],
|
||
|
"total": 42,
|
||
|
}
|
||
|
|
||
|
|
||
|
def test_jsonld_serializer_dereference(a_responses):
|
||
|
class TestSerializer(jsonld.JsonLdSerializer):
|
||
|
id = serializers.URLField()
|
||
|
type = serializers.CharField()
|
||
|
followers = serializers.JSONField()
|
||
|
|
||
|
class Meta:
|
||
|
jsonld_mapping = {
|
||
|
"followers": {"property": contexts.AS.followers, "dereference": True}
|
||
|
}
|
||
|
|
||
|
payload = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop.url/federation/actors/demo",
|
||
|
"type": "Person",
|
||
|
"followers": "https://noop.url/federation/actors/demo/followers",
|
||
|
}
|
||
|
|
||
|
followers_doc = {
|
||
|
"@context": ["https://www.w3.org/ns/activitystreams", {}],
|
||
|
"id": "https://noop.url/federation/actors/demo/followers",
|
||
|
"type": "Collection",
|
||
|
}
|
||
|
|
||
|
a_responses.get(followers_doc["id"], payload=followers_doc)
|
||
|
serializer = TestSerializer(data=payload)
|
||
|
|
||
|
assert serializer.is_valid(raise_exception=True)
|
||
|
assert serializer.validated_data == {
|
||
|
"type": contexts.AS.Person,
|
||
|
"id": payload["id"],
|
||
|
"followers": [followers_doc],
|
||
|
}
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"doc, ctx, expected",
|
||
|
[
|
||
|
(
|
||
|
{"@context": [{}], "hello": "world"},
|
||
|
"http://test",
|
||
|
{"@context": [{}, "http://test"], "hello": "world"},
|
||
|
),
|
||
|
(
|
||
|
{"@context": {"key": "value"}, "hello": "world"},
|
||
|
"http://test",
|
||
|
{"@context": [{"key": "value"}, "http://test"], "hello": "world"},
|
||
|
),
|
||
|
(
|
||
|
{"@context": "http://as", "hello": "world"},
|
||
|
"http://test",
|
||
|
{"@context": ["http://as", "http://test"], "hello": "world"},
|
||
|
),
|
||
|
],
|
||
|
)
|
||
|
def test_insert_context(doc, ctx, expected):
|
||
|
jsonld.insert_context(ctx, doc)
|
||
|
assert doc == expected
|