microblog.pub/tests/factories.py

281 lines
8.0 KiB
Python
Raw Permalink Normal View History

2022-06-22 22:17:07 +02:00
from urllib.parse import urlparse
2022-06-22 20:11:22 +02:00
from uuid import uuid4
import factory # type: ignore
from Crypto.PublicKey import RSA
2022-06-22 22:17:07 +02:00
from dateutil.parser import isoparse
2022-06-22 20:11:22 +02:00
from sqlalchemy import orm
from app import activitypub as ap
from app import actor
from app import models
from app.actor import RemoteActor
from app.ap_object import RemoteObject
2022-07-10 15:29:51 +02:00
from app.database import SessionLocal
from app.utils.datetime import now
2022-06-22 20:11:22 +02:00
2022-07-10 15:29:51 +02:00
_Session = orm.scoped_session(SessionLocal)
2022-06-22 20:11:22 +02:00
def generate_key() -> tuple[str, str]:
k = RSA.generate(1024)
return k.exportKey("PEM").decode(), k.publickey().exportKey("PEM").decode()
def build_follow_activity(
from_remote_actor: actor.RemoteActor,
for_remote_actor: actor.RemoteActor,
outbox_public_id: str | None = None,
) -> ap.RawObject:
return {
"@context": ap.AS_CTX,
"type": "Follow",
"id": from_remote_actor.ap_id + "/follow/" + (outbox_public_id or uuid4().hex),
"actor": from_remote_actor.ap_id,
"object": for_remote_actor.ap_id,
}
2022-07-26 21:10:59 +02:00
def build_delete_activity(
from_remote_actor: actor.RemoteActor | models.Actor,
deleted_object_ap_id: str,
outbox_public_id: str | None = None,
) -> ap.RawObject:
return {
"@context": ap.AS_CTX,
"type": "Delete",
"id": (
from_remote_actor.ap_id # type: ignore
+ "/follow/"
+ (outbox_public_id or uuid4().hex)
),
"actor": from_remote_actor.ap_id,
"object": deleted_object_ap_id,
}
2022-06-22 20:11:22 +02:00
def build_accept_activity(
from_remote_actor: actor.RemoteActor,
for_remote_object: RemoteObject,
outbox_public_id: str | None = None,
) -> ap.RawObject:
return {
"@context": ap.AS_CTX,
"type": "Accept",
"id": from_remote_actor.ap_id + "/accept/" + (outbox_public_id or uuid4().hex),
"actor": from_remote_actor.ap_id,
"object": for_remote_object.ap_id,
}
2022-10-18 21:39:09 +02:00
def build_block_activity(
from_remote_actor: actor.RemoteActor,
for_remote_actor: actor.RemoteActor,
outbox_public_id: str | None = None,
) -> ap.RawObject:
return {
"@context": ap.AS_CTX,
"type": "Block",
"id": from_remote_actor.ap_id + "/block/" + (outbox_public_id or uuid4().hex),
"actor": from_remote_actor.ap_id,
"object": for_remote_actor.ap_id,
}
2022-08-16 22:15:05 +02:00
def build_move_activity(
from_remote_actor: actor.RemoteActor,
for_remote_object: actor.RemoteActor,
outbox_public_id: str | None = None,
) -> ap.RawObject:
return {
"@context": ap.AS_CTX,
"type": "Move",
"id": from_remote_actor.ap_id + "/move/" + (outbox_public_id or uuid4().hex),
"actor": from_remote_actor.ap_id,
"object": from_remote_actor.ap_id,
"target": for_remote_object.ap_id,
}
2022-06-22 22:17:07 +02:00
def build_note_object(
2022-08-30 20:05:10 +02:00
from_remote_actor: actor.RemoteActor | models.Actor,
2022-06-22 22:17:07 +02:00
outbox_public_id: str | None = None,
content: str = "Hello",
to: list[str] = None,
cc: list[str] = None,
tags: list[ap.RawObject] = None,
2022-08-30 20:05:10 +02:00
in_reply_to: str | None = None,
2022-06-22 22:17:07 +02:00
) -> ap.RawObject:
published = now().replace(microsecond=0).isoformat().replace("+00:00", "Z")
context = from_remote_actor.ap_id + "/ctx/" + uuid4().hex
note_id = outbox_public_id or uuid4().hex
return {
"@context": ap.AS_CTX,
"type": "Note",
"id": from_remote_actor.ap_id + "/note/" + note_id,
"attributedTo": from_remote_actor.ap_id,
"content": content,
"to": to or [ap.AS_PUBLIC],
"cc": cc or [],
"published": published,
"context": context,
"conversation": context,
"url": from_remote_actor.ap_id + "/note/" + note_id,
"tag": tags or [],
"summary": None,
"sensitive": False,
2022-08-30 20:05:10 +02:00
"inReplyTo": in_reply_to,
2022-06-22 22:17:07 +02:00
}
2022-07-26 21:10:59 +02:00
def build_create_activity(obj: ap.RawObject) -> ap.RawObject:
return {
"@context": ap.AS_EXTENDED_CTX,
"actor": obj["attributedTo"],
"to": obj.get("to", []),
"cc": obj.get("cc", []),
"id": obj["id"] + "/activity",
"object": ap.remove_context(obj),
"published": obj["published"],
"type": "Create",
}
2022-06-22 20:11:22 +02:00
class BaseModelMeta:
sqlalchemy_session = _Session
sqlalchemy_session_persistence = "commit"
class RemoteActorFactory(factory.Factory):
class Meta:
model = RemoteActor
exclude = (
"base_url",
"username",
"public_key",
2022-08-16 22:15:05 +02:00
"also_known_as",
2022-06-22 20:11:22 +02:00
)
class Params:
icon_url = None
summary = "I like unit tests"
2022-08-16 22:15:05 +02:00
also_known_as: list[str] = []
2022-06-22 20:11:22 +02:00
ap_actor = factory.LazyAttribute(
lambda o: {
"@context": ap.AS_CTX,
"type": "Person",
"id": o.base_url,
"following": o.base_url + "/following",
"followers": o.base_url + "/followers",
# "featured": ID + "/featured",
"inbox": o.base_url + "/inbox",
"outbox": o.base_url + "/outbox",
"preferredUsername": o.username,
"name": o.username,
"summary": o.summary,
"endpoints": {},
"url": o.base_url,
"manuallyApprovesFollowers": False,
"attachment": [],
"icon": {},
"publicKey": {
"id": f"{o.base_url}#main-key",
"owner": o.base_url,
"publicKeyPem": o.public_key,
},
2022-08-16 22:15:05 +02:00
"alsoKnownAs": o.also_known_as,
2022-06-22 20:11:22 +02:00
}
)
class ActorFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta(BaseModelMeta):
model = models.Actor
# ap_actor
# ap_id
ap_type = "Person"
@classmethod
def from_remote_actor(cls, ra):
return cls(
ap_type=ra.ap_type,
ap_actor=ra.ap_actor,
ap_id=ra.ap_id,
)
class OutboxObjectFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta(BaseModelMeta):
model = models.OutboxObject
# public_id
# relates_to_inbox_object_id
# relates_to_outbox_object_id
@classmethod
def from_remote_object(cls, public_id, ro):
return cls(
public_id=public_id,
ap_type=ro.ap_type,
ap_id=ro.ap_id,
2022-06-24 22:41:43 +02:00
ap_context=ro.ap_context,
2022-06-22 20:11:22 +02:00
ap_object=ro.ap_object,
visibility=ro.visibility,
og_meta=ro.og_meta,
activity_object_ap_id=ro.activity_object_ap_id,
is_hidden_from_homepage=True if ro.in_reply_to else False,
)
class OutgoingActivityFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta(BaseModelMeta):
model = models.OutgoingActivity
# recipient
# outbox_object_id
2022-06-22 22:17:07 +02:00
class InboxObjectFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta(BaseModelMeta):
model = models.InboxObject
@classmethod
def from_remote_object(
cls,
ro: RemoteObject,
actor: models.Actor,
relates_to_inbox_object_id: int | None = None,
relates_to_outbox_object_id: int | None = None,
):
ap_published_at = now()
if "published" in ro.ap_object:
ap_published_at = isoparse(ro.ap_object["published"])
return cls(
2022-08-15 10:27:58 +02:00
server=urlparse(ro.ap_id).hostname,
2022-06-22 22:17:07 +02:00
actor_id=actor.id,
ap_actor_id=actor.ap_id,
ap_type=ro.ap_type,
ap_id=ro.ap_id,
2022-06-24 22:41:43 +02:00
ap_context=ro.ap_context,
2022-06-22 22:17:07 +02:00
ap_published_at=ap_published_at,
ap_object=ro.ap_object,
visibility=ro.visibility,
relates_to_inbox_object_id=relates_to_inbox_object_id,
relates_to_outbox_object_id=relates_to_outbox_object_id,
activity_object_ap_id=ro.activity_object_ap_id,
# Hide replies from the stream
is_hidden_from_stream=True if ro.in_reply_to else False,
)
2022-06-27 20:55:44 +02:00
class FollowerFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta(BaseModelMeta):
model = models.Follower
2022-08-16 22:15:05 +02:00
class FollowingFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta(BaseModelMeta):
model = models.Following