From 32bb4c7ca27c5c035b558365b6336b195120085a Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Thu, 19 Jun 2025 06:03:35 +0200 Subject: [PATCH 1/6] Fix follow_controller.ex --- lib/nulla_web/controllers/follow_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nulla_web/controllers/follow_controller.ex b/lib/nulla_web/controllers/follow_controller.ex index e6448c5..ca5afee 100644 --- a/lib/nulla_web/controllers/follow_controller.ex +++ b/lib/nulla_web/controllers/follow_controller.ex @@ -8,7 +8,7 @@ defmodule NullaWeb.FollowController do def following(conn, %{"username" => username, "page" => page_param}) do instance_settings = InstanceSettings.get_instance_settings!() domain = instance_settings.domain - limit = instance_settings.limit + limit = instance_settings.api_limit actor = Actor.get_actor(username, domain) total = Relation.count_following(actor.id) @@ -39,7 +39,7 @@ defmodule NullaWeb.FollowController do def followers(conn, %{"username" => username, "page" => page_param}) do instance_settings = InstanceSettings.get_instance_settings!() domain = instance_settings.domain - limit = instance_settings.limit + limit = instance_settings.api_limit actor = Actor.get_actor(username, domain) total = Relation.count_followers(actor.id) From 95df5ed0e0cb619d9a4f351ada34b63958cfe27d Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Thu, 19 Jun 2025 06:13:17 +0200 Subject: [PATCH 2/6] Fix 20250615131158_create_users.exs --- priv/repo/migrations/20250615131158_create_users.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/repo/migrations/20250615131158_create_users.exs b/priv/repo/migrations/20250615131158_create_users.exs index 3ff4dba..9ee5345 100644 --- a/priv/repo/migrations/20250615131158_create_users.exs +++ b/priv/repo/migrations/20250615131158_create_users.exs @@ -6,7 +6,7 @@ defmodule Nulla.Repo.Migrations.CreateUsers do add :id, :bigint, primary_key: true add :email, :string add :password, :string - add :privateKeyPem, :string + add :privateKeyPem, :text add :last_active_at, :utc_datetime timestamps(type: :utc_datetime) From 2d98fee0c3c80922a2a6189c11e62429fc026b5b Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Thu, 19 Jun 2025 06:13:52 +0200 Subject: [PATCH 3/6] Update nodeinfo_controller_test.exs --- .../controllers/nodeinfo_controller_test.exs | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/test/nulla_web/controllers/nodeinfo_controller_test.exs b/test/nulla_web/controllers/nodeinfo_controller_test.exs index 085b5a0..2003325 100644 --- a/test/nulla_web/controllers/nodeinfo_controller_test.exs +++ b/test/nulla_web/controllers/nodeinfo_controller_test.exs @@ -1,42 +1,46 @@ defmodule NullaWeb.NodeinfoControllerTest do use NullaWeb.ConnCase + alias Nulla.KeyGen alias Nulla.Models.User alias Nulla.Models.Actor setup do - {: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: "PUBLIC KEY" - ), - endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox") - }) + {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") + }) User.create_user(%{ id: actor.id, email: "test@localhost", password: "password", - privateKeyPem: "PRIVATE KEY", + privateKeyPem: privateKeyPem, last_active_at: DateTime.utc_now() }) From 63ce21475e5ca94b8963794af5df3d060b8256b6 Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Thu, 19 Jun 2025 06:17:37 +0200 Subject: [PATCH 4/6] Update nodeinfo_controller_test.exs --- test/nulla_web/controllers/nodeinfo_controller_test.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/nulla_web/controllers/nodeinfo_controller_test.exs b/test/nulla_web/controllers/nodeinfo_controller_test.exs index 2003325..d52add7 100644 --- a/test/nulla_web/controllers/nodeinfo_controller_test.exs +++ b/test/nulla_web/controllers/nodeinfo_controller_test.exs @@ -63,7 +63,9 @@ defmodule NullaWeb.NodeinfoControllerTest do assert is_binary(link["href"]) end) end + end + describe "GET /nodeinfo/2.0" do test "returns Nodeinfo JSON show", %{conn: conn} do conn = conn From 158e500fe6a0e26f03778fdde5e25d36bba10670 Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Thu, 19 Jun 2025 06:58:05 +0200 Subject: [PATCH 5/6] Fix following and followers --- lib/nulla/activitypub.ex | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/nulla/activitypub.ex b/lib/nulla/activitypub.ex index 0f7dd73..40410dd 100644 --- a/lib/nulla/activitypub.ex +++ b/lib/nulla/activitypub.ex @@ -122,10 +122,10 @@ defmodule Nulla.ActivityPub do def following(actor, total) do Jason.OrderedObject.new( "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/users/#{actor.preferredUsername}/following", + id: "#{actor.ap_id}/following", type: "OrderedCollection", totalItems: total, - first: "https://#{actor.domain}/users/#{actor.preferredUsername}/following?page=1" + first: "#{actor.ap_id}/following?page=1" ) end @@ -134,12 +134,12 @@ defmodule Nulla.ActivityPub do def following(actor, total, following_list, page, limit) when is_integer(page) and page > 0 do data = [ "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/@#{actor.preferredUsername}/following?page=#{page}", + id: "#{actor.ap_id}/following?page=#{page}", type: "OrderedCollectionPage", totalItems: total, - next: "https://#{actor.domain}/users/#{actor.preferredUsername}/following?page=#{page + 1}", - prev: "https://#{actor.domain}/users/#{actor.preferredUsername}/following?page=#{page - 1}", - partOf: "https://#{actor.domain}/users/#{actor.preferredUsername}/following", + next: "#{actor.ap_id}/following?page=#{page + 1}", + prev: "#{actor.ap_id}/following?page=#{page - 1}", + partOf: "#{actor.ap_id}/following", orderedItems: following_list ] @@ -166,10 +166,10 @@ defmodule Nulla.ActivityPub do def followers(actor, total) do Jason.OrderedObject.new( "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers", + id: "#{actor.ap_id}/followers", type: "OrderedCollection", totalItems: total, - first: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers?page=1" + first: "#{actor.ap_id}/followers?page=1" ) end @@ -179,12 +179,12 @@ defmodule Nulla.ActivityPub do when is_integer(page) and page > 0 do data = [ "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/users#{actor.preferredUsername}/followers?page=#{page}", + id: "#{actor.ap_id}/followers?page=#{page}", type: "OrderedCollectionPage", totalItems: total, - next: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers?page=#{page + 1}", - prev: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers?page=#{page - 1}", - partOf: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers", + next: "#{actor.ap_id}/followers?page=#{page + 1}", + prev: "#{actor.ap_id}/followers?page=#{page - 1}", + partOf: "#{actor.ap_id}/followers", orderedItems: followers_list ] From 1b4034dac6c694f3046eebee5f00ba4ae7fcaeaa Mon Sep 17 00:00:00 2001 From: miraikumiko Date: Thu, 19 Jun 2025 06:58:33 +0200 Subject: [PATCH 6/6] Add follow_controller_test.exs --- .../controllers/follow_controller_test.exs | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 test/nulla_web/controllers/follow_controller_test.exs diff --git a/test/nulla_web/controllers/follow_controller_test.exs b/test/nulla_web/controllers/follow_controller_test.exs new file mode 100644 index 0000000..900c112 --- /dev/null +++ b/test/nulla_web/controllers/follow_controller_test.exs @@ -0,0 +1,123 @@ +defmodule NullaWeb.FollowControllerTest do + use NullaWeb.ConnCase + alias Nulla.KeyGen + alias Nulla.Models.User + alias Nulla.Models.Actor + + 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") + }) + + User.create_user(%{ + id: actor.id, + email: "test@localhost", + password: "password", + privateKeyPem: privateKeyPem, + last_active_at: DateTime.utc_now() + }) + + :ok + end + + describe "GET /users/username/following" do + test "returns ActivityPub JSON of following", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/following") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/following" + assert response["type"] == "OrderedCollection" + assert response["totalItems"] == 0 + assert response["first"] == "http://localhost/users/test/following?page=1" + end + + test "returns ActivityPub JSON of following with page", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/following?page=1") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/following?page=1" + assert response["type"] == "OrderedCollectionPage" + assert response["totalItems"] == 0 + assert response["partOf"] == "http://localhost/users/test/following" + assert is_list(response["orderedItems"]) + end + + test "returns first page with invalid value", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/following?page=abc") + + assert conn.status == 200 + end + end + + describe "GET /users/username/followers" do + test "returns ActivityPub JSON of followers", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/followers") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/followers" + assert response["type"] == "OrderedCollection" + assert response["totalItems"] == 0 + assert response["first"] == "http://localhost/users/test/followers?page=1" + end + + test "returns ActivityPub JSON of followers with page", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/followers?page=1") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/followers?page=1" + assert response["type"] == "OrderedCollectionPage" + assert response["totalItems"] == 0 + assert response["partOf"] == "http://localhost/users/test/followers" + assert is_list(response["orderedItems"]) + end + + test "returns first page with invalid value", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/followers?page=abc") + + assert conn.status == 200 + end + end +end