From 1ce7fdade0192b151cdbfaeba7deba79370bdddf Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Fri, 20 Jun 2025 09:20:26 +0200 Subject: [PATCH 1/3] Update actor --- lib/nulla/models/actor.ex | 2 ++ priv/repo/migrations/20250615130714_create_actors.exs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/lib/nulla/models/actor.ex b/lib/nulla/models/actor.ex index e173673..6eaf6d7 100644 --- a/lib/nulla/models/actor.ex +++ b/lib/nulla/models/actor.ex @@ -90,6 +90,8 @@ defmodule Nulla.Models.Actor do :publicKey, :endpoints ]) + |> unique_constraint([:preferredUsername, :domain]) + |> unique_constraint(:ap_id) end def create_actor(attrs) when is_map(attrs) do diff --git a/priv/repo/migrations/20250615130714_create_actors.exs b/priv/repo/migrations/20250615130714_create_actors.exs index 8098ce9..116a120 100644 --- a/priv/repo/migrations/20250615130714_create_actors.exs +++ b/priv/repo/migrations/20250615130714_create_actors.exs @@ -31,5 +31,8 @@ defmodule Nulla.Repo.Migrations.CreateActors do add :vcard_bday, :date add :vcard_Address, :string end + + create unique_index(:actors, [:preferredUsername, :domain]) + create unique_index(:actors, [:ap_id]) end end From 56490e3934b729ddfb97ff535bd547516d0b2828 Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Fri, 20 Jun 2025 09:20:44 +0200 Subject: [PATCH 2/3] Update note --- lib/nulla/models/note.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/nulla/models/note.ex b/lib/nulla/models/note.ex index 18a26cf..6d3cf8b 100644 --- a/lib/nulla/models/note.ex +++ b/lib/nulla/models/note.ex @@ -48,7 +48,8 @@ defmodule Nulla.Models.Note do from(n in __MODULE__, where: n.actor_id == ^actor_id, order_by: [desc: n.inserted_at], - limit: ^limit + limit: ^limit, + preload: [:actor, :media_attachments] ) |> Repo.all() end @@ -57,7 +58,8 @@ defmodule Nulla.Models.Note do from(n in __MODULE__, where: n.actor_id == ^actor_id and n.id < ^max_id, order_by: [desc: n.inserted_at], - limit: ^limit + limit: ^limit, + preload: [:actor, :media_attachments] ) |> Repo.all() end From c288831bb26108f27d3766e1faae7c9bcec96d80 Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Fri, 20 Jun 2025 09:20:57 +0200 Subject: [PATCH 3/3] Add outbox_controller_test.exs --- .../controllers/outbox_controller_test.exs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 test/nulla_web/controllers/outbox_controller_test.exs diff --git a/test/nulla_web/controllers/outbox_controller_test.exs b/test/nulla_web/controllers/outbox_controller_test.exs new file mode 100644 index 0000000..cd5512c --- /dev/null +++ b/test/nulla_web/controllers/outbox_controller_test.exs @@ -0,0 +1,85 @@ +defmodule NullaWeb.OutboxControllerTest do + use NullaWeb.ConnCase + alias Nulla.KeyGen + alias Nulla.Snowflake + alias Nulla.Models.Actor + alias Nulla.Models.Note + + setup do + {publicKeyPem, _privateKeyPem} = KeyGen.gen() + + {:ok, actor} = + Actor.create_actor(%{ + domain: "localhost", + ap_id: "http://localhost/users/test", + type: "Person", + following: "http://localhost/users/test/following", + followers: "http://localhost/users/test/followers", + inbox: "http://localhost/users/test/inbox", + outbox: "http://localhost/users/test/outbox", + featured: "http://localhost/users/test/collections/featured", + featuredTags: "http://localhost/users/test/collections/tags", + preferredUsername: "test", + name: "Test", + summary: "Test User", + url: "http://localhost/@test", + manuallyApprovesFollowers: false, + discoverable: true, + indexable: true, + published: DateTime.utc_now(), + memorial: false, + publicKey: + Jason.OrderedObject.new( + id: "http://localhost/users/test#main-key", + owner: "http://localhost/users/test", + publicKeyPem: publicKeyPem + ), + endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox") + }) + + note_id = Snowflake.next_id() + + {:ok, _note} = + Note.create_note(%{ + url: "#{actor.url}/#{note_id}", + content: "Hello World from Nulla!", + language: "en", + actor_id: actor.id + }) + + :ok + end + + describe "GET /users/username/outbox" do + test "returns ActivityPub JSON of outbox", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/outbox") + + assert response = json_response(conn, 200) + + assert is_binary(response["@context"]) + assert response["id"] == "http://localhost/users/test/outbox" + assert response["type"] == "OrderedCollection" + assert response["totalItems"] == 1 + assert response["first"] == "http://localhost/users/test/outbox?page=true" + assert response["last"] == "http://localhost/users/test/outbox?min_id=0&page=true" + end + + test "returns ActivityPub JSON of outbox with params", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/outbox?page=true") + + assert response = json_response(conn, 200) + + assert is_list(response["@context"]) + assert response["id"] == "http://localhost/users/test/outbox?page=true" + assert response["type"] == "OrderedCollectionPage" + assert is_binary(response["next"]) + assert is_binary(response["prev"]) + assert response["partOf"] == "http://localhost/users/test/outbox" + assert Enum.any?(response["orderedItems"]) + end + end +end