Introduce group posts

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2020-07-09 17:24:28 +02:00
parent bec1c69d4b
commit 9c9f1385fb
249 changed files with 11886 additions and 5023 deletions

View File

@@ -8,9 +8,10 @@ defmodule Mobilizon.Federation.ActivityPubTest do
use Mobilizon.DataCase
import Mock
import Mox
import Mobilizon.Factory
alias Mobilizon.{Actors, Conversations, Events}
alias Mobilizon.{Actors, Discussions, Events}
alias Mobilizon.Actors.Actor
alias Mobilizon.Resources.Resource
alias Mobilizon.Todos.{Todo, TodoList}
@@ -18,13 +19,10 @@ defmodule Mobilizon.Federation.ActivityPubTest do
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Utils
alias Mobilizon.Federation.HTTPSignatures.Signature
alias Mobilizon.Service.HTTP.ActivityPub.Mock
@activity_pub_public_audience "https://www.w3.org/ns/activitystreams#Public"
setup_all do
HTTPoison.start()
end
describe "setting HTTP signature" do
test "set http signature header" do
actor = insert(:actor)
@@ -140,40 +138,75 @@ defmodule Mobilizon.Federation.ActivityPubTest do
describe "fetching an" do
test "object by url" do
use_cassette "activity_pub/fetch_framapiaf_framasoft_status" do
{:ok, object} =
ActivityPub.fetch_object_from_url(
"https://framapiaf.org/users/Framasoft/statuses/102093631881522097"
)
url = "https://framapiaf.org/users/Framasoft/statuses/102093631881522097"
{:ok, object_again} =
ActivityPub.fetch_object_from_url(
"https://framapiaf.org/users/Framasoft/statuses/102093631881522097"
)
data =
File.read!("test/fixtures/mastodon-status-2.json")
|> Jason.decode!()
assert object.id == object_again.id
end
Mock
|> expect(:call, fn
%{method: :get, url: ^url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
{:ok, object} = ActivityPub.fetch_object_from_url(url)
{:ok, object_again} = ActivityPub.fetch_object_from_url(url)
assert object.id == object_again.id
end
test "object reply by url" do
use_cassette "activity_pub/fetch_framasoft_framapiaf_reply" do
{:ok, object} =
ActivityPub.fetch_object_from_url("https://mamot.fr/@imacrea/102094441327423790")
url = "https://zoltasila.pl/objects/1c295713-8e3c-411e-9e62-57a7b9c9e514"
reply_to_url = "https://framapiaf.org/users/peertube/statuses/104584600044284729"
assert object.in_reply_to_comment.url ==
"https://framapiaf.org/users/Framasoft/statuses/102093632302210150"
end
data =
File.read!("test/fixtures/mastodon-status-3.json")
|> Jason.decode!()
reply_to_data =
File.read!("test/fixtures/mastodon-status-4.json")
|> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: ^url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{method: :get, url: ^reply_to_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: reply_to_data}}
end)
{:ok, object} = ActivityPub.fetch_object_from_url(url)
assert object.in_reply_to_comment.url == reply_to_url
end
test "object reply to a video by url" do
use_cassette "activity_pub/fetch_reply_to_framatube" do
{:ok, object} =
ActivityPub.fetch_object_from_url(
"https://diaspodon.fr/users/dada/statuses/100820008426311925"
)
url = "https://diaspodon.fr/users/dada/statuses/100820008426311925"
origin_url = "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d"
assert object.in_reply_to_comment == nil
end
data =
File.read!("test/fixtures/mastodon-status-5.json")
|> Jason.decode!()
origin_data =
File.read!("test/fixtures/peertube-video.json")
|> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: ^url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{method: :get, url: ^origin_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: origin_data}}
end)
{:ok, object} = ActivityPub.fetch_object_from_url(url)
assert object.in_reply_to_comment == nil
end
end
@@ -181,26 +214,28 @@ defmodule Mobilizon.Federation.ActivityPubTest do
test "it creates a delete activity and deletes the original event" do
event = insert(:event)
event = Events.get_public_event_by_url_with_preload!(event.url)
{:ok, delete, _} = ActivityPub.delete(event)
{:ok, delete, _} = ActivityPub.delete(event, event.organizer_actor)
assert delete.data["type"] == "Delete"
assert delete.data["actor"] == event.organizer_actor.url
assert delete.data["object"] == event.url
assert delete.data["object"]["type"] == "Event"
assert delete.data["object"]["id"] == event.url
assert Events.get_event_by_url(event.url) == nil
end
test "it deletes the original event but only locally if needed" do
with_mock Utils,
with_mock Utils, [:passthrough],
maybe_federate: fn _ -> :ok end,
lazy_put_activity_defaults: fn args -> args end do
event = insert(:event)
event = Events.get_public_event_by_url_with_preload!(event.url)
{:ok, delete, _} = ActivityPub.delete(event, false)
{:ok, delete, _} = ActivityPub.delete(event, event.organizer_actor, false)
assert delete.data["type"] == "Delete"
assert delete.data["actor"] == event.organizer_actor.url
assert delete.data["object"] == event.url
assert delete.data["object"]["type"] == "Event"
assert delete.data["object"]["id"] == event.url
assert delete.local == false
assert Events.get_event_by_url(event.url) == nil
@@ -211,15 +246,16 @@ defmodule Mobilizon.Federation.ActivityPubTest do
test "it creates a delete activity and deletes the original comment" do
comment = insert(:comment)
comment = Conversations.get_comment_from_url_with_preload!(comment.url)
assert is_nil(Conversations.get_comment_from_url(comment.url).deleted_at)
{:ok, delete, _} = ActivityPub.delete(comment)
comment = Discussions.get_comment_from_url_with_preload!(comment.url)
assert is_nil(Discussions.get_comment_from_url(comment.url).deleted_at)
{:ok, delete, _} = ActivityPub.delete(comment, comment.actor)
assert delete.data["type"] == "Delete"
assert delete.data["actor"] == comment.actor.url
assert delete.data["object"] == comment.url
assert delete.data["object"]["type"] == "Note"
assert delete.data["object"]["id"] == comment.url
refute is_nil(Conversations.get_comment_from_url(comment.url).deleted_at)
refute is_nil(Discussions.get_comment_from_url(comment.url).deleted_at)
end
end
@@ -230,7 +266,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
actor = insert(:actor)
actor_data = %{summary: @updated_actor_summary}
{:ok, update, _} = ActivityPub.update(:actor, actor, actor_data, false)
{:ok, update, _} = ActivityPub.update(actor, actor_data, false)
assert update.data["actor"] == actor.url
assert update.data["to"] == [@activity_pub_public_audience]
@@ -246,7 +282,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
event = insert(:event, organizer_actor: actor)
event_data = %{begins_on: @updated_start_time}
{:ok, update, _} = ActivityPub.update(:event, event, event_data)
{:ok, update, _} = ActivityPub.update(event, event_data)
assert update.data["actor"] == actor.url
assert update.data["to"] == [@activity_pub_public_audience]
@@ -272,8 +308,8 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert create_data.local
assert create_data.data["object"]["id"] == todo_list_url
assert create_data.data["object"]["type"] == "TodoList"
assert create_data.data["object"]["title"] == @todo_list_title
assert create_data.data["to"] == [group.url]
assert create_data.data["object"]["name"] == @todo_list_title
assert create_data.data["to"] == [group.members_url]
assert create_data.data["actor"] == actor.url
assert_called(Utils.maybe_federate(create_data))
@@ -301,7 +337,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert create_data.data["object"]["id"] == todo_url
assert create_data.data["object"]["type"] == "Todo"
assert create_data.data["object"]["name"] == @todo_title
assert create_data.data["to"] == [todo_list.actor.url]
assert create_data.data["to"] == [todo_list.actor.members_url]
assert create_data.data["actor"] == actor.url
assert_called(Utils.maybe_federate(create_data))
@@ -341,7 +377,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert create_data.data["object"]["url"] == @resource_url
assert create_data.data["to"] == [group.url]
assert create_data.data["to"] == [group.members_url]
assert create_data.data["actor"] == actor.url
assert create_data.data["attributedTo"] == [actor.url]
@@ -372,7 +408,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert create_data.data["object"]["id"] == url
assert create_data.data["object"]["type"] == "ResourceCollection"
assert create_data.data["object"]["name"] == @folder_title
assert create_data.data["to"] == [group.url]
assert create_data.data["to"] == [group.members_url]
assert create_data.data["actor"] == actor.url
assert create_data.data["attributedTo"] == [actor.url]
@@ -411,7 +447,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert create_data.data["object"]["url"] == @resource_url
assert create_data.data["to"] == [group.url]
assert create_data.data["to"] == [group.members_url]
assert create_data.data["actor"] == actor.url
assert create_data.data["attributedTo"] == [actor.url]
@@ -437,7 +473,6 @@ defmodule Mobilizon.Federation.ActivityPubTest do
{:ok, update_data, %Resource{url: url}} =
ActivityPub.update(
:resource,
resource,
%{
title: @updated_resource_title
@@ -453,7 +488,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert update_data.data["object"]["url"] == @resource_url
assert update_data.data["to"] == [group.url]
assert update_data.data["to"] == [group.members_url]
assert update_data.data["actor"] == actor.url
assert update_data.data["attributedTo"] == [actor.url]
@@ -496,7 +531,7 @@ defmodule Mobilizon.Federation.ActivityPubTest do
assert update_data.data["object"]["url"] == @resource_url
assert update_data.data["to"] == [group.url]
assert update_data.data["to"] == [group.members_url]
assert update_data.data["actor"] == actor.url
assert update_data.data["origin"] == nil
assert update_data.data["target"] == parent_url
@@ -524,19 +559,23 @@ defmodule Mobilizon.Federation.ActivityPubTest do
{:ok, update_data, %Resource{url: url}} =
ActivityPub.delete(
resource,
actor,
true
)
assert update_data.local
assert update_data.data["type"] == "Delete"
assert update_data.data["object"] == url
assert update_data.data["to"] == [group.url]
# TODO : Add actor parameter to ActivityPub.delete/2
# assert update_data.data["actor"] == actor.url
# assert update_data.data["attributedTo"] == [actor.url]
assert update_data.data["object"]["type"] == "Document"
assert update_data.data["object"]["id"] == url
assert update_data.data["to"] == [group.members_url]
assert update_data.data["actor"] == actor.url
assert update_data.data["attributedTo"] == [group.url]
assert_called(Utils.maybe_federate(update_data))
end
end
end
describe "announce" do
end
end

View File

@@ -2,37 +2,35 @@ defmodule Mobilizon.Federation.ActivityPub.RefresherTest do
use Mobilizon.DataCase
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Refresher
alias Mobilizon.Service.HTTP.ActivityPub.Mock
alias Mobilizon.Web.ActivityPub.ActorView
import Mobilizon.Factory
import Mock
import Mox
test "refreshes a members collection" do
%Actor{members_url: members_url, url: group_url} = group = insert(:group)
%Actor{url: actor_url} = actor = insert(:actor)
%Member{} = insert(:member, parent: group, actor: actor, role: :member)
describe "refreshes a" do
setup :verify_on_exit!
data =
ActorView.render("members.json", %{group: group, actor_applicant: actor}) |> Jason.encode!()
test "members collection" do
%Actor{members_url: members_url} =
group =
insert(:group,
url: "https://remoteinstance.tld/@group",
members_url: "https://remoteinstance.tld/@group/members",
domain: "remoteinstance.tld"
)
%Actor{} = actor = insert(:actor)
%Member{} = insert(:member, parent: group, actor: actor, role: :member)
data = ActorView.render("members.json", %{actor: group, actor_applicant: actor})
Mock
|> expect(:call, fn
%{method: :get, url: ^members_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
with_mocks([
{HTTPoison, [],
[
get!: fn ^members_url, _headers, _options ->
%HTTPoison.Response{status_code: 200, body: data}
end
]},
{ActivityPub, [],
[
get_or_fetch_actor_by_url: fn url ->
case url do
^actor_url -> {:ok, actor}
^group_url -> {:ok, group}
end
end
]}
]) do
assert :ok == Refresher.fetch_collection(group.members_url, actor)
end
end

View File

@@ -0,0 +1,173 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.AnnouncesTest do
use Mobilizon.DataCase
import Mobilizon.Factory
import Mox
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Federation.ActivityPub.Transmogrifier
alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Service.HTTP.ActivityPub.Mock
alias Mobilizon.Tombstone
@comment_text "my comment"
describe "incoming announces for discussion creation" do
setup :verify_on_exit!
test "by group member works" do
actor = insert(:actor)
group = insert(:group)
insert(:member, parent: group, actor: actor, role: :member)
%Comment{url: comment_url} =
comment = build(:comment, actor: actor, attributed_to: group, event: nil)
comment_data = Convertible.model_to_as(comment)
Mock
|> expect(:call, fn
%{method: :get, url: ^comment_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: comment_data}}
end)
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("actor", group.url)
|> Map.put("object", comment.url)
{:ok, _, %Comment{actor: %Actor{url: actor_url}, url: comment_url}} =
Transmogrifier.handle_incoming(data)
assert actor_url == comment.actor.url
assert comment_url == comment.url
end
end
describe "handle incoming announces for discussion updates" do
setup :verify_on_exit!
@updated_title "Updated title"
test "by group member works" do
actor =
insert(:actor,
domain: "otherremoteinstance.tld",
url: "http://otherremoteinstance.tld/@somemember"
)
group =
insert(:group,
url: "http://remoteinstance.tld/@mygroup",
domain: "remoteinstance.tld",
members_url: "http://remoteinstance.tld/@mygroup/members"
)
insert(:member, parent: group, actor: actor, role: :member)
%Comment{url: _comment_url} =
comment =
insert(:comment,
actor: actor,
attributed_to: group,
text: @comment_text,
url: "http://otherremoteinstance.tld/@somemember/uuid"
)
%Discussion{url: discussion_url} =
discussion =
insert(:discussion,
last_comment: comment,
comments: [comment],
creator: actor,
actor: group,
url: "http://otherremoteinstance.tld/@mygroup/c/talk-of-something-sh0rt-uu1d"
)
discussion_updated = Map.put(discussion, :title, @updated_title)
discussion_updated_data = Convertible.model_to_as(discussion_updated)
Mock
|> expect(:call, fn
%{url: ^discussion_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: discussion_updated_data}}
end)
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("actor", group.url)
|> Map.put("object", discussion_url)
assert {:ok, _, %Discussion{title: title}} = Transmogrifier.handle_incoming(data)
assert title == @updated_title
end
end
describe "handle incoming announces for discussion deletion" do
setup :verify_on_exit!
test "by group member works" do
actor =
insert(:actor,
url: "http://otherremoteinstance.tld/@somemember",
domain: "otherremoteinstance.tld"
)
group =
insert(:group,
url: "http://remoteinstance.tld/@mygroup",
domain: "remoteinstance.tld",
members_url: "http://remoteinstance.tld/@mygroup/members"
)
insert(:member, parent: group, actor: actor, role: :member)
%Comment{url: comment_url} =
comment =
insert(:comment,
actor: actor,
attributed_to: group,
text: @comment_text,
url: "http://otherremoteinstance.tld/comment/uuid"
)
tombstone = build(:tombstone, uri: comment.url, actor: actor)
tombstone_data = Convertible.model_to_as(tombstone)
Mock
|> expect(:call, fn
%{url: ^comment_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: tombstone_data}}
end)
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("actor", group.url)
|> Map.put("object", comment.url)
%Comment{deleted_at: deleted_at, text: comment_text} =
Discussions.get_comment_from_url(comment.url)
assert is_nil(deleted_at)
assert comment_text == @comment_text
{:ok, _, %Comment{deleted_at: deleted_at, text: comment_text}} =
Transmogrifier.handle_incoming(data)
refute is_nil(deleted_at)
refute comment_text == @comment_text
%Tombstone{actor_id: _actor_id, uri: tombstone_uri} = Tombstone.find_tombstone(comment_url)
# assert actor_id == comment.actor.id
assert tombstone_uri == comment.url
end
end
end

View File

@@ -0,0 +1,155 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
use Mobilizon.DataCase
import Mobilizon.Factory
import Mox
import ExUnit.CaptureLog
alias Mobilizon.Actors
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions
alias Mobilizon.Discussions.Comment
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Service.HTTP.ActivityPub.Mock
describe "handle incoming comments" do
setup :verify_on_exit!
test "it ignores an incoming comment if we already have it" do
comment = insert(:comment)
comment = Repo.preload(comment, [:attributed_to])
activity = %{
"type" => "Create",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"actor" => comment.actor.url,
"object" => Convertible.model_to_as(comment)
}
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
|> Map.put("object", activity["object"])
assert {:ok, nil, _} = Transmogrifier.handle_incoming(data)
end
test "it fetches replied-to activities if we don't have them" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
reply_to_url = "https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94"
object =
data["object"]
|> Map.put("inReplyTo", reply_to_url)
data =
data
|> Map.put("object", object)
reply_to_data =
File.read!("test/fixtures/pleroma-comment-object.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: ^reply_to_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: reply_to_data}}
end)
{:ok, returned_activity, _} = Transmogrifier.handle_incoming(data)
%Comment{} =
origin_comment =
Discussions.get_comment_from_url(
"https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94"
)
assert returned_activity.data["object"]["inReplyTo"] ==
"https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94"
assert returned_activity.data["object"]["inReplyTo"] == origin_comment.url
end
@url_404 "https://404.site/whatever"
test "it does not crash if the object in inReplyTo can't be fetched" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
object =
data["object"]
|> Map.put("inReplyTo", @url_404)
data =
data
|> Map.put("object", object)
Mock
|> expect(:call, fn
%{method: :get, url: "https://404.site/whatever"}, _opts ->
{:ok, %Tesla.Env{status: 404, body: "Not found"}}
end)
assert capture_log([level: :warn], fn ->
{:ok, _returned_activity, _entity} = Transmogrifier.handle_incoming(data)
end) =~ "[warn] Parent object is something we don't handle"
end
test "it works for incoming notices" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["id"] ==
"https://framapiaf.org/users/admin/statuses/99512778738411822/activity"
assert data["to"] == [
"https://www.w3.org/ns/activitystreams#Public",
"https://framapiaf.org/users/tcit"
]
# assert data["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://mobilizon.com/@tcit"
# ]
assert data["actor"] == "https://framapiaf.org/users/admin"
object = data["object"]
assert object["id"] == "https://framapiaf.org/users/admin/statuses/99512778738411822"
assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
# assert object["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://localtesting.pleroma.lol/users/lain"
# ]
assert object["actor"] == "https://framapiaf.org/users/admin"
assert object["attributedTo"] == "https://framapiaf.org/users/admin"
{:ok, %Actor{}} = Actors.get_actor_by_url(object["actor"])
end
test "it works for incoming notices with hashtags" do
data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert Enum.at(data["object"]["tag"], 0)["name"] == "@tcit@framapiaf.org"
assert Enum.at(data["object"]["tag"], 1)["name"] == "#moo"
end
test "it works for incoming notices with url not being a string (prismo)" do
data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!()
assert {:error, :not_supported} == Transmogrifier.handle_incoming(data)
# Pages without groups are not supported
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
# assert data["object"]["url"] == "https://prismo.news/posts/83"
end
end
end

View File

@@ -0,0 +1,122 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.FollowTest do
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Actors
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
describe "handle incoming follow accept activities" do
test "it works for incoming accepts which were pre-accepted" do
follower = insert(:actor)
followed = insert(:actor)
refute Actors.is_following(follower, followed)
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
assert Actors.is_following(follower, followed)
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
object =
accept_data["object"]
|> Map.put("actor", follower.url)
|> Map.put("id", follow_activity.data["id"])
accept_data = Map.put(accept_data, "object", object)
{:ok, activity, _} = Transmogrifier.handle_incoming(accept_data)
refute activity.local
assert activity.data["object"]["id"] == follow_activity.data["id"]
{:ok, follower} = Actors.get_actor_by_url(follower.url)
assert Actors.is_following(follower, followed)
end
test "it works for incoming accepts which are referenced by IRI only" do
follower = insert(:actor)
followed = insert(:actor)
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
|> Map.put("object", follow_activity.data["id"])
{:ok, activity, _} = Transmogrifier.handle_incoming(accept_data)
assert activity.data["object"]["id"] == follow_activity.data["id"]
assert activity.data["object"]["id"] =~ "/follow/"
assert activity.data["id"] =~ "/accept/follow/"
{:ok, follower} = Actors.get_actor_by_url(follower.url)
assert Actors.is_following(follower, followed)
end
test "it fails for incoming accepts which cannot be correlated" do
follower = insert(:actor)
followed = insert(:actor)
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
accept_data =
Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.url))
:error = Transmogrifier.handle_incoming(accept_data)
{:ok, follower} = Actors.get_actor_by_url(follower.url)
refute Actors.is_following(follower, followed)
end
end
describe "handle incoming follow reject activities" do
test "it fails for incoming rejects which cannot be correlated" do
follower = insert(:actor)
followed = insert(:actor)
accept_data =
File.read!("test/fixtures/mastodon-reject-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
accept_data =
Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.url))
:error = Transmogrifier.handle_incoming(accept_data)
{:ok, follower} = Actors.get_actor_by_url(follower.url)
refute Actors.is_following(follower, followed)
end
test "it works for incoming rejects which are referenced by IRI only" do
follower = insert(:actor)
followed = insert(:actor)
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
assert Actors.is_following(follower, followed)
reject_data =
File.read!("test/fixtures/mastodon-reject-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
|> Map.put("object", follow_activity.data["id"])
{:ok, %Activity{data: _}, _} = Transmogrifier.handle_incoming(reject_data)
refute Actors.is_following(follower, followed)
end
end
end

View File

@@ -0,0 +1,60 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.InviteTest do
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Actors
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Federation.ActivityPub.Transmogrifier
describe "handle Invite activities on group" do
test "it accepts Invite activities" do
%Actor{url: group_url, id: group_id} = group = insert(:group)
%Actor{url: group_admin_url, id: group_admin_id} = group_admin = insert(:actor)
%Member{} =
_group_admin_member =
insert(:member, parent: group, actor: group_admin, role: :administrator)
%Actor{url: invitee_url, id: invitee_id} = _invitee = insert(:actor)
invite_data =
File.read!("test/fixtures/mobilizon-invite-activity.json")
|> Jason.decode!()
|> Map.put("actor", group_admin_url)
|> Map.put("object", group_url)
|> Map.put("target", invitee_url)
assert {:ok, activity, %Member{}} = Transmogrifier.handle_incoming(invite_data)
assert %Member{} = member = Actors.get_member_by_url(invite_data["id"])
assert member.actor.id == invitee_id
assert member.parent.id == group_id
assert member.role == :invited
assert member.invited_by_id == group_admin_id
end
test "it refuses Invite activities for " do
%Actor{url: group_url, id: group_id} = group = insert(:group)
%Actor{url: group_admin_url, id: group_admin_id} = group_admin = insert(:actor)
%Member{} =
_group_admin_member =
insert(:member, parent: group, actor: group_admin, role: :administrator)
%Actor{url: invitee_url, id: invitee_id} = _invitee = insert(:actor)
invite_data =
File.read!("test/fixtures/mobilizon-invite-activity.json")
|> Jason.decode!()
|> Map.put("actor", group_admin_url)
|> Map.put("object", group_url)
|> Map.put("target", invitee_url)
assert {:ok, activity, %Member{}} = Transmogrifier.handle_incoming(invite_data)
assert %Member{} = member = Actors.get_member_by_url(invite_data["id"])
assert member.actor.id == invitee_id
assert member.parent.id == group_id
assert member.role == :invited
assert member.invited_by_id == group_admin_id
end
end
end

View File

@@ -0,0 +1,104 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.JoinTest do
use Mobilizon.DataCase
import Mobilizon.Factory
import ExUnit.CaptureLog
alias Mobilizon.Actors.Actor
alias Mobilizon.Events
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Transmogrifier
describe "handle incoming join activities" do
@join_message "I want to get in!"
test "it accepts Join activities" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Actor{url: participant_url} = _participant = insert(:actor)
%Event{url: event_url} = _event = insert(:event, organizer_actor: organizer)
join_data =
File.read!("test/fixtures/mobilizon-join-activity.json")
|> Jason.decode!()
|> Map.put("actor", participant_url)
|> Map.put("object", event_url)
|> Map.put("participationMessage", @join_message)
assert {:ok, activity, %Participant{} = participant} =
Transmogrifier.handle_incoming(join_data)
assert participant.metadata.message == @join_message
assert participant.role == :participant
assert activity.data["type"] == "Accept"
assert activity.data["object"]["object"] == event_url
assert activity.data["object"]["id"] =~ "/join/event/"
assert activity.data["object"]["type"] =~ "Join"
assert activity.data["object"]["participationMessage"] == @join_message
assert activity.data["actor"] == organizer_url
assert activity.data["id"] =~ "/accept/join/"
end
end
describe "handle incoming accept join activities" do
test "it accepts Accept activities for Join activities" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Actor{} = participant_actor = insert(:actor)
%Event{} = event = insert(:event, organizer_actor: organizer, join_options: :restricted)
{:ok, join_activity, participation} =
ActivityPub.join(event, participant_actor, false, %{metadata: %{role: :not_approved}})
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", organizer_url)
|> Map.put("object", participation.url)
{:ok, accept_activity, _} = Transmogrifier.handle_incoming(accept_data)
assert accept_activity.data["object"]["id"] == join_activity.data["id"]
assert accept_activity.data["object"]["id"] =~ "/join/"
assert accept_activity.data["id"] =~ "/accept/join/"
# We don't accept already accepted Accept activities
:error = Transmogrifier.handle_incoming(accept_data)
end
end
describe "handle incoming reject join activities" do
test "it accepts Reject activities for Join activities" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Actor{} = participant_actor = insert(:actor)
%Event{} = event = insert(:event, organizer_actor: organizer, join_options: :restricted)
{:ok, join_activity, participation} = ActivityPub.join(event, participant_actor)
reject_data =
File.read!("test/fixtures/mastodon-reject-activity.json")
|> Jason.decode!()
|> Map.put("actor", organizer_url)
|> Map.put("object", participation.url)
{:ok, reject_activity, _} = Transmogrifier.handle_incoming(reject_data)
assert reject_activity.data["object"]["id"] == join_activity.data["id"]
assert reject_activity.data["object"]["id"] =~ "/join/"
assert reject_activity.data["id"] =~ "/reject/join/"
# We don't accept already rejected Reject activities
assert capture_log([level: :warn], fn ->
assert :error == Transmogrifier.handle_incoming(reject_data)
end) =~
"Unable to process Reject activity \"http://mastodon.example.org/users/admin#rejects/follows/4\". Object \"#{
join_activity.data["id"]
}\" wasn't found."
# Organiser is not present since we use factories directly
assert event.id
|> Events.list_participants_for_event()
|> Map.get(:elements)
|> Enum.map(& &1.role) == [:rejected]
end
end
end

View File

@@ -0,0 +1,60 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.LeaveTest do
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Actors.Actor
alias Mobilizon.Events
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Transmogrifier
describe "handle incoming leave activities on events" do
test "it accepts Leave activities" do
%Actor{url: _organizer_url} = organizer = insert(:actor)
%Actor{url: participant_url} = participant_actor = insert(:actor)
%Event{url: event_url} =
event = insert(:event, organizer_actor: organizer, join_options: :restricted)
organizer_participation =
%Participant{} = insert(:participant, event: event, actor: organizer, role: :creator)
{:ok, _join_activity, _participation} = ActivityPub.join(event, participant_actor)
join_data =
File.read!("test/fixtures/mobilizon-leave-activity.json")
|> Jason.decode!()
|> Map.put("actor", participant_url)
|> Map.put("object", event_url)
assert {:ok, activity, _} = Transmogrifier.handle_incoming(join_data)
assert activity.data["object"] == event_url
assert activity.data["actor"] == participant_url
# The only participant left is the organizer
assert event.id
|> Events.list_participants_for_event()
|> Map.get(:elements)
|> Enum.map(& &1.id) ==
[organizer_participation.id]
end
test "it refuses Leave activities when actor is the only organizer" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Event{url: event_url} =
event = insert(:event, organizer_actor: organizer, join_options: :restricted)
%Participant{} = insert(:participant, event: event, actor: organizer, role: :creator)
join_data =
File.read!("test/fixtures/mobilizon-leave-activity.json")
|> Jason.decode!()
|> Map.put("actor", organizer_url)
|> Map.put("object", event_url)
assert :error = Transmogrifier.handle_incoming(join_data)
end
end
end

View File

@@ -0,0 +1,85 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.UndoTest do
use Mobilizon.DataCase
import Mobilizon.Factory
import Mox
alias Mobilizon.Actors
alias Mobilizon.Discussions.Comment
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
alias Mobilizon.Service.HTTP.ActivityPub.Mock
describe "handle incoming undo activities" do
test "it works for incoming unannounces with an existing notice" do
comment = insert(:comment)
announce_data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("object", comment.url)
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://framapiaf.org/users/Framasoft"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, _, %Comment{}} = Transmogrifier.handle_incoming(announce_data)
data =
File.read!("test/fixtures/mastodon-undo-announce.json")
|> Jason.decode!()
|> Map.put("object", announce_data)
|> Map.put("actor", announce_data["actor"])
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["type"] == "Undo"
assert data["object"]["type"] == "Announce"
assert data["object"]["object"] == comment.url
assert data["object"]["id"] ==
"https://framapiaf.org/users/peertube/statuses/104584600044284729/activity"
end
test "it works for incomming unfollows with an existing follow" do
actor = insert(:actor)
follow_data =
File.read!("test/fixtures/mastodon-follow-activity.json")
|> Jason.decode!()
|> Map.put("object", actor.url)
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
|> Map.put("id", "https://social.tcit.fr/users/tcit")
Mock
|> expect(:call, fn
%{method: :get, url: "https://social.tcit.fr/users/tcit"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, %Activity{data: _, local: false}, _} = Transmogrifier.handle_incoming(follow_data)
data =
File.read!("test/fixtures/mastodon-unfollow-activity.json")
|> Jason.decode!()
|> Map.put("object", follow_data)
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["type"] == "Undo"
assert data["object"]["type"] == "Follow"
assert data["object"]["object"] == actor.url
assert data["actor"] == "https://social.tcit.fr/users/tcit"
{:ok, followed} = Actors.get_actor_by_url(data["actor"])
refute Actors.is_following(followed, actor)
end
end
end

View File

@@ -11,11 +11,12 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
import Mobilizon.Factory
import ExUnit.CaptureLog
import Mock
import Mox
alias Mobilizon.{Actors, Conversations, Events}
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Conversations.Comment
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.{Actors, Discussions, Events}
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Resources.Resource
alias Mobilizon.Todos.{Todo, TodoList}
@@ -25,13 +26,10 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.GraphQL.API
alias Mobilizon.Service.HTTP.ActivityPub.Mock
alias Mobilizon.Tombstone
alias Mobilizon.Web.Endpoint
setup_all do
HTTPoison.start()
end
describe "handle incoming events" do
test "it works for incoming events" do
use_cassette "activity_pub/fetch_mobilizon_post_activity" do
@@ -67,7 +65,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
assert object["actor"] == "https://test.mobilizon.org/@Alicia"
assert object["location"]["name"] == "Locaux de Framasoft"
assert object["attributedTo"] == "https://test.mobilizon.org/@Alicia"
# assert object["attributedTo"] == "https://test.mobilizon.org/@Alicia"
assert event.physical_address.street == "10 Rue Jangot"
@@ -79,172 +77,47 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
{:ok, %Actor{}} = Actors.get_actor_by_url(object["actor"])
end
end
end
describe "handle incoming comments" do
test "it ignores an incoming comment if we already have it" do
comment = insert(:comment)
test "it works for incoming events for local groups" do
%Actor{url: group_url, id: group_id} = group = insert(:group)
activity = %{
"type" => "Create",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"actor" => comment.actor.url,
"object" => Convertible.model_to_as(comment)
}
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
|> Map.put("object", activity["object"])
assert {:ok, nil, _} = Transmogrifier.handle_incoming(data)
end
test "it fetches replied-to activities if we don't have them" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
object =
data["object"]
|> Map.put("inReplyTo", "https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94")
data =
data
|> Map.put("object", object)
{:ok, returned_activity, _} = Transmogrifier.handle_incoming(data)
%Comment{} =
origin_comment =
Conversations.get_comment_from_url(
"https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94"
%Actor{url: actor_url, id: actor_id} =
actor =
insert(:actor,
domain: "test.mobilizon.org",
url: "https://test.mobilizon.org/@member",
preferred_username: "member"
)
assert returned_activity.data["object"]["inReplyTo"] ==
"https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94"
with_mock ActivityPub, [:passthrough],
get_or_fetch_actor_by_url: fn url ->
case url do
^group_url -> {:ok, group}
^actor_url -> {:ok, actor}
end
end do
data = File.read!("test/fixtures/mobilizon-post-activity-group.json") |> Jason.decode!()
assert returned_activity.data["object"]["inReplyTo"] == origin_comment.url
end
object =
data["object"] |> Map.put("actor", actor_url) |> Map.put("attributedTo", group_url)
test "it does not crash if the object in inReplyTo can't be fetched" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
data =
data
|> Map.put("actor", actor_url)
|> Map.put("attributedTo", group_url)
|> Map.put("object", object)
object =
data["object"]
|> Map.put("inReplyTo", "https://404.site/whatever")
assert {:ok, %Activity{data: activity_data, local: false}, %Event{} = event} =
Transmogrifier.handle_incoming(data)
data =
data
|> Map.put("object", object)
assert capture_log([level: :warn], fn ->
{:ok, _returned_activity, _entity} = Transmogrifier.handle_incoming(data)
end) =~ "[warn] Parent object is something we don't handle"
end
test "it works for incoming notices" do
use_cassette "activity_pub/mastodon_post_activity" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["id"] ==
"https://framapiaf.org/users/admin/statuses/99512778738411822/activity"
assert data["to"] == [
"https://www.w3.org/ns/activitystreams#Public",
"https://framapiaf.org/users/tcit"
]
# assert data["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://mobilizon.com/@tcit"
# ]
assert data["actor"] == "https://framapiaf.org/users/admin"
object = data["object"]
assert object["id"] == "https://framapiaf.org/users/admin/statuses/99512778738411822"
assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
# assert object["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://localtesting.pleroma.lol/users/lain"
# ]
assert object["actor"] == "https://framapiaf.org/users/admin"
assert object["attributedTo"] == "https://framapiaf.org/users/admin"
{:ok, %Actor{}} = Actors.get_actor_by_url(object["actor"])
assert event.organizer_actor_id == actor_id
assert event.attributed_to_id == group_id
assert activity_data["actor"] == actor_url
assert activity_data["attributedTo"] == group_url
assert activity_data["object"]["actor"] == actor_url
assert activity_data["object"]["attributedTo"] == group_url
end
end
test "it works for incoming notices with hashtags" do
use_cassette "activity_pub/mastodon_activity_hashtag" do
data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert Enum.at(data["object"]["tag"], 0)["name"] == "@tcit@framapiaf.org"
assert Enum.at(data["object"]["tag"], 1)["name"] == "#moo"
end
end
# test "it works for incoming notices with contentMap" do
# data =
# File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
# assert data["object"]["content"] ==
# "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
# end
# test "it works for incoming notices with to/cc not being an array (kroeg)" do
# data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
# assert data["object"]["content"] ==
# "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
# end
# test "it works for incoming announces with actor being inlined (kroeg)" do
# data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
# assert data["actor"] == "https://puckipedia.com/"
# end
# test "it works for incoming notices with tag not being an array (kroeg)" do
# data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
# assert data["object"]["emoji"] == %{
# "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
# }
# data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Jason.decode!()
# {:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
# assert "test" in data["object"]["tag"]
# end
test "it works for incoming notices with url not being a string (prismo)" do
data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!()
assert {:error, :not_supported} == Transmogrifier.handle_incoming(data)
# Pages are not supported
# {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
# assert data["object"]["url"] == "https://prismo.news/posts/83"
end
end
describe "handle incoming todo lists" do
@@ -463,7 +336,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
activity = %{
"type" => "Add",
"to" => [group.url],
"to" => [group.members_url],
"actor" => actor.url,
"target" => group.resources_url,
"object" => Convertible.model_to_as(resource)
@@ -491,7 +364,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
activity = %{
"type" => "Add",
"to" => [group.url],
"to" => [group.members_url],
"actor" => creator.url,
"target" => group.resources_url,
"object" => %{
@@ -534,7 +407,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
activity = %{
"type" => "Add",
"to" => [group.url],
"to" => [group.members_url],
"actor" => creator.url,
"target" => group.resources_url,
"object" => %{
@@ -586,7 +459,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
activity = %{
"type" => "Add",
"to" => [group.url],
"to" => [group.members_url],
"actor" => creator.url,
"target" => parent_resource.url,
"object" => %{
@@ -631,7 +504,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
activity = %{
"type" => "Add",
"to" => [group.url],
"to" => [group.members_url],
"actor" => creator.url,
"target" => group.resources_url,
"object" => %{
@@ -665,7 +538,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
activity = %{
"type" => "Add",
"to" => [group.url],
"to" => [group.members_url],
"actor" => creator.url,
"target" => group.resources_url,
"object" => %{
@@ -787,43 +660,49 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
describe "handle incoming follow announces" do
test "it works for incoming announces" do
use_cassette "activity_pub/mastodon_announce_activity" do
data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
status_data = File.read!("test/fixtures/mastodon-status.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
Mock
|> expect(:call, fn
%{method: :get, url: "https://framapiaf.org/users/peertube/statuses/104584600044284729"},
_opts ->
{:ok, %Tesla.Env{status: 200, body: status_data}}
end)
assert data["actor"] == "https://framapiaf.org/users/Framasoft"
assert data["type"] == "Announce"
{:ok, _, %Comment{actor: %Actor{url: actor_url}, url: comment_url}} =
Transmogrifier.handle_incoming(data)
assert data["id"] ==
"https://framapiaf.org/users/Framasoft/statuses/102501959686438400/activity"
assert actor_url == "https://framapiaf.org/users/peertube"
assert data["object"] ==
"https://framapiaf.org/users/Framasoft/statuses/102501959686438400"
assert %Comment{} = Conversations.get_comment_from_url(data["object"])
end
assert comment_url ==
"https://framapiaf.org/users/peertube/statuses/104584600044284729"
end
test "it works for incoming announces with an existing activity" do
use_cassette "activity_pub/mastodon_announce_existing_activity" do
comment = insert(:comment)
%Comment{url: comment_url, actor: %Actor{url: actor_url} = actor} = insert(:comment)
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("object", comment.url)
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("object", comment_url)
assert data["actor"] == "https://framapiaf.org/users/Framasoft"
assert data["type"] == "Announce"
Mock
|> expect(:call, fn
%{method: :get, url: actor_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
assert data["id"] ==
"https://framapiaf.org/users/Framasoft/statuses/102501959686438400/activity"
{:ok, _, %Comment{actor: %Actor{url: actor_url}, url: comment_url_2}} =
Transmogrifier.handle_incoming(data)
assert data["object"] == comment.url
end
assert actor_url == actor.url
assert comment_url == comment_url_2
end
end
@@ -926,12 +805,12 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
|> Map.put("object", object)
|> Map.put("actor", actor_url)
assert Conversations.get_comment_from_url(comment_url)
assert is_nil(Conversations.get_comment_from_url(comment_url).deleted_at)
assert Discussions.get_comment_from_url(comment_url)
assert is_nil(Discussions.get_comment_from_url(comment_url).deleted_at)
{:ok, %Activity{local: false}, _} = Transmogrifier.handle_incoming(data)
refute is_nil(Conversations.get_comment_from_url(comment_url).deleted_at)
refute is_nil(Discussions.get_comment_from_url(comment_url).deleted_at)
end
test "it fails for incoming deletes with spoofed origin" do
@@ -942,7 +821,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
|> Jason.decode!()
|> Map.put("object", comment.url)
{:ok, %Activity{local: false}, _} = Transmogrifier.handle_incoming(announce_data)
{:ok, _, _} = Transmogrifier.handle_incoming(announce_data)
data =
File.read!("test/fixtures/mastodon-delete.json")
@@ -958,9 +837,11 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
:error = Transmogrifier.handle_incoming(data)
assert Conversations.get_comment_from_url(comment.url)
assert Discussions.get_comment_from_url(comment.url)
end
setup :set_mox_from_context
test "it works for incoming actor deletes" do
%Actor{url: url} = actor = insert(:actor, url: "https://framapiaf.org/users/admin")
%Event{url: event1_url} = event1 = insert(:event, organizer_actor: actor)
@@ -971,7 +852,13 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
data =
File.read!("test/fixtures/mastodon-delete-user.json")
|> Poison.decode!()
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok, %Tesla.Env{status: 410, body: "Gone"}}
end)
{:ok, _activity, _actor} = Transmogrifier.handle_incoming(data)
assert %{success: 1, failure: 0} == Oban.drain_queue(:background)
@@ -980,19 +867,31 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
assert {:error, :event_not_found} = Events.get_event(event1.id)
# Tombstone are cascade deleted, seems correct for now
# assert %Tombstone{} = Tombstone.find_tombstone(event1_url)
assert %Comment{deleted_at: deleted_at} = Conversations.get_comment(comment1.id)
assert %Comment{deleted_at: deleted_at} = Discussions.get_comment(comment1.id)
refute is_nil(deleted_at)
# assert %Tombstone{} = Tombstone.find_tombstone(comment1_url)
end
test "it fails for incoming actor deletes with spoofed origin" do
%{url: url} = insert(:actor)
deleted_actor_url = "https://framapiaf.org/users/admin"
data =
File.read!("test/fixtures/mastodon-delete-user.json")
|> Poison.decode!()
|> Jason.decode!()
|> Map.put("actor", url)
deleted_actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
|> Map.put("id", deleted_actor_url)
Mock
|> expect(:call, fn
%{url: ^deleted_actor_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: deleted_actor_data}}
end)
assert capture_log(fn ->
assert :error == Transmogrifier.handle_incoming(data)
end) =~ "Object origin check failed"
@@ -1001,62 +900,29 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
end
end
describe "handle incoming undo activities" do
test "it works for incoming unannounces with an existing notice" do
use_cassette "activity_pub/mastodon_unannounce_activity" do
comment = insert(:comment)
describe "handle tombstones" do
setup :verify_on_exit!
announce_data =
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("object", comment.url)
# This is a hack to handle fetching tombstones
test "works for incoming tombstone creations" do
%Comment{url: comment_url} = comment = insert(:comment, local: false)
tombstone = build(:tombstone, uri: comment_url)
data = Convertible.model_to_as(tombstone)
{:ok, %Activity{data: announce_data, local: false}, _} =
Transmogrifier.handle_incoming(announce_data)
activity = %{
"type" => "Create",
"to" => data["to"],
"cc" => data["cc"],
"actor" => data["actor"],
"attributedTo" => data["attributedTo"],
"object" => data
}
data =
File.read!("test/fixtures/mastodon-undo-announce.json")
|> Jason.decode!()
|> Map.put("object", announce_data)
|> Map.put("actor", announce_data["actor"])
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["type"] == "Undo"
assert data["object"]["type"] == "Announce"
assert data["object"]["object"] == comment.url
assert data["object"]["id"] ==
"https://framapiaf.org/users/Framasoft/statuses/102501959686438400/activity"
end
end
test "it works for incomming unfollows with an existing follow" do
use_cassette "activity_pub/unfollow_existing_follow_activity" do
actor = insert(:actor)
follow_data =
File.read!("test/fixtures/mastodon-follow-activity.json")
|> Jason.decode!()
|> Map.put("object", actor.url)
{:ok, %Activity{data: _, local: false}, _} = Transmogrifier.handle_incoming(follow_data)
data =
File.read!("test/fixtures/mastodon-unfollow-activity.json")
|> Jason.decode!()
|> Map.put("object", follow_data)
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["type"] == "Undo"
assert data["object"]["type"] == "Follow"
assert data["object"]["object"] == actor.url
assert data["actor"] == "https://social.tcit.fr/users/tcit"
{:ok, followed} = Actors.get_actor_by_url(data["actor"])
refute Actors.is_following(followed, actor)
end
{:ok, _activity, %Comment{url: comment_url}} = Transmogrifier.handle_incoming(activity)
assert comment_url == comment.url
assert %Comment{} = comment = Discussions.get_comment_from_url(comment_url)
assert %Tombstone{} = Tombstone.find_tombstone(comment_url)
refute is_nil(comment.deleted_at)
end
end
@@ -1136,120 +1002,6 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
# refute User.blocks?(blocker, user)
# end
describe "handle incoming follow accept activities" do
test "it works for incoming accepts which were pre-accepted" do
follower = insert(:actor)
followed = insert(:actor)
refute Actors.is_following(follower, followed)
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
assert Actors.is_following(follower, followed)
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
object =
accept_data["object"]
|> Map.put("actor", follower.url)
|> Map.put("id", follow_activity.data["id"])
accept_data = Map.put(accept_data, "object", object)
{:ok, activity, _} = Transmogrifier.handle_incoming(accept_data)
refute activity.local
assert activity.data["object"]["id"] == follow_activity.data["id"]
{:ok, follower} = Actors.get_actor_by_url(follower.url)
assert Actors.is_following(follower, followed)
end
test "it works for incoming accepts which are referenced by IRI only" do
follower = insert(:actor)
followed = insert(:actor)
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
|> Map.put("object", follow_activity.data["id"])
{:ok, activity, _} = Transmogrifier.handle_incoming(accept_data)
assert activity.data["object"]["id"] == follow_activity.data["id"]
assert activity.data["object"]["id"] =~ "/follow/"
assert activity.data["id"] =~ "/accept/follow/"
{:ok, follower} = Actors.get_actor_by_url(follower.url)
assert Actors.is_following(follower, followed)
end
test "it fails for incoming accepts which cannot be correlated" do
follower = insert(:actor)
followed = insert(:actor)
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
accept_data =
Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.url))
:error = Transmogrifier.handle_incoming(accept_data)
{:ok, follower} = Actors.get_actor_by_url(follower.url)
refute Actors.is_following(follower, followed)
end
end
describe "handle incoming follow reject activities" do
test "it fails for incoming rejects which cannot be correlated" do
follower = insert(:actor)
followed = insert(:actor)
accept_data =
File.read!("test/fixtures/mastodon-reject-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
accept_data =
Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.url))
:error = Transmogrifier.handle_incoming(accept_data)
{:ok, follower} = Actors.get_actor_by_url(follower.url)
refute Actors.is_following(follower, followed)
end
test "it works for incoming rejects which are referenced by IRI only" do
follower = insert(:actor)
followed = insert(:actor)
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
assert Actors.is_following(follower, followed)
reject_data =
File.read!("test/fixtures/mastodon-reject-activity.json")
|> Jason.decode!()
|> Map.put("actor", followed.url)
|> Map.put("object", follow_activity.data["id"])
{:ok, %Activity{data: _}, _} = Transmogrifier.handle_incoming(reject_data)
refute Actors.is_following(follower, followed)
end
end
describe "handle incoming flag activities" do
test "it accepts Flag activities" do
%Actor{url: reporter_url} = Relay.get_actor()
@@ -1276,201 +1028,6 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
end
end
describe "handle incoming join activities" do
@join_message "I want to get in!"
test "it accepts Join activities" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Actor{url: participant_url} = _participant = insert(:actor)
%Event{url: event_url} = _event = insert(:event, organizer_actor: organizer)
join_data =
File.read!("test/fixtures/mobilizon-join-activity.json")
|> Jason.decode!()
|> Map.put("actor", participant_url)
|> Map.put("object", event_url)
|> Map.put("participationMessage", @join_message)
assert {:ok, activity, %Participant{} = participant} =
Transmogrifier.handle_incoming(join_data)
assert participant.metadata.message == @join_message
assert participant.role == :participant
assert activity.data["type"] == "Accept"
assert activity.data["object"]["object"] == event_url
assert activity.data["object"]["id"] =~ "/join/event/"
assert activity.data["object"]["type"] =~ "Join"
assert activity.data["object"]["participationMessage"] == @join_message
assert activity.data["actor"] == organizer_url
assert activity.data["id"] =~ "/accept/join/"
end
end
describe "handle incoming accept join activities" do
test "it accepts Accept activities for Join activities" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Actor{} = participant_actor = insert(:actor)
%Event{} = event = insert(:event, organizer_actor: organizer, join_options: :restricted)
{:ok, join_activity, participation} =
ActivityPub.join(event, participant_actor, false, %{metadata: %{role: :not_approved}})
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", organizer_url)
|> Map.put("object", participation.url)
{:ok, accept_activity, _} = Transmogrifier.handle_incoming(accept_data)
assert accept_activity.data["object"]["id"] == join_activity.data["id"]
assert accept_activity.data["object"]["id"] =~ "/join/"
assert accept_activity.data["id"] =~ "/accept/join/"
# We don't accept already accepted Accept activities
:error = Transmogrifier.handle_incoming(accept_data)
end
end
describe "handle incoming reject join activities" do
test "it accepts Reject activities for Join activities" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Actor{} = participant_actor = insert(:actor)
%Event{} = event = insert(:event, organizer_actor: organizer, join_options: :restricted)
{:ok, join_activity, participation} = ActivityPub.join(event, participant_actor)
reject_data =
File.read!("test/fixtures/mastodon-reject-activity.json")
|> Jason.decode!()
|> Map.put("actor", organizer_url)
|> Map.put("object", participation.url)
{:ok, reject_activity, _} = Transmogrifier.handle_incoming(reject_data)
assert reject_activity.data["object"]["id"] == join_activity.data["id"]
assert reject_activity.data["object"]["id"] =~ "/join/"
assert reject_activity.data["id"] =~ "/reject/join/"
# We don't accept already rejected Reject activities
assert capture_log([level: :warn], fn ->
assert :error == Transmogrifier.handle_incoming(reject_data)
end) =~
"Unable to process Reject activity \"http://mastodon.example.org/users/admin#rejects/follows/4\". Object \"#{
join_activity.data["id"]
}\" wasn't found."
# Organiser is not present since we use factories directly
assert event.id
|> Events.list_participants_for_event()
|> Map.get(:elements)
|> Enum.map(& &1.role) == [:rejected]
end
end
describe "handle incoming leave activities on events" do
test "it accepts Leave activities" do
%Actor{url: _organizer_url} = organizer = insert(:actor)
%Actor{url: participant_url} = participant_actor = insert(:actor)
%Event{url: event_url} =
event = insert(:event, organizer_actor: organizer, join_options: :restricted)
organizer_participation =
%Participant{} = insert(:participant, event: event, actor: organizer, role: :creator)
{:ok, _join_activity, _participation} = ActivityPub.join(event, participant_actor)
join_data =
File.read!("test/fixtures/mobilizon-leave-activity.json")
|> Jason.decode!()
|> Map.put("actor", participant_url)
|> Map.put("object", event_url)
assert {:ok, activity, _} = Transmogrifier.handle_incoming(join_data)
assert activity.data["object"] == event_url
assert activity.data["actor"] == participant_url
# The only participant left is the organizer
assert event.id
|> Events.list_participants_for_event()
|> Map.get(:elements)
|> Enum.map(& &1.id) ==
[organizer_participation.id]
end
test "it refuses Leave activities when actor is the only organizer" do
%Actor{url: organizer_url} = organizer = insert(:actor)
%Event{url: event_url} =
event = insert(:event, organizer_actor: organizer, join_options: :restricted)
%Participant{} = insert(:participant, event: event, actor: organizer, role: :creator)
join_data =
File.read!("test/fixtures/mobilizon-leave-activity.json")
|> Jason.decode!()
|> Map.put("actor", organizer_url)
|> Map.put("object", event_url)
assert :error = Transmogrifier.handle_incoming(join_data)
end
end
describe "handle Invite activities on group" do
test "it accepts Invite activities" do
%Actor{url: group_url, id: group_id} = group = insert(:group)
%Actor{url: group_admin_url, id: group_admin_id} = group_admin = insert(:actor)
%Member{} =
_group_admin_member =
insert(:member, parent: group, actor: group_admin, role: :administrator)
%Actor{url: invitee_url, id: invitee_id} = _invitee = insert(:actor)
invite_data =
File.read!("test/fixtures/mobilizon-invite-activity.json")
|> Jason.decode!()
|> Map.put("actor", group_admin_url)
|> Map.put("object", group_url)
|> Map.put("target", invitee_url)
assert {:ok, activity, %Member{}} = Transmogrifier.handle_incoming(invite_data)
assert %Member{} = member = Actors.get_member_by_url(invite_data["id"])
assert member.actor.id == invitee_id
assert member.parent.id == group_id
assert member.role == :invited
assert member.invited_by_id == group_admin_id
end
test "it refuses Invite activities for " do
%Actor{url: group_url, id: group_id} = group = insert(:group)
%Actor{url: group_admin_url, id: group_admin_id} = group_admin = insert(:actor)
%Member{} =
_group_admin_member =
insert(:member, parent: group, actor: group_admin, role: :administrator)
%Actor{url: invitee_url, id: invitee_id} = _invitee = insert(:actor)
invite_data =
File.read!("test/fixtures/mobilizon-invite-activity.json")
|> Jason.decode!()
|> Map.put("actor", group_admin_url)
|> Map.put("object", group_url)
|> Map.put("target", invitee_url)
assert {:ok, activity, %Member{}} = Transmogrifier.handle_incoming(invite_data)
assert %Member{} = member = Actors.get_member_by_url(invite_data["id"])
assert member.actor.id == invitee_id
assert member.parent.id == group_id
assert member.role == :invited
assert member.invited_by_id == group_admin_id
end
end
describe "prepare outgoing" do
test "it turns mentions into tags" do
actor = insert(:actor)
@@ -1501,7 +1058,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
assert Enum.member?(object["tag"], expected_mention)
end
test "it adds the json-ld context and the conversation property" do
test "it adds the json-ld context and the discussion property" do
actor = insert(:actor)
{:ok, activity, _} = API.Comments.create_comment(%{actor_id: actor.id, text: "hey"})
@@ -1558,24 +1115,40 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
describe "actor origin check" do
test "it rejects objects with a bogus origin" do
use_cassette "activity_pub/object_bogus_origin" do
{:error, _} = ActivityPub.fetch_object_from_url("https://info.pleroma.site/activity.json")
end
data =
File.read!("test/fixtures/https__info.pleroma.site_activity.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://info.pleroma.site/activity.json"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
{:error, _} = ActivityPub.fetch_object_from_url("https://info.pleroma.site/activity.json")
end
test "it rejects activities which reference objects with bogus origins" do
use_cassette "activity_pub/activity_object_bogus" do
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "https://framapiaf.org/users/admin/activities/1234",
"actor" => "https://framapiaf.org/users/admin",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"object" => "https://info.pleroma.site/activity.json",
"type" => "Announce"
}
data =
File.read!("test/fixtures/https__info.pleroma.site_activity.json")
|> Jason.decode!()
:error = Transmogrifier.handle_incoming(data)
end
Mock
|> expect(:call, fn
%{method: :get, url: "https://info.pleroma.site/activity.json"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "https://framapiaf.org/users/admin/activities/1234",
"actor" => "https://framapiaf.org/users/admin",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"object" => "https://info.pleroma.site/activity.json",
"type" => "Announce"
}
:error = Transmogrifier.handle_incoming(data)
end
end
end

View File

@@ -10,15 +10,11 @@ defmodule Mobilizon.Federation.ActivityPub.UtilsTest do
alias Mobilizon.Web.Endpoint
alias Mobilizon.Web.Router.Helpers, as: Routes
setup_all do
HTTPoison.start()
end
describe "make" do
test "comment data from struct" do
comment = insert(:comment)
tag = insert(:tag, title: "MyTag")
reply = insert(:comment, in_reply_to_comment: comment, tags: [tag])
reply = insert(:comment, in_reply_to_comment: comment, tags: [tag], attributed_to: nil)
assert %{
"type" => "Note",
@@ -42,8 +38,8 @@ defmodule Mobilizon.Federation.ActivityPub.UtilsTest do
end
test "comment data from map" do
comment = insert(:comment)
reply = insert(:comment, in_reply_to_comment: comment)
comment = insert(:comment, attributed_to: nil)
reply = insert(:comment, in_reply_to_comment: comment, attributed_to: nil)
to = ["https://www.w3.org/ns/activitystreams#Public"]
comment_data = Converter.Comment.model_to_as(reply)
assert comment_data["type"] == "Note"

View File

@@ -0,0 +1,31 @@
{
"type": "FeatureCollection",
"version": "draft",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [4.842569, 45.751718] },
"properties": {
"label": "10 Rue Jangot 69007 Lyon",
"score": 0.9999999999926557,
"housenumber": "10",
"id": "69387_3650_00010",
"type": "housenumber",
"x": 843232.29,
"y": 6518573.31,
"importance": 0.5454797306366062,
"name": "10 Rue Jangot",
"postcode": "69007",
"citycode": "69387",
"city": "Lyon",
"district": "Lyon 7e Arrondissement",
"context": "69, Rh\u00f4ne, Auvergne-Rh\u00f4ne-Alpes",
"street": "Rue Jangot",
"distance": 0
}
}
],
"attribution": "BAN",
"licence": "ETALAB-2.0",
"limit": 1
}

View File

@@ -0,0 +1,49 @@
{
"type": "FeatureCollection",
"version": "draft",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [4.842569, 45.751718] },
"properties": {
"label": "10 Rue Jangot 69007 Lyon",
"score": 0.8677708846033279,
"housenumber": "10",
"id": "69387_3650_00010",
"type": "housenumber",
"x": 843232.29,
"y": 6518573.31,
"importance": 0.5454797306366062,
"name": "10 Rue Jangot",
"postcode": "69007",
"citycode": "69387",
"city": "Lyon",
"district": "Lyon 7e Arrondissement",
"context": "69, Rh\u00f4ne, Auvergne-Rh\u00f4ne-Alpes",
"street": "Rue Jangot"
}
},
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [2.440319, 50.371266] },
"properties": {
"label": "Rue Jangon 62127 Bailleul-aux-Cornailles",
"score": 0.5269641371131077,
"id": "62070_0100",
"type": "street",
"x": 660129.18,
"y": 7030540.46,
"importance": 0.25814396978264664,
"name": "Rue Jangon",
"postcode": "62127",
"citycode": "62070",
"city": "Bailleul-aux-Cornailles",
"context": "62, Pas-de-Calais, Hauts-de-France"
}
}
],
"attribution": "BAN",
"licence": "ETALAB-2.0",
"query": "10 Rue Jangot",
"limit": 5
}

View File

@@ -0,0 +1,5 @@
{
"error_message": "The provided API key is invalid.",
"results": [],
"status": "REQUEST_DENIED"
}

View File

@@ -0,0 +1,109 @@
{
"plus_code": {
"compound_code": "QR2V+M2 Lyon, France",
"global_code": "8FQ6QR2V+M2"
},
"results": [
{
"address_components": [
{
"long_name": "10bis",
"short_name": "10bis",
"types": ["street_number"]
},
{
"long_name": "Rue Jangot",
"short_name": "Rue Jangot",
"types": ["route"]
},
{
"long_name": "Lyon",
"short_name": "Lyon",
"types": ["locality", "political"]
},
{
"long_name": "Rhône",
"short_name": "Rhône",
"types": ["administrative_area_level_2", "political"]
},
{
"long_name": "Auvergne-Rhône-Alpes",
"short_name": "Auvergne-Rhône-Alpes",
"types": ["administrative_area_level_1", "political"]
},
{
"long_name": "France",
"short_name": "FR",
"types": ["country", "political"]
},
{
"long_name": "69007",
"short_name": "69007",
"types": ["postal_code"]
}
],
"formatted_address": "10bis Rue Jangot, 69007 Lyon, France",
"geometry": {
"location": { "lat": 45.751725, "lng": 4.8424966 },
"location_type": "ROOFTOP",
"viewport": {
"northeast": { "lat": 45.7530739802915, "lng": 4.843845580291503 },
"southwest": { "lat": 45.7503760197085, "lng": 4.841147619708499 }
}
},
"place_id": "ChIJrW0QikTq9EcR96jk2OnO75w",
"plus_code": {
"compound_code": "QR2R+MX Lyon, France",
"global_code": "8FQ6QR2R+MX"
},
"types": ["street_address"]
},
{
"address_components": [
{ "long_name": "9", "short_name": "9", "types": ["street_number"] },
{
"long_name": "Rue Jangot",
"short_name": "Rue Jangot",
"types": ["route"]
},
{
"long_name": "Lyon",
"short_name": "Lyon",
"types": ["locality", "political"]
},
{
"long_name": "Rhône",
"short_name": "Rhône",
"types": ["administrative_area_level_2", "political"]
},
{
"long_name": "Auvergne-Rhône-Alpes",
"short_name": "Auvergne-Rhône-Alpes",
"types": ["administrative_area_level_1", "political"]
},
{
"long_name": "France",
"short_name": "FR",
"types": ["country", "political"]
},
{
"long_name": "69007",
"short_name": "69007",
"types": ["postal_code"]
}
],
"formatted_address": "9 Rue Jangot, 69007 Lyon, France",
"geometry": {
"location": { "lat": 45.7518165, "lng": 4.8427168 },
"location_type": "RANGE_INTERPOLATED",
"viewport": {
"northeast": { "lat": 45.7531654802915, "lng": 4.844065780291502 },
"southwest": { "lat": 45.7504675197085, "lng": 4.841367819708497 }
}
},
"place_id": "EiA5IFJ1ZSBKYW5nb3QsIDY5MDA3IEx5b24sIEZyYW5jZSIaEhgKFAoSCR8N2ItE6vRHEW9tyPnhQsUIEAk",
"types": ["street_address"]
}
],
"status": "OK"
}

View File

@@ -0,0 +1,62 @@
{
"html_attributions": [],
"result": {
"address_components": [
{
"long_name": "10bis",
"short_name": "10bis",
"types": ["street_number"]
},
{
"long_name": "Rue Jangot",
"short_name": "Rue Jangot",
"types": ["route"]
},
{
"long_name": "Lyon",
"short_name": "Lyon",
"types": ["locality", "political"]
},
{
"long_name": "Rhône",
"short_name": "Rhône",
"types": ["administrative_area_level_2", "political"]
},
{
"long_name": "Auvergne-Rhône-Alpes",
"short_name": "Auvergne-Rhône-Alpes",
"types": ["administrative_area_level_1", "political"]
},
{
"long_name": "France",
"short_name": "FR",
"types": ["country", "political"]
},
{ "long_name": "69007", "short_name": "69007", "types": ["postal_code"] }
],
"adr_address": "\u003cspan class=\"street-address\"\u003e10bis Rue Jangot\u003c/span\u003e, \u003cspan class=\"postal-code\"\u003e69007\u003c/span\u003e \u003cspan class=\"locality\"\u003eLyon\u003c/span\u003e, \u003cspan class=\"country-name\"\u003eFrance\u003c/span\u003e",
"formatted_address": "10bis Rue Jangot, 69007 Lyon, France",
"geometry": {
"location": { "lat": 45.751725, "lng": 4.8424966 },
"viewport": {
"northeast": { "lat": 45.7531097802915, "lng": 4.843951380291502 },
"southwest": { "lat": 45.7504118197085, "lng": 4.841253419708497 }
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png",
"id": "4a3482a7a74c6203048adf713b736186c4ace7cd",
"name": "10bis Rue Jangot",
"place_id": "ChIJrW0QikTq9EcR96jk2OnO75w",
"plus_code": {
"compound_code": "QR2R+MX Lyon, France",
"global_code": "8FQ6QR2R+MX"
},
"reference": "ChIJrW0QikTq9EcR96jk2OnO75w",
"scope": "GOOGLE",
"types": ["street_address"],
"url": "https://maps.google.com/?q=10bis+Rue+Jangot,+69007+Lyon,+France&ftid=0x47f4ea448a106dad:0x9cefcee9d8e4a8f7",
"utc_offset": 120,
"vicinity": "Lyon"
},
"status": "OK"
}

View File

@@ -0,0 +1,55 @@
{
"results": [
{
"address_components": [
{ "long_name": "10", "short_name": "10", "types": ["street_number"] },
{
"long_name": "Rue Jangot",
"short_name": "Rue Jangot",
"types": ["route"]
},
{
"long_name": "Lyon",
"short_name": "Lyon",
"types": ["locality", "political"]
},
{
"long_name": "Rhône",
"short_name": "Rhône",
"types": ["administrative_area_level_2", "political"]
},
{
"long_name": "Auvergne-Rhône-Alpes",
"short_name": "Auvergne-Rhône-Alpes",
"types": ["administrative_area_level_1", "political"]
},
{
"long_name": "France",
"short_name": "FR",
"types": ["country", "political"]
},
{
"long_name": "69007",
"short_name": "69007",
"types": ["postal_code"]
}
],
"formatted_address": "10 Rue Jangot, 69007 Lyon, France",
"geometry": {
"location": { "lat": 45.75164940000001, "lng": 4.8424032 },
"location_type": "ROOFTOP",
"viewport": {
"northeast": { "lat": 45.75299838029151, "lng": 4.843752180291502 },
"southwest": { "lat": 45.75030041970851, "lng": 4.841054219708497 }
}
},
"place_id": "ChIJtW0QikTq9EcRLI4Vy6bRx0U",
"plus_code": {
"compound_code": "QR2R+MX Lyon, France",
"global_code": "8FQ6QR2R+MX"
},
"types": ["street_address"]
}
],
"status": "OK"
}

View File

@@ -0,0 +1,58 @@
{
"html_attributions": [],
"result": {
"address_components": [
{ "long_name": "10", "short_name": "10", "types": ["street_number"] },
{
"long_name": "Rue Jangot",
"short_name": "Rue Jangot",
"types": ["route"]
},
{
"long_name": "Lyon",
"short_name": "Lyon",
"types": ["locality", "political"]
},
{
"long_name": "Rhône",
"short_name": "Rhône",
"types": ["administrative_area_level_2", "political"]
},
{
"long_name": "Auvergne-Rhône-Alpes",
"short_name": "Auvergne-Rhône-Alpes",
"types": ["administrative_area_level_1", "political"]
},
{
"long_name": "France",
"short_name": "FR",
"types": ["country", "political"]
},
{ "long_name": "69007", "short_name": "69007", "types": ["postal_code"] }
],
"adr_address": "\u003cspan class=\"street-address\"\u003e10 Rue Jangot\u003c/span\u003e, \u003cspan class=\"postal-code\"\u003e69007\u003c/span\u003e \u003cspan class=\"locality\"\u003eLyon\u003c/span\u003e, \u003cspan class=\"country-name\"\u003eFrance\u003c/span\u003e",
"formatted_address": "10 Rue Jangot, 69007 Lyon, France",
"geometry": {
"location": { "lat": 45.75164940000001, "lng": 4.842403200000001 },
"viewport": {
"northeast": { "lat": 45.7530412802915, "lng": 4.843668630291503 },
"southwest": { "lat": 45.75034331970851, "lng": 4.840970669708498 }
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png",
"id": "61b9418d092d2ed05ddd65a55dddefda5b9628cc",
"name": "10 Rue Jangot",
"place_id": "ChIJtW0QikTq9EcRLI4Vy6bRx0U",
"plus_code": {
"compound_code": "QR2R+MX Lyon, France",
"global_code": "8FQ6QR2R+MX"
},
"reference": "ChIJtW0QikTq9EcRLI4Vy6bRx0U",
"scope": "GOOGLE",
"types": ["street_address"],
"url": "https://maps.google.com/?q=10+Rue+Jangot,+69007+Lyon,+France&ftid=0x47f4ea448a106db5:0x45c7d1a6cb158e2c",
"utc_offset": 120,
"vicinity": "Lyon"
},
"status": "OK"
}

View File

@@ -0,0 +1,43 @@
{
"info": {
"statuscode": 0,
"copyright": {
"text": "\\u00A9 2019 MapQuest, Inc.",
"imageUrl": "http://api.mqcdn.com/res/mqlogo.gif",
"imageAltText": "\\u00A9 2019 MapQuest, Inc."
},
"messages": []
},
"options": { "maxResults": 1, "thumbMaps": true, "ignoreLatLngInput": false },
"results": [
{
"providedLocation": { "latLng": { "lat": 45.751718, "lng": 4.842569 } },
"locations": [
{
"street": "10 Rue Jangot",
"adminArea6": "",
"adminArea6Type": "Neighborhood",
"adminArea5": "Lyon",
"adminArea5Type": "City",
"adminArea4": "",
"adminArea4Type": "County",
"adminArea3": "Auvergne-Rhône-Alpes",
"adminArea3Type": "State",
"adminArea1": "FR",
"adminArea1Type": "Country",
"postalCode": "69007",
"geocodeQualityCode": "P1AAA",
"geocodeQuality": "POINT",
"dragPoint": false,
"sideOfStreet": "N",
"linkId": "0",
"unknownInput": "",
"type": "s",
"latLng": { "lat": 45.751714, "lng": 4.842566 },
"displayLatLng": { "lat": 45.751714, "lng": 4.842566 },
"mapUrl": "http://open.mapquestapi.com/staticmap/v5/map?key=secret_key&type=map&size=225,160&locations=45.7517141,4.8425657|marker-sm-50318A-1&scalebar=true&zoom=15&rand=-570915433"
}
]
}
]
}

View File

@@ -0,0 +1,47 @@
{
"info": {
"statuscode": 0,
"copyright": {
"text": "\\u00A9 2019 MapQuest, Inc.",
"imageUrl": "http://api.mqcdn.com/res/mqlogo.gif",
"imageAltText": "\\u00A9 2019 MapQuest, Inc."
},
"messages": []
},
"options": {
"maxResults": 10,
"thumbMaps": true,
"ignoreLatLngInput": false
},
"results": [
{
"providedLocation": { "location": "10 rue Jangot" },
"locations": [
{
"street": "10 Rue Jangot",
"adminArea6": "7e",
"adminArea6Type": "Neighborhood",
"adminArea5": "Lyon",
"adminArea5Type": "City",
"adminArea4": "Lyon",
"adminArea4Type": "County",
"adminArea3": "Auvergne-Rhône-Alpes",
"adminArea3Type": "State",
"adminArea1": "FR",
"adminArea1Type": "Country",
"postalCode": "69007",
"geocodeQualityCode": "P1AXX",
"geocodeQuality": "POINT",
"dragPoint": false,
"sideOfStreet": "N",
"linkId": "0",
"unknownInput": "",
"type": "s",
"latLng": { "lat": 45.751714, "lng": 4.842566 },
"displayLatLng": { "lat": 45.751714, "lng": 4.842566 },
"mapUrl": "http://open.mapquestapi.com/staticmap/v5/map?key=secret_key&type=map&size=225,160&locations=45.7517141,4.8425657|marker-sm-50318A-1&scalebar=true&zoom=15&rand=1358091752"
}
]
}
]
}

View File

@@ -0,0 +1,43 @@
{
"type": "FeatureCollection",
"geocoding": {
"version": "0.1.0",
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"licence": "ODbL",
"query": "45.751718,4.842569"
},
"features": [
{
"type": "Feature",
"properties": {
"geocoding": {
"place_id": 41453794,
"osm_type": "node",
"osm_id": 3078260611,
"type": "house",
"accuracy": 0,
"label": "10, Rue Jangot, La Guillotière, Lyon 7e Arrondissement, Lyon, Métropole de Lyon, Departemental constituency of Rhône, Auvergne-Rhône-Alpes, Metropolitan France, 69007, France",
"name": null,
"housenumber": "10",
"street": "Rue Jangot",
"postcode": "69007",
"city": "Lyon",
"county": "Lyon",
"state": "Auvergne-Rhône-Alpes",
"country": "France",
"admin": {
"level2": "France",
"level3": "Metropolitan France",
"level4": "Auvergne-Rhône-Alpes",
"level5": "Departemental constituency of Rhône",
"level6": "Métropole de Lyon",
"level7": "Lyon",
"level8": "Lyon",
"level9": "Lyon 7e Arrondissement"
}
}
},
"geometry": { "type": "Point", "coordinates": [4.8425657, 45.7517141] }
}
]
}

View File

@@ -0,0 +1,42 @@
{
"type": "FeatureCollection",
"geocoding": {
"version": "0.1.0",
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"licence": "ODbL",
"query": "10 rue Jangot"
},
"features": [
{
"type": "Feature",
"properties": {
"geocoding": {
"place_id": 41453794,
"osm_type": "node",
"osm_id": 3078260611,
"type": "house",
"label": "10, Rue Jangot, La Guillotière, Lyon 7e Arrondissement, Lyon, Métropole de Lyon, Departemental constituency of Rhône, Auvergne-Rhône-Alpes, Metropolitan France, 69007, France",
"name": null,
"housenumber": "10",
"street": "Rue Jangot",
"postcode": "69007",
"city": "Lyon",
"county": "Lyon",
"state": "Auvergne-Rhône-Alpes",
"country": "France",
"admin": {
"level2": "France",
"level3": "Metropolitan France",
"level4": "Auvergne-Rhône-Alpes",
"level5": "Departemental constituency of Rhône",
"level6": "Métropole de Lyon",
"level7": "Lyon",
"level8": "Lyon",
"level9": "Lyon 7e Arrondissement"
}
}
},
"geometry": { "type": "Point", "coordinates": [4.8425657, 45.7517141] }
}
]
}

View File

@@ -0,0 +1,37 @@
{
"features": [
{
"geometry": { "coordinates": [4.8425657, 45.7517141], "type": "Point" },
"type": "Feature",
"properties": {
"osm_id": 3078260611,
"osm_type": "N",
"country": "France",
"osm_key": "place",
"housenumber": "10",
"city": "Lyon",
"street": "Rue Jangot",
"osm_value": "house",
"postcode": "69007",
"state": "Auvergne-Rhône-Alpes"
}
},
{
"geometry": { "coordinates": [4.8424254, 45.7517056], "type": "Point" },
"type": "Feature",
"properties": {
"osm_id": 3078260612,
"osm_type": "N",
"country": "France",
"osm_key": "place",
"housenumber": "10bis",
"city": "Lyon",
"street": "Rue Jangot",
"osm_value": "house",
"postcode": "69007",
"state": "Auvergne-Rhône-Alpes"
}
}
],
"type": "FeatureCollection"
}

View File

@@ -0,0 +1,12 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "http://mastodon.example.org/users/admin",
"attachment": [],
"attributedTo": "http://mastodon.example.org/users/admin",
"content": "<p>this post was not actually written by Haelwenn</p>",
"id": "https://info.pleroma.site/activity.json",
"published": "2018-09-01T22:15:00Z",
"tag": [],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Note"
}

91
test/fixtures/mastodon-actor.json vendored Normal file
View File

@@ -0,0 +1,91 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"toot": "http://joinmastodon.org/ns#",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"IdentityProof": "toot:IdentityProof",
"discoverable": "toot:discoverable",
"Device": "toot:Device",
"Ed25519Signature": "toot:Ed25519Signature",
"Ed25519Key": "toot:Ed25519Key",
"Curve25519Key": "toot:Curve25519Key",
"EncryptedMessage": "toot:EncryptedMessage",
"publicKeyBase64": "toot:publicKeyBase64",
"deviceId": "toot:deviceId",
"claim": {
"@type": "@id",
"@id": "toot:claim"
},
"fingerprintKey": {
"@type": "@id",
"@id": "toot:fingerprintKey"
},
"identityKey": {
"@type": "@id",
"@id": "toot:identityKey"
},
"devices": {
"@type": "@id",
"@id": "toot:devices"
},
"messageFranking": "toot:messageFranking",
"messageType": "toot:messageType",
"cipherText": "toot:cipherText",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://framapiaf.org/users/peertube",
"type": "Person",
"following": "https://framapiaf.org/users/peertube/following",
"followers": "https://framapiaf.org/users/peertube/followers",
"inbox": "https://framapiaf.org/users/peertube/inbox",
"outbox": "https://framapiaf.org/users/peertube/outbox",
"featured": "https://framapiaf.org/users/peertube/collections/featured",
"preferredUsername": "peertube",
"name": "PeerTube",
"summary": "<p>PeerTube Software Official Account - No support here // Compte officiel du logiciel PeerTube (animé par Framasoft). Nous ne faisons pas de support depuis ce compte. Merci de contacter l&apos;administrateur⋅ice de l&apos;instance concernée ou de vous rendre sur <a href=\"https://framacolibri.org/c/peertube\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">framacolibri.org/c/peertube</span><span class=\"invisible\"></span></a></p>",
"url": "https://framapiaf.org/@peertube",
"manuallyApprovesFollowers": false,
"discoverable": false,
"devices": "https://framapiaf.org/users/peertube/collections/devices",
"publicKey": {
"id": "https://framapiaf.org/users/peertube#main-key",
"owner": "https://framapiaf.org/users/peertube",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn1y53r+ymOmDoP8iYxa1\nb1VvXkldZVpxJg1ZVeq4SijVS3oNurrQQhpTwTmCCAue2m+UvG4eEEYAYSfb5+C3\nbqH3kLlQrptkp8y/qz3d4tk/b8RConAaws7/SwksDC5rs+cYLnnXgD7rAaT1uH/B\nVTzG79YLgnasK6IxpnBth6Vru+9g2U8PzIUOfuwPV3aZeu9q2xEdC5/GnnjsfKZv\nWEzpG3HkRAlaTRDYadl9dWOPlfhy/LMkknAP02j+Qt/s7y83YqsrUyvQcfTSy3Zf\nLNNFrpU4u1ACyZXzvaoDXQH8HetKSA06xqa4pJO4xmM2PWMoBq1KX3Us4sP291w4\nEQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"tag": [],
"attachment": [],
"endpoints": {
"sharedInbox": "https://framapiaf.org/inbox"
},
"icon": {
"type": "Image",
"mediaType": "image/png",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/accounts/avatars/000/223/824/original/03ed95406a9a3cd0.png"
},
"image": {
"type": "Image",
"mediaType": "image/png",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/accounts/headers/000/223/824/original/2fbb4d6268c2fb20.png"
}
}

View File

@@ -1,8 +1,6 @@
{
"type": "Announce",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"signature": {
"type": "RsaSignature2017",
"signatureValue": "T95DRE0eAligvMuRMkQA01lsoz2PKi4XXF+cyZ0BqbrO12p751TEWTyyRn5a+HH0e4kc77EUhQVXwMq80WAYDzHKVUTf2XBJPBa68vl0j6RXw3+HK4ef5hR4KWFNBU34yePS7S1fEmc1mTG4Yx926wtmZwDpEMTp1CXOeVEjCYzmdyHpepPPH2ZZettiacmPRSqBLPGWZoot7kH/SioIdnrMGY0I7b+rqkIdnnEcdhu9N1BKPEO9Sr+KmxgAUiidmNZlbBXX6gCxp8BiIdH4ABsIcwoDcGNkM5EmWunGW31LVjsEQXhH5c1Wly0ugYYPCg/0eHLNBOhKkY/teSM8Lg==",
@@ -10,14 +8,14 @@
"created": "2018-02-17T19:39:15Z"
},
"published": "2018-02-17T19:39:15Z",
"object": "https://framapiaf.org/users/Framasoft/statuses/102501959686438400",
"id": "https://framapiaf.org/users/Framasoft/statuses/102501959686438400/activity",
"object": "https://framapiaf.org/users/peertube/statuses/104584600044284729",
"id": "https://framapiaf.org/users/peertube/statuses/104584600044284729/activity",
"cc": [
"https://framapiaf.org/users/Framasoft",
"https://framapiaf.org/users/Framasoft/followers"
"https://framapiaf.org/users/peertube",
"https://framapiaf.org/users/peertube/followers"
],
"atomUri": "https://framapiaf.org/users/Framasoft/statuses/102501959686438400/activity",
"actor": "https://framapiaf.org/users/Framasoft",
"atomUri": "https://framapiaf.org/users/peertube/statuses/104584600044284729/activity",
"actor": "https://framapiaf.org/users/peertube",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",

68
test/fixtures/mastodon-status-2.json vendored Normal file
View File

@@ -0,0 +1,68 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"blurhash": "toot:blurhash",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
"Hashtag": "as:Hashtag"
}
],
"id": "https://framapiaf.org/users/Framasoft/statuses/102093631881522097",
"type": "Note",
"summary": null,
"inReplyTo": null,
"published": "2019-05-14T09:13:13Z",
"url": "https://framapiaf.org/@Framasoft/102093631881522097",
"attributedTo": "https://framapiaf.org/users/Framasoft",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"cc": ["https://framapiaf.org/users/Framasoft/followers"],
"sensitive": false,
"atomUri": "https://framapiaf.org/users/Framasoft/statuses/102093631881522097",
"inReplyToAtomUri": null,
"conversation": "tag:framapiaf.org,2019-05-14:objectId=16200311:objectType=Conversation",
"content": "<p>Mobilizon : Finançons un outil pour sortir nos événements de Facebook !</p><p><a href=\"https://framablog.org/2019/05/14/mobilizon-financons-un-outil-pour-sortir-nos-evenements-de-facebook/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">framablog.org/2019/05/14/mobil</span><span class=\"invisible\">izon-financons-un-outil-pour-sortir-nos-evenements-de-facebook/</span></a> <a href=\"https://framapiaf.org/tags/Framablog\" class=\"mention hashtag\" rel=\"tag\">#<span>Framablog</span></a> <a href=\"https://framapiaf.org/tags/TootOuRien\" class=\"mention hashtag\" rel=\"tag\">#<span>TootOuRien</span></a></p><p>Nous avons moins de 60 jours pour financer Mobilizon. Moins de 60 jours pour faire connaître notre projet d&apos;alternative libre et fédérée aux événements Facebook ; et pour savoir à quel point nous devons nous y investir.</p><p>Changer le logiciel de celles et ceux qui changent le …</p>",
"contentMap": {
"fr": "<p>Mobilizon : Finançons un outil pour sortir nos événements de Facebook !</p><p><a href=\"https://framablog.org/2019/05/14/mobilizon-financons-un-outil-pour-sortir-nos-evenements-de-facebook/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">framablog.org/2019/05/14/mobil</span><span class=\"invisible\">izon-financons-un-outil-pour-sortir-nos-evenements-de-facebook/</span></a> <a href=\"https://framapiaf.org/tags/Framablog\" class=\"mention hashtag\" rel=\"tag\">#<span>Framablog</span></a> <a href=\"https://framapiaf.org/tags/TootOuRien\" class=\"mention hashtag\" rel=\"tag\">#<span>TootOuRien</span></a></p><p>Nous avons moins de 60 jours pour financer Mobilizon. Moins de 60 jours pour faire connaître notre projet d&apos;alternative libre et fédérée aux événements Facebook ; et pour savoir à quel point nous devons nous y investir.</p><p>Changer le logiciel de celles et ceux qui changent le …</p>"
},
"attachment": [
{
"type": "Document",
"mediaType": "image/jpeg",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/media_attachments/files/003/337/144/original/39d457fd9e0f0171.jpg",
"name": null,
"blurhash": "UIF=jrpIM|~q~VT0%2t6Ne9a?G-;9ZRP%2Rk"
}
],
"tag": [
{
"type": "Hashtag",
"href": "https://framapiaf.org/tags/framablog",
"name": "#framablog"
},
{
"type": "Hashtag",
"href": "https://framapiaf.org/tags/tootourien",
"name": "#tootourien"
}
],
"replies": {
"id": "https://framapiaf.org/users/Framasoft/statuses/102093631881522097/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://framapiaf.org/users/Framasoft/statuses/102093631881522097/replies?only_other_accounts=true&page=true",
"partOf": "https://framapiaf.org/users/Framasoft/statuses/102093631881522097/replies",
"items": []
}
}
}

34
test/fixtures/mastodon-status-3.json vendored Normal file
View File

@@ -0,0 +1,34 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://zoltasila.pl/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"actor": "https://zoltasila.pl/users/mkljczk",
"attachment": [],
"attributedTo": "https://zoltasila.pl/users/mkljczk",
"cc": ["https://zoltasila.pl/users/mkljczk/followers"],
"content": "<p><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"9xUDK5nC4I2pNtyIsq\" href=\"https://framapiaf.org/@peertube\" rel=\"ugc\">@<span>peertube</span></a></span> guess you wanted to put the <a href=\"https://joinpeertube.org/en_US/news#release-2-3-0\">en_US lang link</a></p>",
"context": "tag:framapiaf.org,2020-07-27:objectId=39135637:objectType=Conversation",
"conversation": "tag:framapiaf.org,2020-07-27:objectId=39135637:objectType=Conversation",
"id": "https://zoltasila.pl/objects/1c295713-8e3c-411e-9e62-57a7b9c9e514",
"inReplyTo": "https://framapiaf.org/users/peertube/statuses/104584600044284729",
"published": "2020-07-27T09:37:57.202806Z",
"sensitive": false,
"source": "@peertube@framapiaf.org guess you wanted to put the [en_US lang link](https://joinpeertube.org/en_US/news#release-2-3-0)",
"summary": "",
"tag": [
{
"href": "https://framapiaf.org/users/peertube",
"name": "@peertube@framapiaf.org",
"type": "Mention"
}
],
"to": [
"https://framapiaf.org/users/peertube",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Note"
}

56
test/fixtures/mastodon-status-4.json vendored Normal file
View File

@@ -0,0 +1,56 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"blurhash": "toot:blurhash",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://framapiaf.org/users/peertube/statuses/104584600044284729",
"type": "Note",
"summary": null,
"inReplyTo": null,
"published": "2020-07-27T07:19:11Z",
"url": "https://framapiaf.org/@peertube/104584600044284729",
"attributedTo": "https://framapiaf.org/users/peertube",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"cc": ["https://framapiaf.org/users/peertube/followers"],
"sensitive": false,
"atomUri": "https://framapiaf.org/users/peertube/statuses/104584600044284729",
"inReplyToAtomUri": null,
"conversation": "tag:framapiaf.org,2020-07-27:objectId=39135637:objectType=Conversation",
"content": "<p>PeerTube 2.3 is out! Discover on <a href=\"https://joinpeertube.org/fr_FR/news#release-2-3-0\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">joinpeertube.org/fr_FR/news#re</span><span class=\"invisible\">lease-2-3-0</span></a> the list of new features! </p><p>Have you seen the broadcast message system ? 🤩</p>",
"contentMap": {
"en": "<p>PeerTube 2.3 is out! Discover on <a href=\"https://joinpeertube.org/fr_FR/news#release-2-3-0\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">joinpeertube.org/fr_FR/news#re</span><span class=\"invisible\">lease-2-3-0</span></a> the list of new features! </p><p>Have you seen the broadcast message system ? 🤩</p>"
},
"attachment": [
{
"type": "Document",
"mediaType": "image/png",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/media_attachments/files/104/584/599/807/860/387/original/88c94143f78fdfa3.png",
"name": null,
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
}
],
"tag": [],
"replies": {
"id": "https://framapiaf.org/users/peertube/statuses/104584600044284729/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://framapiaf.org/users/peertube/statuses/104584600044284729/replies?only_other_accounts=true&page=true",
"partOf": "https://framapiaf.org/users/peertube/statuses/104584600044284729/replies",
"items": []
}
}
}

58
test/fixtures/mastodon-status-5.json vendored Normal file
View File

@@ -0,0 +1,58 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount"
}
],
"id": "https://diaspodon.fr/users/dada/statuses/100820008426311925",
"type": "Note",
"summary": null,
"inReplyTo": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d",
"published": "2018-10-01T10:54:01Z",
"url": "https://diaspodon.fr/@dada/100820008426311925",
"attributedTo": "https://diaspodon.fr/users/dada",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"cc": [
"https://diaspodon.fr/users/dada/followers",
"https://framapiaf.org/users/Pouhiou",
"https://framatube.org/accounts/framasoft"
],
"sensitive": false,
"atomUri": "https://diaspodon.fr/users/dada/statuses/100820008426311925",
"inReplyToAtomUri": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d",
"conversation": "tag:diaspodon.fr,2018-10-01:objectId=1187066:objectType=Conversation",
"content": "<p><span class=\"h-card\"><a href=\"https://framatube.org/accounts/framasoft\" class=\"u-url mention\">@<span>framasoft</span></a></span> Ça ne serait pas la voix de <span class=\"h-card\"><a href=\"https://framapiaf.org/@Pouhiou\" class=\"u-url mention\">@<span>Pouhiou</span></a></span> ? 🤔</p>",
"contentMap": {
"fr": "<p><span class=\"h-card\"><a href=\"https://framatube.org/accounts/framasoft\" class=\"u-url mention\">@<span>framasoft</span></a></span> Ça ne serait pas la voix de <span class=\"h-card\"><a href=\"https://framapiaf.org/@Pouhiou\" class=\"u-url mention\">@<span>Pouhiou</span></a></span> ? 🤔</p>"
},
"attachment": [],
"tag": [
{
"type": "Mention",
"href": "https://framatube.org/accounts/framasoft",
"name": "@framasoft@framatube.org"
},
{
"type": "Mention",
"href": "https://framapiaf.org/users/Pouhiou",
"name": "@Pouhiou@framapiaf.org"
}
],
"replies": {
"id": "https://diaspodon.fr/users/dada/statuses/100820008426311925/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://diaspodon.fr/users/dada/statuses/100820008426311925/replies?only_other_accounts=true&page=true",
"partOf": "https://diaspodon.fr/users/dada/statuses/100820008426311925/replies",
"items": []
}
}
}

56
test/fixtures/mastodon-status.json vendored Normal file
View File

@@ -0,0 +1,56 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"blurhash": "toot:blurhash",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://framapiaf.org/users/peertube/statuses/104584600044284729",
"type": "Note",
"summary": null,
"inReplyTo": null,
"published": "2020-07-27T07:19:11Z",
"url": "https://framapiaf.org/@peertube/104584600044284729",
"attributedTo": "https://framapiaf.org/users/peertube",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"cc": ["https://framapiaf.org/users/peertube/followers"],
"sensitive": false,
"atomUri": "https://framapiaf.org/users/peertube/statuses/104584600044284729",
"inReplyToAtomUri": null,
"conversation": "tag:framapiaf.org,2020-07-27:objectId=39135637:objectType=Conversation",
"content": "<p>PeerTube 2.3 is out! Discover on <a href=\"https://joinpeertube.org/fr_FR/news#release-2-3-0\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">joinpeertube.org/fr_FR/news#re</span><span class=\"invisible\">lease-2-3-0</span></a> the list of new features! </p><p>Have you seen the broadcast message system ? 🤩</p>",
"contentMap": {
"en": "<p>PeerTube 2.3 is out! Discover on <a href=\"https://joinpeertube.org/fr_FR/news#release-2-3-0\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">joinpeertube.org/fr_FR/news#re</span><span class=\"invisible\">lease-2-3-0</span></a> the list of new features! </p><p>Have you seen the broadcast message system ? 🤩</p>"
},
"attachment": [
{
"type": "Document",
"mediaType": "image/png",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/media_attachments/files/104/584/599/807/860/387/original/88c94143f78fdfa3.png",
"name": null,
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
}
],
"tag": [],
"replies": {
"id": "https://framapiaf.org/users/peertube/statuses/104584600044284729/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://framapiaf.org/users/peertube/statuses/104584600044284729/replies?only_other_accounts=true&page=true",
"partOf": "https://framapiaf.org/users/peertube/statuses/104584600044284729/replies",
"items": []
}
}
}

View File

@@ -0,0 +1,52 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://litepub.social/litepub/context.jsonld",
{
"Hashtag": "as:Hashtag",
"category": "sc:category",
"ical": "http://www.w3.org/2002/12/cal/ical#",
"joinMode": {
"@id": "mz:joinMode",
"@type": "mz:joinModeType"
},
"joinModeType": {
"@id": "mz:joinModeType",
"@type": "rdfs:Class"
},
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
"mz": "https://joinmobilizon.org/ns#",
"repliesModerationOption": {
"@id": "mz:repliesModerationOption",
"@type": "mz:repliesModerationOptionType"
},
"repliesModerationOptionType": {
"@id": "mz:repliesModerationOptionType",
"@type": "rdfs:Class"
},
"sc": "http://schema.org#",
"uuid": "sc:identifier"
}
],
"actor": "http://mobilizon1.com/@user",
"attributedTo": "http://mobilizon1.com/@group",
"cc": [],
"id": "http://mobilizon1.com/events/f270ae07-7991-453c-9bb7-3d2122ededae/activity",
"object": {
"actor": "http://mobilizon1.com/@user",
"attributedTo": "http://mobilizon1.com/@group",
"startTime": "2018-02-12T14:08:20Z",
"cc": [],
"content": "<p><span class=\"h-card\"><a href=\"https://framapiaf.org/users/tcit\" class=\"u-url mention\">@<span>tcit</span></a></span></p>",
"id": "http://mobilizon1.com/events/f270ae07-7991-453c-9bb7-3d2122ededae",
"name": "My first event",
"published": "2018-02-12T14:08:20Z",
"tag": [],
"to": ["http://mobilizon1.com/@group"],
"type": "Event",
"uuid": "f270ae07-7991-453c-9bb7-3d2122ededae"
},
"published": "2018-02-12T14:08:20Z",
"to": ["http://mobilizon1.com/@group"],
"type": "Create"
}

393
test/fixtures/peertube-video.json vendored Normal file
View File

@@ -0,0 +1,393 @@
{
"type": "Video",
"id": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d",
"name": "What is PeerTube?",
"duration": "PT113S",
"uuid": "9c9de5e8-0a1e-484a-b099-e80766180a6d",
"tag": [
{
"type": "Hashtag",
"name": "framasoft"
},
{
"type": "Hashtag",
"name": "peertube"
}
],
"category": {
"identifier": "15",
"name": "Science & Technology"
},
"licence": {
"identifier": "2",
"name": "Attribution - Share Alike"
},
"language": {
"identifier": "en",
"name": "English"
},
"views": 53137,
"sensitive": false,
"waitTranscoding": true,
"state": 1,
"commentsEnabled": true,
"downloadEnabled": true,
"published": "2018-10-01T10:52:46.396Z",
"originallyPublishedAt": null,
"updated": "2020-07-30T08:01:00.836Z",
"mediaType": "text/markdown",
"content": "**[Want to help to translate this video?](https://weblate.framasoft.org/projects/what-is-peertube-video/)**\r\n\r\n**Take back the control of your videos! [#JoinPeertube](https://joinpeertube.org)**\r\n*A decentralized video hosting network, based on free/libre software!*\r\n\r\n**Animation Produced by:** [LILA](https://libreart.info) - [ZeMarmot Team](https://film.zemarmot.net)\r\n*Directed by* Aryeom\r\n*Assistant* Jehan\r\n**Licence**: [CC-By-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)\r\n\r\n**Sponsored by** [Framasoft](https://framasoft.org)\r\n\r\n**Music**: [Red Step Forward](http://play.dogmazic.net/song.php?song_id=52491) - CC-By Ken Bushima\r\n\r\n**Movie Clip**: [Caminades 3: Llamigos](http://www.caminandes.com/) CC-By Blender Institute\r\n\r\n**Video sources**: https://gitlab.gnome.org/Jehan/what-is-peertube/",
"support": null,
"subtitleLanguage": [
{
"identifier": "ca",
"name": "Catalan",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-ca.vtt"
},
{
"identifier": "cs",
"name": "Czech",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-cs.vtt"
},
{
"identifier": "de",
"name": "German",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-de.vtt"
},
{
"identifier": "en",
"name": "English",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-en.vtt"
},
{
"identifier": "es",
"name": "Spanish",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-es.vtt"
},
{
"identifier": "eu",
"name": "Basque",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-eu.vtt"
},
{
"identifier": "fr",
"name": "French",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-fr.vtt"
},
{
"identifier": "gl",
"name": "Galician",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-gl.vtt"
},
{
"identifier": "hu",
"name": "Hungarian",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-hu.vtt"
},
{
"identifier": "it",
"name": "Italian",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-it.vtt"
},
{
"identifier": "lt",
"name": "Lithuanian",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-lt.vtt"
},
{
"identifier": "nl",
"name": "Dutch",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-nl.vtt"
},
{
"identifier": "pl",
"name": "Polish",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-pl.vtt"
},
{
"identifier": "pt",
"name": "Portuguese",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-pt.vtt"
},
{
"identifier": "ru",
"name": "Russian",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-ru.vtt"
},
{
"identifier": "sv",
"name": "Swedish",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-sv.vtt"
},
{
"identifier": "zh",
"name": "Chinese",
"url": "https://framatube.org/static/video-captions/9c9de5e8-0a1e-484a-b099-e80766180a6d-zh.vtt"
}
],
"icon": [
{
"type": "Image",
"url": "https://framatube.org/static/thumbnails/9c9de5e8-0a1e-484a-b099-e80766180a6d.jpg",
"mediaType": "image/jpeg",
"width": 223,
"height": 122
},
{
"type": "Image",
"url": "https://framatube.org/static/previews/9c9de5e8-0a1e-484a-b099-e80766180a6d.jpg",
"mediaType": "image/jpeg",
"width": 850,
"height": 480
}
],
"url": [
{
"type": "Link",
"mediaType": "text/html",
"href": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d"
},
{
"type": "Link",
"mediaType": "video/mp4",
"href": "https://framatube.org/static/webseed/9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4",
"height": 1080,
"size": 14689568,
"fps": 24
},
{
"type": "Link",
"rel": ["metadata", "video/mp4"],
"mediaType": "application/json",
"href": "https://framatube.org/api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180a6d/metadata/10124",
"height": 1080,
"fps": 24
},
{
"type": "Link",
"mediaType": "application/x-bittorrent",
"href": "https://framatube.org/static/torrents/9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.torrent",
"height": 1080
},
{
"type": "Link",
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
"href": "magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.torrent&xt=urn:btih:dc84b692c4002fec0cae873df0dc7f5d67fc09db&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fvideo.antopie.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.foxfam.club%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.freeforge.eu%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.krapace.fr%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fvideo.blueline.mg%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.video%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Ftube.crapaud-fou.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fflim.ml%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4&ws=https%3A%2F%2Fpeertube.nomagic.uk%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-1080.mp4",
"height": 1080
},
{
"type": "Link",
"mediaType": "video/mp4",
"href": "https://framatube.org/static/webseed/9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4",
"height": 720,
"size": 8365049,
"fps": 24
},
{
"type": "Link",
"rel": ["metadata", "video/mp4"],
"mediaType": "application/json",
"href": "https://framatube.org/api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180a6d/metadata/10127",
"height": 720,
"fps": 24
},
{
"type": "Link",
"mediaType": "application/x-bittorrent",
"href": "https://framatube.org/static/torrents/9c9de5e8-0a1e-484a-b099-e80766180a6d-720.torrent",
"height": 720
},
{
"type": "Link",
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
"href": "magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.torrent&xt=urn:btih:9fb0e35b4945565fa842b2bb0fe8b03edf223b15&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fvideo.antopie.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.freeforge.eu%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.krapace.fr%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fvideo.blueline.mg%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.video%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Ftube.crapaud-fou.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fflim.ml%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.foxfam.club%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4&ws=https%3A%2F%2Fpeertube.nomagic.uk%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-720.mp4",
"height": 720
},
{
"type": "Link",
"mediaType": "video/mp4",
"href": "https://framatube.org/static/webseed/9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4",
"height": 480,
"size": 5650553,
"fps": 24
},
{
"type": "Link",
"rel": ["metadata", "video/mp4"],
"mediaType": "application/json",
"href": "https://framatube.org/api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180a6d/metadata/10125",
"height": 480,
"fps": 24
},
{
"type": "Link",
"mediaType": "application/x-bittorrent",
"href": "https://framatube.org/static/torrents/9c9de5e8-0a1e-484a-b099-e80766180a6d-480.torrent",
"height": 480
},
{
"type": "Link",
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
"href": "magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.torrent&xt=urn:btih:bf02027a9aad4275d8baa25afae230b85187bcf7&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fvideo.antopie.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.freeforge.eu%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.krapace.fr%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fvideo.blueline.mg%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.video%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Ftube.crapaud-fou.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fflim.ml%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.foxfam.club%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4&ws=https%3A%2F%2Fpeertube.nomagic.uk%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-480.mp4",
"height": 480
},
{
"type": "Link",
"mediaType": "video/mp4",
"href": "https://framatube.org/static/webseed/9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4",
"height": 360,
"size": 4524751,
"fps": 24
},
{
"type": "Link",
"rel": ["metadata", "video/mp4"],
"mediaType": "application/json",
"href": "https://framatube.org/api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180a6d/metadata/10126",
"height": 360,
"fps": 24
},
{
"type": "Link",
"mediaType": "application/x-bittorrent",
"href": "https://framatube.org/static/torrents/9c9de5e8-0a1e-484a-b099-e80766180a6d-360.torrent",
"height": 360
},
{
"type": "Link",
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
"href": "magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.torrent&xt=urn:btih:16b3720b18752523e2848341d3207120f2de26f8&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fvideo.antopie.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.freeforge.eu%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.krapace.fr%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fvideo.blueline.mg%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.video%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Ftube.crapaud-fou.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fflim.ml%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.foxfam.club%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4&ws=https%3A%2F%2Fpeertube.nomagic.uk%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-360.mp4",
"height": 360
},
{
"type": "Link",
"mediaType": "video/mp4",
"href": "https://framatube.org/static/webseed/9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4",
"height": 240,
"size": 3452697,
"fps": 24
},
{
"type": "Link",
"rel": ["metadata", "video/mp4"],
"mediaType": "application/json",
"href": "https://framatube.org/api/v1/videos/9c9de5e8-0a1e-484a-b099-e80766180a6d/metadata/10128",
"height": 240,
"fps": 24
},
{
"type": "Link",
"mediaType": "application/x-bittorrent",
"href": "https://framatube.org/static/torrents/9c9de5e8-0a1e-484a-b099-e80766180a6d-240.torrent",
"height": 240
},
{
"type": "Link",
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
"href": "magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.torrent&xt=urn:btih:38b4747ff788b30bf61f59d1965cd38f9e48e01f&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fvideo.antopie.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.foxfam.club%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.freeforge.eu%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.krapace.fr%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fvideo.blueline.mg%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.video%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Ftube.crapaud-fou.org%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fflim.ml%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4&ws=https%3A%2F%2Fpeertube.nomagic.uk%2Fstatic%2Fredundancy%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4",
"height": 240
}
],
"likes": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d/likes",
"dislikes": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d/dislikes",
"shares": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d/announces",
"comments": "https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d/comments",
"attributedTo": [
{
"type": "Person",
"id": "https://framatube.org/accounts/framasoft"
},
{
"type": "Group",
"id": "https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8"
}
],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"cc": ["https://framatube.org/accounts/framasoft/followers"],
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"RsaSignature2017": "https://w3id.org/security#RsaSignature2017"
},
{
"pt": "https://joinpeertube.org/ns#",
"sc": "http://schema.org#",
"Hashtag": "as:Hashtag",
"uuid": "sc:identifier",
"category": "sc:category",
"licence": "sc:license",
"subtitleLanguage": "sc:subtitleLanguage",
"sensitive": "as:sensitive",
"language": "sc:inLanguage",
"Infohash": "pt:Infohash",
"Playlist": "pt:Playlist",
"PlaylistElement": "pt:PlaylistElement",
"originallyPublishedAt": "sc:datePublished",
"views": {
"@type": "sc:Number",
"@id": "pt:views"
},
"state": {
"@type": "sc:Number",
"@id": "pt:state"
},
"size": {
"@type": "sc:Number",
"@id": "pt:size"
},
"fps": {
"@type": "sc:Number",
"@id": "pt:fps"
},
"startTimestamp": {
"@type": "sc:Number",
"@id": "pt:startTimestamp"
},
"stopTimestamp": {
"@type": "sc:Number",
"@id": "pt:stopTimestamp"
},
"position": {
"@type": "sc:Number",
"@id": "pt:position"
},
"commentsEnabled": {
"@type": "sc:Boolean",
"@id": "pt:commentsEnabled"
},
"downloadEnabled": {
"@type": "sc:Boolean",
"@id": "pt:downloadEnabled"
},
"waitTranscoding": {
"@type": "sc:Boolean",
"@id": "pt:waitTranscoding"
},
"support": {
"@type": "sc:Text",
"@id": "pt:support"
},
"likes": {
"@id": "as:likes",
"@type": "@id"
},
"dislikes": {
"@id": "as:dislikes",
"@type": "@id"
},
"playlists": {
"@id": "pt:playlists",
"@type": "@id"
},
"shares": {
"@id": "as:shares",
"@type": "@id"
},
"comments": {
"@id": "as:comments",
"@type": "@id"
}
}
]
}

View File

@@ -0,0 +1,30 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://blob.cat/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"actor": "https://blob.cat/users/comicbot",
"attachment": [
{
"mediaType": "image/gif",
"name": "1574936800141.gif",
"type": "Document",
"url": "https://blob.cat/media/143ba9b1ed15e67d7401906f7b71a459b90680af7075af5b8ac9cb8e3b86868a.gif"
}
],
"attributedTo": "https://blob.cat/users/comicbot",
"cc": ["https://blob.cat/users/comicbot/followers"],
"content": "Super Mega Comics <br> <a href=\"http://supermegacomics.com/\" rel=\"ugc\">http://supermegacomics.com/</a>",
"context": "https://blob.cat/contexts/26f3271a-3eb8-4f3f-8fb1-8ff96e2c4a47",
"conversation": "https://blob.cat/contexts/26f3271a-3eb8-4f3f-8fb1-8ff96e2c4a47",
"id": "https://blob.cat/objects/02fdea3d-932c-4348-9ecb-3f9eb3fbdd94",
"published": "2019-11-28T10:26:42.503473Z",
"sensitive": false,
"summary": "",
"tag": [],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Note"
}

View File

@@ -1,31 +0,0 @@
[
{
"request": {
"body": "",
"headers": {
"Accept": "*/*",
"Accept-Encoding":"deflate, gzip"
},
"method": "get",
"options": [],
"request_body": "",
"url": "https://api-adresse.data.gouv.fr/reverse/?lon=4.842569&lat=45.751718"
},
"response": {
"binary": false,
"body": "{\"type\": \"FeatureCollection\", \"version\": \"draft\", \"features\": [{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [4.842569, 45.751718]}, \"properties\": {\"label\": \"10 Rue Jangot 69007 Lyon\", \"score\": 0.9999999999926557, \"housenumber\": \"10\", \"id\": \"69387_3650_00010\", \"type\": \"housenumber\", \"x\": 843232.29, \"y\": 6518573.31, \"importance\": 0.5454797306366062, \"name\": \"10 Rue Jangot\", \"postcode\": \"69007\", \"citycode\": \"69387\", \"city\": \"Lyon\", \"district\": \"Lyon 7e Arrondissement\", \"context\": \"69, Rh\u00f4ne, Auvergne-Rh\u00f4ne-Alpes\", \"street\": \"Rue Jangot\", \"distance\": 0}}], \"attribution\": \"BAN\", \"licence\": \"ETALAB-2.0\", \"limit\": 1}",
"headers": {
"Server": "nginx/1.9.3",
"Date": "Thu, 14 Mar 2019 10:46:45 GMT",
"Content-type":"application/json; charset=utf-8",
"Vary":"Accept-Encoding",
"X-cache-status":"MISS",
"Access-control-allow-origin":"*",
"Access-control-allow-headers":"X-Requested-With,Content-Type",
"Content-encoding":"gzip"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,31 +0,0 @@
[
{
"request": {
"body": "",
"headers": {
"Accept": "*/*",
"Accept-Encoding":"deflate, gzip"
},
"method": "get",
"options": [],
"request_body": "",
"url": "https://api-adresse.data.gouv.fr/search/?q=10%20Rue%20Jangot"
},
"response": {
"binary": false,
"body": "{\"type\": \"FeatureCollection\", \"version\": \"draft\", \"features\": [{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [4.842569, 45.751718]}, \"properties\": {\"label\": \"10 Rue Jangot 69007 Lyon\", \"score\": 0.8677708846033279, \"housenumber\": \"10\", \"id\": \"69387_3650_00010\", \"type\": \"housenumber\", \"x\": 843232.29, \"y\": 6518573.31, \"importance\": 0.5454797306366062, \"name\": \"10 Rue Jangot\", \"postcode\": \"69007\", \"citycode\": \"69387\", \"city\": \"Lyon\", \"district\": \"Lyon 7e Arrondissement\", \"context\": \"69, Rh\u00f4ne, Auvergne-Rh\u00f4ne-Alpes\", \"street\": \"Rue Jangot\"}}, {\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [2.440319, 50.371266]}, \"properties\": {\"label\": \"Rue Jangon 62127 Bailleul-aux-Cornailles\", \"score\": 0.5269641371131077, \"id\": \"62070_0100\", \"type\": \"street\", \"x\": 660129.18, \"y\": 7030540.46, \"importance\": 0.25814396978264664, \"name\": \"Rue Jangon\", \"postcode\": \"62127\", \"citycode\": \"62070\", \"city\": \"Bailleul-aux-Cornailles\", \"context\": \"62, Pas-de-Calais, Hauts-de-France\"}}], \"attribution\": \"BAN\", \"licence\": \"ETALAB-2.0\", \"query\": \"10 Rue Jangot\", \"limit\": 5}",
"headers": {
"Server":"nginx/1.10.3",
"Date":"Thu, 25 Jun 2020 11:23:54 GMT",
"Content-type":"application/json; charset=utf-8",
"Vary":"Accept-Encoding",
"X-cache-status":"MISS",
"Access-control-allow-origin":"*",
"Access-control-allow-headers":"X-Requested-With,Content-Type",
"Content-encoding":"gzip"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,63 +0,0 @@
[
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://maps.googleapis.com/maps/api/place/details/json?key=toto&placeid=ChIJrW0QikTq9EcR96jk2OnO75w"
},
"response": {
"binary": false,
"body": "{\n \"html_attributions\" : [],\n \"result\" : {\n \"address_components\" : [\n {\n \"long_name\" : \"10bis\",\n \"short_name\" : \"10bis\",\n \"types\" : [ \"street_number\" ]\n },\n {\n \"long_name\" : \"Rue Jangot\",\n \"short_name\" : \"Rue Jangot\",\n \"types\" : [ \"route\" ]\n },\n {\n \"long_name\" : \"Lyon\",\n \"short_name\" : \"Lyon\",\n \"types\" : [ \"locality\", \"political\" ]\n },\n {\n \"long_name\" : \"Rhône\",\n \"short_name\" : \"Rhône\",\n \"types\" : [ \"administrative_area_level_2\", \"political\" ]\n },\n {\n \"long_name\" : \"Auvergne-Rhône-Alpes\",\n \"short_name\" : \"Auvergne-Rhône-Alpes\",\n \"types\" : [ \"administrative_area_level_1\", \"political\" ]\n },\n {\n \"long_name\" : \"France\",\n \"short_name\" : \"FR\",\n \"types\" : [ \"country\", \"political\" ]\n },\n {\n \"long_name\" : \"69007\",\n \"short_name\" : \"69007\",\n \"types\" : [ \"postal_code\" ]\n }\n ],\n \"adr_address\" : \"\\u003cspan class=\\\"street-address\\\"\\u003e10bis Rue Jangot\\u003c/span\\u003e, \\u003cspan class=\\\"postal-code\\\"\\u003e69007\\u003c/span\\u003e \\u003cspan class=\\\"locality\\\"\\u003eLyon\\u003c/span\\u003e, \\u003cspan class=\\\"country-name\\\"\\u003eFrance\\u003c/span\\u003e\",\n \"formatted_address\" : \"10bis Rue Jangot, 69007 Lyon, France\",\n \"geometry\" : {\n \"location\" : {\n \"lat\" : 45.751725,\n \"lng\" : 4.8424966\n },\n \"viewport\" : {\n \"northeast\" : {\n \"lat\" : 45.7531097802915,\n \"lng\" : 4.843951380291502\n },\n \"southwest\" : {\n \"lat\" : 45.7504118197085,\n \"lng\" : 4.841253419708497\n }\n }\n },\n \"icon\" : \"https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png\",\n \"id\" : \"4a3482a7a74c6203048adf713b736186c4ace7cd\",\n \"name\" : \"10bis Rue Jangot\",\n \"place_id\" : \"ChIJrW0QikTq9EcR96jk2OnO75w\",\n \"plus_code\" : {\n \"compound_code\" : \"QR2R+MX Lyon, France\",\n \"global_code\" : \"8FQ6QR2R+MX\"\n },\n \"reference\" : \"ChIJrW0QikTq9EcR96jk2OnO75w\",\n \"scope\" : \"GOOGLE\",\n \"types\" : [ \"street_address\" ],\n \"url\" : \"https://maps.google.com/?q=10bis+Rue+Jangot,+69007+Lyon,+France&ftid=0x47f4ea448a106dad:0x9cefcee9d8e4a8f7\",\n \"utc_offset\" : 120,\n \"vicinity\" : \"Lyon\"\n },\n \"status\" : \"OK\"\n}\n",
"headers": {
"Content-Type": "application/json; charset=UTF-8",
"Date": "Thu, 22 Aug 2019 13:18:50 GMT",
"Expires": "Thu, 22 Aug 2019 13:23:50 GMT",
"Cache-Control": "public, max-age=300",
"Server": "scaffolding on HTTPServer2",
"X-XSS-Protection": "0",
"X-Frame-Options": "SAMEORIGIN",
"Server-Timing": "gfet4t7; dur=87",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"46,43,39\"",
"Accept-Ranges": "none",
"Vary": "Accept-Language,Accept-Encoding",
"Transfer-Encoding": "chunked"
},
"status_code": 200,
"type": "ok"
}
},
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://maps.googleapis.com/maps/api/geocode/json?limit=10&key=toto&language=en&latlng=45.751718,4.842569&result_type=street_address"
},
"response": {
"binary": false,
"body": "{\n \"plus_code\" : {\n \"compound_code\" : \"QR2V+M2 Lyon, France\",\n \"global_code\" : \"8FQ6QR2V+M2\"\n },\n \"results\" : [\n {\n \"address_components\" : [\n {\n \"long_name\" : \"10bis\",\n \"short_name\" : \"10bis\",\n \"types\" : [ \"street_number\" ]\n },\n {\n \"long_name\" : \"Rue Jangot\",\n \"short_name\" : \"Rue Jangot\",\n \"types\" : [ \"route\" ]\n },\n {\n \"long_name\" : \"Lyon\",\n \"short_name\" : \"Lyon\",\n \"types\" : [ \"locality\", \"political\" ]\n },\n {\n \"long_name\" : \"Rhône\",\n \"short_name\" : \"Rhône\",\n \"types\" : [ \"administrative_area_level_2\", \"political\" ]\n },\n {\n \"long_name\" : \"Auvergne-Rhône-Alpes\",\n \"short_name\" : \"Auvergne-Rhône-Alpes\",\n \"types\" : [ \"administrative_area_level_1\", \"political\" ]\n },\n {\n \"long_name\" : \"France\",\n \"short_name\" : \"FR\",\n \"types\" : [ \"country\", \"political\" ]\n },\n {\n \"long_name\" : \"69007\",\n \"short_name\" : \"69007\",\n \"types\" : [ \"postal_code\" ]\n }\n ],\n \"formatted_address\" : \"10bis Rue Jangot, 69007 Lyon, France\",\n \"geometry\" : {\n \"location\" : {\n \"lat\" : 45.751725,\n \"lng\" : 4.8424966\n },\n \"location_type\" : \"ROOFTOP\",\n \"viewport\" : {\n \"northeast\" : {\n \"lat\" : 45.7530739802915,\n \"lng\" : 4.843845580291503\n },\n \"southwest\" : {\n \"lat\" : 45.7503760197085,\n \"lng\" : 4.841147619708499\n }\n }\n },\n \"place_id\" : \"ChIJrW0QikTq9EcR96jk2OnO75w\",\n \"plus_code\" : {\n \"compound_code\" : \"QR2R+MX Lyon, France\",\n \"global_code\" : \"8FQ6QR2R+MX\"\n },\n \"types\" : [ \"street_address\" ]\n },\n {\n \"address_components\" : [\n {\n \"long_name\" : \"9\",\n \"short_name\" : \"9\",\n \"types\" : [ \"street_number\" ]\n },\n {\n \"long_name\" : \"Rue Jangot\",\n \"short_name\" : \"Rue Jangot\",\n \"types\" : [ \"route\" ]\n },\n {\n \"long_name\" : \"Lyon\",\n \"short_name\" : \"Lyon\",\n \"types\" : [ \"locality\", \"political\" ]\n },\n {\n \"long_name\" : \"Rhône\",\n \"short_name\" : \"Rhône\",\n \"types\" : [ \"administrative_area_level_2\", \"political\" ]\n },\n {\n \"long_name\" : \"Auvergne-Rhône-Alpes\",\n \"short_name\" : \"Auvergne-Rhône-Alpes\",\n \"types\" : [ \"administrative_area_level_1\", \"political\" ]\n },\n {\n \"long_name\" : \"France\",\n \"short_name\" : \"FR\",\n \"types\" : [ \"country\", \"political\" ]\n },\n {\n \"long_name\" : \"69007\",\n \"short_name\" : \"69007\",\n \"types\" : [ \"postal_code\" ]\n }\n ],\n \"formatted_address\" : \"9 Rue Jangot, 69007 Lyon, France\",\n \"geometry\" : {\n \"location\" : {\n \"lat\" : 45.7518165,\n \"lng\" : 4.8427168\n },\n \"location_type\" : \"RANGE_INTERPOLATED\",\n \"viewport\" : {\n \"northeast\" : {\n \"lat\" : 45.7531654802915,\n \"lng\" : 4.844065780291502\n },\n \"southwest\" : {\n \"lat\" : 45.7504675197085,\n \"lng\" : 4.841367819708497\n }\n }\n },\n \"place_id\" : \"EiA5IFJ1ZSBKYW5nb3QsIDY5MDA3IEx5b24sIEZyYW5jZSIaEhgKFAoSCR8N2ItE6vRHEW9tyPnhQsUIEAk\",\n \"types\" : [ \"street_address\" ]\n }\n ],\n \"status\" : \"OK\"\n}\n",
"headers": {
"Content-Type": "application/json; charset=UTF-8",
"Date": "Thu, 22 Aug 2019 13:18:50 GMT",
"Expires": "Thu, 22 Aug 2019 13:19:20 GMT",
"Cache-Control": "public, max-age=30",
"Access-Control-Allow-Origin": "*",
"Server": "mafe",
"X-XSS-Protection": "0",
"X-Frame-Options": "SAMEORIGIN",
"Server-Timing": "gfet4t7; dur=30",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"46,43,39\"",
"Accept-Ranges": "none",
"Vary": "Accept-Encoding",
"Transfer-Encoding": "chunked"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,63 +0,0 @@
[
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://maps.googleapis.com/maps/api/geocode/json?limit=10&key=toto&language=en&address=10%20rue%20Jangot"
},
"response": {
"binary": false,
"body": "{\n \"results\" : [\n {\n \"address_components\" : [\n {\n \"long_name\" : \"10\",\n \"short_name\" : \"10\",\n \"types\" : [ \"street_number\" ]\n },\n {\n \"long_name\" : \"Rue Jangot\",\n \"short_name\" : \"Rue Jangot\",\n \"types\" : [ \"route\" ]\n },\n {\n \"long_name\" : \"Lyon\",\n \"short_name\" : \"Lyon\",\n \"types\" : [ \"locality\", \"political\" ]\n },\n {\n \"long_name\" : \"Rhône\",\n \"short_name\" : \"Rhône\",\n \"types\" : [ \"administrative_area_level_2\", \"political\" ]\n },\n {\n \"long_name\" : \"Auvergne-Rhône-Alpes\",\n \"short_name\" : \"Auvergne-Rhône-Alpes\",\n \"types\" : [ \"administrative_area_level_1\", \"political\" ]\n },\n {\n \"long_name\" : \"France\",\n \"short_name\" : \"FR\",\n \"types\" : [ \"country\", \"political\" ]\n },\n {\n \"long_name\" : \"69007\",\n \"short_name\" : \"69007\",\n \"types\" : [ \"postal_code\" ]\n }\n ],\n \"formatted_address\" : \"10 Rue Jangot, 69007 Lyon, France\",\n \"geometry\" : {\n \"location\" : {\n \"lat\" : 45.75164940000001,\n \"lng\" : 4.8424032\n },\n \"location_type\" : \"ROOFTOP\",\n \"viewport\" : {\n \"northeast\" : {\n \"lat\" : 45.75299838029151,\n \"lng\" : 4.843752180291502\n },\n \"southwest\" : {\n \"lat\" : 45.75030041970851,\n \"lng\" : 4.841054219708497\n }\n }\n },\n \"place_id\" : \"ChIJtW0QikTq9EcRLI4Vy6bRx0U\",\n \"plus_code\" : {\n \"compound_code\" : \"QR2R+MX Lyon, France\",\n \"global_code\" : \"8FQ6QR2R+MX\"\n },\n \"types\" : [ \"street_address\" ]\n }\n ],\n \"status\" : \"OK\"\n}\n",
"headers": {
"Content-Type": "application/json; charset=UTF-8",
"Date": "Thu, 22 Aug 2019 13:05:39 GMT",
"Expires": "Thu, 22 Aug 2019 13:06:09 GMT",
"Cache-Control": "public, max-age=30",
"Access-Control-Allow-Origin": "*",
"Server": "mafe",
"X-XSS-Protection": "0",
"X-Frame-Options": "SAMEORIGIN",
"Server-Timing": "gfet4t7; dur=44",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"46,43,39\"",
"Accept-Ranges": "none",
"Vary": "Accept-Encoding",
"Transfer-Encoding": "chunked"
},
"status_code": 200,
"type": "ok"
}
},
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://maps.googleapis.com/maps/api/place/details/json?key=toto&placeid=ChIJtW0QikTq9EcRLI4Vy6bRx0U"
},
"response": {
"binary": false,
"body": "{\n \"html_attributions\" : [],\n \"result\" : {\n \"address_components\" : [\n {\n \"long_name\" : \"10\",\n \"short_name\" : \"10\",\n \"types\" : [ \"street_number\" ]\n },\n {\n \"long_name\" : \"Rue Jangot\",\n \"short_name\" : \"Rue Jangot\",\n \"types\" : [ \"route\" ]\n },\n {\n \"long_name\" : \"Lyon\",\n \"short_name\" : \"Lyon\",\n \"types\" : [ \"locality\", \"political\" ]\n },\n {\n \"long_name\" : \"Rhône\",\n \"short_name\" : \"Rhône\",\n \"types\" : [ \"administrative_area_level_2\", \"political\" ]\n },\n {\n \"long_name\" : \"Auvergne-Rhône-Alpes\",\n \"short_name\" : \"Auvergne-Rhône-Alpes\",\n \"types\" : [ \"administrative_area_level_1\", \"political\" ]\n },\n {\n \"long_name\" : \"France\",\n \"short_name\" : \"FR\",\n \"types\" : [ \"country\", \"political\" ]\n },\n {\n \"long_name\" : \"69007\",\n \"short_name\" : \"69007\",\n \"types\" : [ \"postal_code\" ]\n }\n ],\n \"adr_address\" : \"\\u003cspan class=\\\"street-address\\\"\\u003e10 Rue Jangot\\u003c/span\\u003e, \\u003cspan class=\\\"postal-code\\\"\\u003e69007\\u003c/span\\u003e \\u003cspan class=\\\"locality\\\"\\u003eLyon\\u003c/span\\u003e, \\u003cspan class=\\\"country-name\\\"\\u003eFrance\\u003c/span\\u003e\",\n \"formatted_address\" : \"10 Rue Jangot, 69007 Lyon, France\",\n \"geometry\" : {\n \"location\" : {\n \"lat\" : 45.75164940000001,\n \"lng\" : 4.842403200000001\n },\n \"viewport\" : {\n \"northeast\" : {\n \"lat\" : 45.7530412802915,\n \"lng\" : 4.843668630291503\n },\n \"southwest\" : {\n \"lat\" : 45.75034331970851,\n \"lng\" : 4.840970669708498\n }\n }\n },\n \"icon\" : \"https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png\",\n \"id\" : \"61b9418d092d2ed05ddd65a55dddefda5b9628cc\",\n \"name\" : \"10 Rue Jangot\",\n \"place_id\" : \"ChIJtW0QikTq9EcRLI4Vy6bRx0U\",\n \"plus_code\" : {\n \"compound_code\" : \"QR2R+MX Lyon, France\",\n \"global_code\" : \"8FQ6QR2R+MX\"\n },\n \"reference\" : \"ChIJtW0QikTq9EcRLI4Vy6bRx0U\",\n \"scope\" : \"GOOGLE\",\n \"types\" : [ \"street_address\" ],\n \"url\" : \"https://maps.google.com/?q=10+Rue+Jangot,+69007+Lyon,+France&ftid=0x47f4ea448a106db5:0x45c7d1a6cb158e2c\",\n \"utc_offset\" : 120,\n \"vicinity\" : \"Lyon\"\n },\n \"status\" : \"OK\"\n}\n",
"headers": {
"Content-Type": "application/json; charset=UTF-8",
"Date": "Thu, 22 Aug 2019 13:05:39 GMT",
"Expires": "Thu, 22 Aug 2019 13:10:39 GMT",
"Cache-Control": "public, max-age=300",
"Server": "scaffolding on HTTPServer2",
"X-XSS-Protection": "0",
"X-Frame-Options": "SAMEORIGIN",
"Server-Timing": "gfet4t7; dur=86",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"46,43,39\"",
"Accept-Ranges": "none",
"Vary": "Accept-Language,Accept-Encoding",
"Transfer-Encoding": "chunked"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,36 +0,0 @@
[
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://open.mapquestapi.com/geocoding/v1/reverse?key=secret_key&location=45.751718,4.842569&maxResults=10"
},
"response": {
"binary": false,
"body": "{\"info\":{\"statuscode\":0,\"copyright\":{\"text\":\"\\u00A9 2019 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2019 MapQuest, Inc.\"},\"messages\":[]},\"options\":{\"maxResults\":1,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{\"latLng\":{\"lat\":45.751718,\"lng\":4.842569}},\"locations\":[{\"street\":\"10 Rue Jangot\",\"adminArea6\":\"\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"Lyon\",\"adminArea5Type\":\"City\",\"adminArea4\":\"\",\"adminArea4Type\":\"County\",\"adminArea3\":\"Auvergne-Rh\\u00F4ne-Alpes\",\"adminArea3Type\":\"State\",\"adminArea1\":\"FR\",\"adminArea1Type\":\"Country\",\"postalCode\":\"69007\",\"geocodeQualityCode\":\"P1AAA\",\"geocodeQuality\":\"POINT\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"0\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":45.751714,\"lng\":4.842566},\"displayLatLng\":{\"lat\":45.751714,\"lng\":4.842566},\"mapUrl\":\"http://open.mapquestapi.com/staticmap/v5/map?key=secret_key&type=map&size=225,160&locations=45.7517141,4.8425657|marker-sm-50318A-1&scalebar=true&zoom=15&rand=-570915433\"}]}]}",
"headers": {
"Access-Control-Allow-Methods": "OPTIONS,GET,POST",
"Access-Control-Allow-Origin": "*",
"Cache-Control": "no-cache, must-revalidate",
"Content-Type": "application/json;charset=UTF-8",
"Date": "Thu, 14 Mar 2019 09:27:01 GMT",
"Expires": "Mon, 20 Dec 1998 01:00:00 GMT",
"GeocodeTransactionCount": "0",
"Last-Modified": "Thu, 14 Mar 2019 09:27:01 GMT",
"Pragma": "no-cache",
"ReverseGeocodeTransactionCount": "1",
"Server": "Apache-Coyote/1.1",
"Set-Cookie": "JSESSIONID=something; Path=/; HttpOnly",
"status": "success",
"transactionWeight": "1.0",
"Content-Length": "1063",
"Connection": "keep-alive"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,36 +0,0 @@
[
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://open.mapquestapi.com/geocoding/v1/address?key=secret_key&location=10%20rue%20Jangot&maxResults=10"
},
"response": {
"binary": false,
"body": "{\"info\":{\"statuscode\":0,\"copyright\":{\"text\":\"\\u00A9 2019 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2019 MapQuest, Inc.\"},\"messages\":[]},\"options\":{\"maxResults\":10,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{\"location\":\"10 rue Jangot\"},\"locations\":[{\"street\":\"10 Rue Jangot\",\"adminArea6\":\"7e\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"Lyon\",\"adminArea5Type\":\"City\",\"adminArea4\":\"Lyon\",\"adminArea4Type\":\"County\",\"adminArea3\":\"Auvergne-Rh\\u00F4ne-Alpes\",\"adminArea3Type\":\"State\",\"adminArea1\":\"FR\",\"adminArea1Type\":\"Country\",\"postalCode\":\"69007\",\"geocodeQualityCode\":\"P1AXX\",\"geocodeQuality\":\"POINT\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"0\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":45.751714,\"lng\":4.842566},\"displayLatLng\":{\"lat\":45.751714,\"lng\":4.842566},\"mapUrl\":\"http://open.mapquestapi.com/staticmap/v5/map?key=secret_key&type=map&size=225,160&locations=45.7517141,4.8425657|marker-sm-50318A-1&scalebar=true&zoom=15&rand=1358091752\"}]}]}",
"headers": {
"Access-Control-Allow-Methods": "OPTIONS,GET,POST",
"Access-Control-Allow-Origin": "*",
"Cache-Control": "no-cache, must-revalidate",
"Content-Type": "application/json;charset=UTF-8",
"Date": "Thu, 14 Mar 2019 09:27:01 GMT",
"Expires": "Mon, 20 Dec 1998 01:00:00 GMT",
"GeocodeTransactionCount": "1",
"Last-Modified": "Thu, 14 Mar 2019 09:27:01 GMT",
"Pragma": "no-cache",
"ReverseGeocodeTransactionCount": "0",
"Server": "Apache-Coyote/1.1",
"Set-Cookie": "JSESSIONID=something; Path=/; HttpOnly",
"status": "success",
"transactionWeight": "1.0",
"Content-Length": "1055",
"Connection": "keep-alive"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,32 +0,0 @@
[
{
"request": {
"body": "",
"headers": {
"User-Agent": "Test instance mobilizon.test - Mobilizon 1.0.0-beta.1"
},
"method": "get",
"options": [],
"request_body": "",
"url": "https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=45.751718&lon=4.842569&accept-language=en&addressdetails=1&namedetails=1"
},
"response": {
"binary": false,
"body": "{\"type\":\"FeatureCollection\",\"geocoding\":{\"version\":\"0.1.0\",\"attribution\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"licence\":\"ODbL\",\"query\":\"45.751718,4.842569\"},\"features\":[{\"type\":\"Feature\",\"properties\":{\"geocoding\":{\"place_id\":41453794,\"osm_type\":\"node\",\"osm_id\":3078260611,\"type\":\"house\",\"accuracy\":0,\"label\":\"10, Rue Jangot, La Guillotière, Lyon 7e Arrondissement, Lyon, Métropole de Lyon, Departemental constituency of Rhône, Auvergne-Rhône-Alpes, Metropolitan France, 69007, France\",\"name\":null,\"housenumber\":\"10\",\"street\":\"Rue Jangot\",\"postcode\":\"69007\",\"city\":\"Lyon\",\"county\":\"Lyon\",\"state\":\"Auvergne-Rhône-Alpes\",\"country\":\"France\",\"admin\":{\"level2\":\"France\",\"level3\":\"Metropolitan France\",\"level4\":\"Auvergne-Rhône-Alpes\",\"level5\":\"Departemental constituency of Rhône\",\"level6\":\"Métropole de Lyon\",\"level7\":\"Lyon\",\"level8\":\"Lyon\",\"level9\":\"Lyon 7e Arrondissement\"}}},\"geometry\":{\"type\":\"Point\",\"coordinates\":[4.8425657,45.7517141]}}]}",
"headers": {
"Date": "Tue, 12 Nov 2019 12:21:45 GMT",
"Server": "Apache/2.4.29 (Ubuntu)",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,GET",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"Expect-CT": "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"",
"Upgrade": "h2",
"Connection": "Upgrade, close",
"Transfer-Encoding": "chunked",
"Content-Type": "application/json; charset=UTF-8"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,32 +0,0 @@
[
{
"request": {
"body": "",
"headers": {
"User-Agent": "Test instance mobilizon.test - Mobilizon 1.0.0-beta.1"
},
"method": "get",
"options": [],
"request_body": "",
"url": "https://nominatim.openstreetmap.org/search?format=geocodejson&q=10%20rue%20Jangot&limit=10&accept-language=en&addressdetails=1&namedetails=1"
},
"response": {
"binary": false,
"body": "{\"type\":\"FeatureCollection\",\"geocoding\":{\"version\":\"0.1.0\",\"attribution\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"licence\":\"ODbL\",\"query\":\"10 rue Jangot\"},\"features\":[{\"type\":\"Feature\",\"properties\":{\"geocoding\":{\"place_id\":41453794,\"osm_type\":\"node\",\"osm_id\":3078260611,\"type\":\"house\",\"label\":\"10, Rue Jangot, La Guillotière, Lyon 7e Arrondissement, Lyon, Métropole de Lyon, Departemental constituency of Rhône, Auvergne-Rhône-Alpes, Metropolitan France, 69007, France\",\"name\":null,\"housenumber\":\"10\",\"street\":\"Rue Jangot\",\"postcode\":\"69007\",\"city\":\"Lyon\",\"county\":\"Lyon\",\"state\":\"Auvergne-Rhône-Alpes\",\"country\":\"France\",\"admin\":{\"level2\":\"France\",\"level3\":\"Metropolitan France\",\"level4\":\"Auvergne-Rhône-Alpes\",\"level5\":\"Departemental constituency of Rhône\",\"level6\":\"Métropole de Lyon\",\"level7\":\"Lyon\",\"level8\":\"Lyon\",\"level9\":\"Lyon 7e Arrondissement\"}}},\"geometry\":{\"type\":\"Point\",\"coordinates\":[4.8425657,45.7517141]}}]}",
"headers": {
"Date": "Tue, 12 Nov 2019 12:21:46 GMT",
"Server": "Apache/2.4.29 (Ubuntu)",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,GET",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"Expect-CT": "max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\"",
"Upgrade": "h2",
"Connection": "Upgrade, close",
"Transfer-Encoding": "chunked",
"Content-Type": "application/json; charset=UTF-8"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,26 +0,0 @@
[
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://photon.komoot.de/reverse?lon=4.842569&lat=45.751718"
},
"response": {
"binary": false,
"body": "{\"features\":[{\"geometry\":{\"coordinates\":[4.8416864,45.7605435],\"type\":\"Point\"},\"type\":\"Feature\",\"properties\":{\"osm_id\":4662865602,\"osm_type\":\"N\",\"country\":\"France\",\"osm_key\":\"leisure\",\"city\":\"Lyon\",\"street\":\"Rue Pravaz\",\"osm_value\":\"fitness_centre\",\"postcode\":\"69003\",\"name\":\"L'appart Fitness\",\"state\":\"Auvergne-Rhône-Alpes\"}}],\"type\":\"FeatureCollection\"}",
"headers": {
"Server": "nginx/1.9.3 (Ubuntu)",
"Date": "Thu, 14 Mar 2019 10:46:45 GMT",
"Content-Type": "application/json;charset=utf-8",
"Transfer-Encoding": "chunked",
"Connection": "keep-alive",
"Access-Control-Allow-Origin": "*"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -1,26 +0,0 @@
[
{
"request": {
"body": "",
"headers": [],
"method": "get",
"options": [],
"request_body": "",
"url": "https://photon.komoot.de/api/?q=10%20rue%20Jangot&lang=en&limit=10"
},
"response": {
"binary": false,
"body": "{\"features\":[{\"geometry\":{\"coordinates\":[4.8425657,45.7517141],\"type\":\"Point\"},\"type\":\"Feature\",\"properties\":{\"osm_id\":3078260611,\"osm_type\":\"N\",\"country\":\"France\",\"osm_key\":\"place\",\"housenumber\":\"10\",\"city\":\"Lyon\",\"street\":\"Rue Jangot\",\"osm_value\":\"house\",\"postcode\":\"69007\",\"state\":\"Auvergne-Rhône-Alpes\"}},{\"geometry\":{\"coordinates\":[4.8424254,45.7517056],\"type\":\"Point\"},\"type\":\"Feature\",\"properties\":{\"osm_id\":3078260612,\"osm_type\":\"N\",\"country\":\"France\",\"osm_key\":\"place\",\"housenumber\":\"10bis\",\"city\":\"Lyon\",\"street\":\"Rue Jangot\",\"osm_value\":\"house\",\"postcode\":\"69007\",\"state\":\"Auvergne-Rhône-Alpes\"}}],\"type\":\"FeatureCollection\"}",
"headers": {
"Server": "nginx/1.9.3 (Ubuntu)",
"Date": "Thu, 14 Mar 2019 10:46:43 GMT",
"Content-Type": "application/json;charset=utf-8",
"Transfer-Encoding": "chunked",
"Connection": "keep-alive",
"Access-Control-Allow-Origin": "*"
},
"status_code": 200,
"type": "ok"
}
}
]

View File

@@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.API.ReportTest do
import Mobilizon.Factory
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Comment
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Reports.{Note, Report}
alias Mobilizon.Service.Formatter.HTML

View File

@@ -0,0 +1,627 @@
defmodule Mobilizon.GraphQL.Resolvers.PostTest do
use Mobilizon.Web.ConnCase
import Mobilizon.Factory
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Posts.Post
alias Mobilizon.Users.User
alias Mobilizon.GraphQL.AbsintheHelpers
@post_fragment """
fragment PostFragment on Post {
id
title
slug
url
body
author {
id
preferredUsername
name
avatar {
url
}
}
attributedTo {
id
preferredUsername
name
avatar {
url
}
}
visibility
insertedAt
updatedAt
draft
}
"""
@get_group_posts """
query($name: String!, $page: Int, $limit: Int) {
group(preferredUsername: $name) {
id
posts(page: $page, limit: $limit) {
elements {
id,
title,
},
total
},
}
}
"""
@post_query """
query Post($slug: String!) {
post(slug: $slug) {
...PostFragment
}
}
#{@post_fragment}
"""
@create_post """
mutation CreatePost($title: String!, $body: String, $attributedToId: ID!, $draft: Boolean) {
createPost(title: $title, body: $body, attributedToId: $attributedToId, draft: $draft) {
...PostFragment
}
}
#{@post_fragment}
"""
@update_post """
mutation UpdatePost($id: ID!, $title: String, $body: String, $attributedToId: ID, $draft: Boolean) {
updatePost(id: $id, title: $title, body: $body, attributedToId: $attributedToId, draft: $draft) {
...PostFragment
}
}
#{@post_fragment}
"""
@delete_post """
mutation DeletePost($id: ID!) {
deletePost(id: $id) {
id
}
}
"""
@post_title "my post"
@updated_post_title "my updated post"
setup do
%User{} = user = insert(:user)
%Actor{} = actor = insert(:actor, user: user)
%Actor{} = group = insert(:group)
%Post{} = post = insert(:post, attributed_to: group, author: actor)
%Post{} =
post_unlisted = insert(:post, attributed_to: group, author: actor, visibility: :unlisted)
%Post{} = post_draft = insert(:post, attributed_to: group, author: actor, draft: true)
%Member{} = insert(:member, parent: group, actor: actor, role: :member)
%Post{} =
post_private = insert(:post, attributed_to: group, author: actor, visibility: :private)
{:ok,
user: user,
group: group,
post: post,
post_unlisted: post_unlisted,
post_draft: post_draft,
post_private: post_private}
end
describe "Resolver: Get group's posts" do
test "find_posts_for_group/3", %{
conn: conn,
user: user,
group: group,
post: post,
post_unlisted: post_unlisted,
post_draft: post_draft,
post_private: post_private
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @get_group_posts,
variables: %{
name: group.preferred_username
}
)
assert is_nil(res["errors"])
assert res["data"]["group"]["posts"]["total"] == 4
assert res["data"]["group"]["posts"]["elements"] |> Enum.map(& &1["id"]) |> MapSet.new() ==
MapSet.new([
post.id,
post_unlisted.id,
post_draft.id,
post_private.id
])
end
test "find_posts_for_group/3 when not member of group", %{
conn: conn,
group: group,
post: post
} do
%User{} = user = insert(:user)
%Actor{} = insert(:actor, user: user)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @get_group_posts,
variables: %{
name: group.preferred_username
}
)
assert is_nil(res["errors"])
assert res["data"]["group"]["posts"]["total"] == 1
assert res["data"]["group"]["posts"]["elements"] |> Enum.map(& &1["id"]) == [post.id]
end
test "find_posts_for_group/3 when not connected", %{
conn: conn,
group: group,
post: post
} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @get_group_posts,
variables: %{
name: group.preferred_username
}
)
assert is_nil(res["errors"])
assert res["data"]["group"]["posts"]["total"] == 1
assert res["data"]["group"]["posts"]["elements"] |> Enum.map(& &1["id"]) == [post.id]
end
end
describe "Resolver: Get a specific post" do
test "get_post/3 for a public post", %{
conn: conn,
user: user,
post: post
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post.title
end
test "get_post/3 for a non-existing post", %{
conn: conn,
user: user
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: "not existing"
}
)
assert hd(res["errors"])["message"] == "No such post"
end
test "get_post/3 for an unlisted post", %{
conn: conn,
user: user,
post_unlisted: post_unlisted
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_unlisted.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post_unlisted.title
assert res["data"]["post"]["visibility"] ==
post_unlisted.visibility |> to_string() |> String.upcase()
end
test "get_post/3 for a private post", %{
conn: conn,
user: user,
post_private: post_private
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_private.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post_private.title
assert res["data"]["post"]["visibility"] ==
post_private.visibility |> to_string() |> String.upcase()
end
test "get_post/3 for a draft post", %{
conn: conn,
user: user,
post_draft: post_draft
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_draft.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post_draft.title
assert res["data"]["post"]["draft"] == true
end
test "get_post/3 without being a member for a public post", %{
conn: conn,
post: post
} do
%User{} = user = insert(:user)
%Actor{} = insert(:actor, user: user)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post.title
end
test "get_post/3 without being connected for a public post", %{
conn: conn,
post: post
} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post.title
end
test "get_post/3 without being a member for an unlisted post", %{
conn: conn,
post_unlisted: post_unlisted
} do
%User{} = user = insert(:user)
%Actor{} = insert(:actor, user: user)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_unlisted.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post_unlisted.title
assert res["data"]["post"]["visibility"] ==
post_unlisted.visibility |> to_string() |> String.upcase()
end
test "get_post/3 without being a member for a private post", %{
conn: conn,
post_private: post_private
} do
%User{} = user = insert(:user)
%Actor{} = insert(:actor, user: user)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_private.slug
}
)
assert hd(res["errors"])["message"] == "No such post"
end
test "get_post/3 without being connected for an unlisted post still gives the post", %{
conn: conn,
post_unlisted: post_unlisted
} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_unlisted.slug
}
)
assert is_nil(res["errors"])
assert res["data"]["post"]["title"] == post_unlisted.title
assert res["data"]["post"]["visibility"] ==
post_unlisted.visibility |> to_string() |> String.upcase()
end
test "get_post/3 without being connected for a private post", %{
conn: conn,
post_private: post_private
} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_private.slug
}
)
assert hd(res["errors"])["message"] == "No such post"
end
test "get_post/3 without being a member for a draft post", %{
conn: conn,
post_draft: post_draft
} do
%User{} = user = insert(:user)
%Actor{} = insert(:actor, user: user)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_draft.slug
}
)
assert hd(res["errors"])["message"] == "No such post"
end
test "get_post/3 without being connected for a draft post", %{
conn: conn,
post_draft: post_draft
} do
res =
conn
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_draft.slug
}
)
assert hd(res["errors"])["message"] == "No such post"
end
end
describe "Resolver: Create a post" do
test "create_post/3 creates a post for a group", %{
conn: conn,
user: user,
group: group
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @create_post,
variables: %{
title: @post_title,
body: "My new post is here",
attributedToId: group.id
}
)
assert is_nil(res["errors"])
assert res["data"]["createPost"]["title"] == @post_title
id = res["data"]["createPost"]["id"]
assert res["data"]["createPost"]["slug"] == "my-post-#{ShortUUID.encode!(id)}"
end
test "create_post/3 doesn't create a post if no group is defined", %{
conn: conn,
user: user
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @create_post,
variables: %{
title: @post_title,
body: "some body",
attributedToId: nil
}
)
assert Enum.map(res["errors"], & &1["message"]) == [
"Argument \"attributedToId\" has invalid value $attributedToId.",
"Variable \"attributedToId\": Expected non-null, found null."
]
end
test "create_post/3 doesn't create a post if the actor is not a member of the group",
%{
conn: conn,
group: group
} do
%User{} = user = insert(:user)
%Actor{} = insert(:actor, user: user)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @create_post,
variables: %{
title: @post_title,
body: "My body",
attributedToId: group.id
}
)
assert Enum.map(res["errors"], & &1["message"]) == [
"Actor id is not member of group"
]
end
end
describe "Resolver: Update a post" do
test "update_post/3 updates a post for a group", %{
conn: conn,
user: user,
group: group
} do
%Post{id: post_id} = insert(:post, attributed_to: group)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @update_post,
variables: %{
id: post_id,
title: @updated_post_title
}
)
assert is_nil(res["errors"])
assert res["data"]["updatePost"]["title"] == @updated_post_title
end
end
describe "Resolver: Delete a post" do
test "delete_post/3 deletes a post", %{
conn: conn,
user: user,
group: group
} do
%Post{id: post_id, slug: post_slug} =
insert(:post,
attributed_to: group
)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @delete_post,
variables: %{
id: post_id
}
)
assert is_nil(res["errors"])
assert res["data"]["deletePost"]["id"] == post_id
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @post_query,
variables: %{
slug: post_slug
}
)
assert hd(res["errors"])["message"] == "No such post"
end
test "delete_post/3 deletes a post not found", %{
conn: conn,
user: user
} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @delete_post,
variables: %{
id: "not found"
}
)
assert hd(res["errors"])["message"] == "Post ID is not a valid ID"
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @delete_post,
variables: %{
id: "d276ef98-8433-48d7-890e-c24eda0dcdbe"
}
)
assert hd(res["errors"])["message"] == "Post doesn't exist"
end
end
end

View File

@@ -5,9 +5,9 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do
import Mobilizon.Factory
alias Mobilizon.{Actors, Config, Conversations, Events, Users}
alias Mobilizon.{Actors, Config, Discussions, Events, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Comment
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Service.Auth.Authenticator
alias Mobilizon.Users.User
@@ -1388,7 +1388,7 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do
end
assert_raise Ecto.NoResultsError, fn ->
Conversations.get_comment!(comment_id)
Discussions.get_comment!(comment_id)
end
# Actors are not deleted but emptied (to keep the username reserved)

View File

@@ -5,9 +5,9 @@ defmodule Mobilizon.ActorsTest do
import Mobilizon.Factory
alias Mobilizon.{Actors, Config, Conversations, Events, Tombstone, Users}
alias Mobilizon.{Actors, Config, Discussions, Events, Tombstone, Users}
alias Mobilizon.Actors.{Actor, Bot, Follower, Member}
alias Mobilizon.Conversations.Comment
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Media.File, as: FileModel
alias Mobilizon.Service.Workers
@@ -291,6 +291,56 @@ defmodule Mobilizon.ActorsTest do
assert actor = actor_fetched
end
test "perform delete the actor actually deletes the actor", %{
actor: %Actor{avatar: %{url: avatar_url}, banner: %{url: banner_url}, id: actor_id} = actor
} do
%Event{url: event1_url} = event1 = insert(:event, organizer_actor: actor)
insert(:event, organizer_actor: actor)
%Comment{url: comment1_url} = comment1 = insert(:comment, actor: actor)
insert(:comment, actor: actor)
%URI{path: "/media/" <> avatar_path} = URI.parse(avatar_url)
%URI{path: "/media/" <> banner_path} = URI.parse(banner_url)
assert File.exists?(
Config.get!([Uploader.Local, :uploads]) <>
"/" <> avatar_path
)
assert File.exists?(
Config.get!([Uploader.Local, :uploads]) <>
"/" <> banner_path
)
assert {:ok, %Actor{}} = Actors.perform(:delete_actor, actor)
assert %Actor{
name: nil,
summary: nil,
suspended: true,
avatar: nil,
banner: nil,
user_id: nil
} = Actors.get_actor(actor_id)
assert {:error, :event_not_found} = Events.get_event(event1.id)
assert %Tombstone{} = Tombstone.find_tombstone(event1_url)
assert %Comment{deleted_at: deleted_at} = Discussions.get_comment(comment1.id)
refute is_nil(deleted_at)
assert %Tombstone{} = Tombstone.find_tombstone(comment1_url)
refute File.exists?(
Config.get!([Uploader.Local, :uploads]) <>
"/" <> avatar_path
)
refute File.exists?(
Config.get!([Uploader.Local, :uploads]) <>
"/" <> banner_path
)
end
test "delete_actor/1 deletes the actor", %{
actor: %Actor{avatar: %{url: avatar_url}, banner: %{url: banner_url}, id: actor_id} = actor
} do
@@ -333,7 +383,7 @@ defmodule Mobilizon.ActorsTest do
assert {:error, :event_not_found} = Events.get_event(event1.id)
assert %Tombstone{} = Tombstone.find_tombstone(event1_url)
assert %Comment{deleted_at: deleted_at} = Conversations.get_comment(comment1.id)
assert %Comment{deleted_at: deleted_at} = Discussions.get_comment(comment1.id)
refute is_nil(deleted_at)
assert %Tombstone{} = Tombstone.find_tombstone(comment1_url)

View File

@@ -1,13 +1,11 @@
defmodule Mobilizon.ConversationsTest do
defmodule Mobilizon.DiscussionsTest do
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations
alias Mobilizon.Conversations.Comment
alias Mobilizon.Service.Workers
alias Mobilizon.Storage.Page
alias Mobilizon.Discussions
alias Mobilizon.Discussions.Comment
describe "comments" do
@valid_attrs %{text: "some text"}
@@ -16,13 +14,13 @@ defmodule Mobilizon.ConversationsTest do
test "list_comments/0 returns all comments" do
%Comment{id: comment_id} = insert(:comment)
comment_ids = Conversations.list_comments() |> Enum.map(& &1.id)
comment_ids = Discussions.list_comments() |> Enum.map(& &1.id)
assert comment_ids == [comment_id]
end
test "get_comment!/1 returns the comment with given id" do
%Comment{id: comment_id} = insert(:comment)
comment_fetched = Conversations.get_comment!(comment_id)
comment_fetched = Discussions.get_comment!(comment_id)
assert comment_fetched.id == comment_id
end
@@ -30,7 +28,7 @@ defmodule Mobilizon.ConversationsTest do
%Actor{} = actor = insert(:actor)
comment_data = Map.merge(@valid_attrs, %{actor_id: actor.id})
case Conversations.create_comment(comment_data) do
case Discussions.create_comment(comment_data) do
{:ok, %Comment{} = comment} ->
assert comment.text == "some text"
assert comment.actor_id == actor.id
@@ -41,13 +39,13 @@ defmodule Mobilizon.ConversationsTest do
end
test "create_comment/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Conversations.create_comment(@invalid_attrs)
assert {:error, %Ecto.Changeset{}} = Discussions.create_comment(@invalid_attrs)
end
test "update_comment/2 with valid data updates the comment" do
%Comment{} = comment = insert(:comment)
case Conversations.update_comment(comment, @update_attrs) do
case Discussions.update_comment(comment, @update_attrs) do
{:ok, %Comment{} = comment} ->
assert comment.text == "some updated text"
@@ -58,15 +56,15 @@ defmodule Mobilizon.ConversationsTest do
test "update_comment/2 with invalid data returns error changeset" do
%Comment{} = comment = insert(:comment)
assert {:error, %Ecto.Changeset{}} = Conversations.update_comment(comment, @invalid_attrs)
%Comment{} = comment_fetched = Conversations.get_comment!(comment.id)
assert {:error, %Ecto.Changeset{}} = Discussions.update_comment(comment, @invalid_attrs)
%Comment{} = comment_fetched = Discussions.get_comment!(comment.id)
assert comment = comment_fetched
end
test "delete_comment/1 deletes the comment" do
%Comment{} = comment = insert(:comment)
assert {:ok, %Comment{}} = Conversations.delete_comment(comment)
refute is_nil(Conversations.get_comment!(comment.id).deleted_at)
assert {:ok, %Comment{}} = Discussions.delete_comment(comment)
refute is_nil(Discussions.get_comment!(comment.id).deleted_at)
end
end
end

View File

@@ -141,7 +141,7 @@ defmodule Mobilizon.EventsTest do
end
test "list_public_events_for_actor/1", %{actor: actor, event: event} do
assert {:ok, [event_found], 1} = Events.list_public_events_for_actor(actor)
assert %Page{elements: [event_found], total: 1} = Events.list_public_events_for_actor(actor)
assert event_found.title == event.title
end
@@ -149,7 +149,7 @@ defmodule Mobilizon.EventsTest do
event1 = insert(:event, organizer_actor: actor)
case Events.list_public_events_for_actor(actor, 1, 10) do
{:ok, events_found, 2} ->
%Page{elements: events_found, total: 2} ->
event_ids = MapSet.new(events_found |> Enum.map(& &1.id))
assert event_ids == MapSet.new([event.id, event1.id])
@@ -162,7 +162,7 @@ defmodule Mobilizon.EventsTest do
event1 = insert(:event, organizer_actor: actor)
case Events.list_public_events_for_actor(actor, 1, 1) do
{:ok, [%Event{id: event_found_id}], 2} ->
%Page{elements: [%Event{id: event_found_id}], total: 2} ->
assert event_found_id in [event.id, event1.id]
err ->

View File

@@ -0,0 +1,82 @@
defmodule Mobilizon.PostsTest do
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Actors.Actor
alias Mobilizon.Posts
alias Mobilizon.Posts.Post
describe "posts" do
@valid_attrs %{body: "some text", title: "some title"}
@update_attrs %{body: "some updated text", title: "some updated title"}
@invalid_attrs %{body: nil}
test "list_posts/0 returns all posts" do
group = insert(:group)
%Post{id: post_id} = insert(:post, attributed_to: group)
post_ids = Posts.get_posts_for_group(group).elements |> Enum.map(& &1.id)
assert post_ids == [post_id]
end
test "get_post!/1 returns the post with given id" do
%Post{id: post_id} = insert(:post)
post_fetched = Posts.get_post(post_id)
assert post_fetched.id == post_id
end
test "create_post/1 with valid data creates a post" do
%Actor{} = actor = insert(:actor)
%Actor{} = group = insert(:group)
post_data = Map.merge(@valid_attrs, %{author_id: actor.id, attributed_to_id: group.id})
case Posts.create_post(post_data) do
{:ok, %Post{} = post} ->
assert post.body == "some text"
assert post.author_id == actor.id
assert post.title == "some title"
assert post.slug == "some-title-" <> ShortUUID.encode!(post.id)
err ->
flunk("Failed to create a post #{inspect(err)}")
end
end
test "create_post/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Posts.create_post(@invalid_attrs)
end
test "update_post/2 with valid data updates the post" do
%Actor{} = actor = insert(:actor)
%Actor{} = group = insert(:group)
post_data = Map.merge(@valid_attrs, %{author_id: actor.id, attributed_to_id: group.id})
{:ok, %Post{} = post} = Posts.create_post(post_data)
case Posts.update_post(post, @update_attrs) do
{:ok, %Post{} = updated_post} ->
assert updated_post.body == @update_attrs.body
assert updated_post.title == @update_attrs.title
# Slug and URL don't change
assert updated_post.slug == post.slug
assert updated_post.url == post.url
err ->
flunk("Failed to update a post #{inspect(err)}")
end
end
test "update_post/2 with invalid data returns error changeset" do
%Post{} = post = insert(:post)
assert {:error, %Ecto.Changeset{}} = Posts.update_post(post, @invalid_attrs)
%Post{} = post_fetched = Posts.get_post(post.id)
assert post = post_fetched
end
test "delete_post/1 deletes the post" do
%Post{} = post = insert(:post)
assert {:ok, %Post{}} = Posts.delete_post(post)
assert is_nil(Posts.get_post(post.id))
end
end
end

View File

@@ -1,91 +1,63 @@
defmodule Mobilizon.Service.Geospatial.AddokTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
import Mock
import Mox
alias Mobilizon.Addresses.Address
alias Mobilizon.Config
alias Mobilizon.Service.Geospatial.Addok
@http_options [
follow_redirect: true,
ssl: [{:versions, [:"tlsv1.2"]}]
]
setup do
# Config.instance_user_agent/0 makes database calls so because of ownership connection
# we need to define it like this instead of a constant
# See https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html
{:ok,
httpoison_headers: [
{"User-Agent", Config.instance_user_agent()}
]}
end
@endpoint get_in(Application.get_env(:mobilizon, Addok), [:endpoint])
@fake_endpoint "https://domain.tld"
alias Mobilizon.Service.HTTP.BaseClient.Mock
describe "search address" do
test "produces a valid search address", %{httpoison_headers: httpoison_headers} do
with_mock HTTPoison, get: fn _url, _headers, _options -> "{}" end do
Addok.search("10 Rue Jangot")
assert_called(
HTTPoison.get(
"#{@endpoint}/search/?q=10%20Rue%20Jangot&limit=10",
httpoison_headers,
@http_options
)
)
end
end
test "produces a valid search address with options", %{httpoison_headers: httpoison_headers} do
with_mock HTTPoison, get: fn _url, _headers, _options -> "{}" end do
Addok.search("10 Rue Jangot",
endpoint: @fake_endpoint,
limit: 5,
coords: %{lat: 49, lon: 12}
)
assert_called(
HTTPoison.get(
"#{@fake_endpoint}/search/?q=10%20Rue%20Jangot&limit=5&lat=49&lon=12",
httpoison_headers,
@http_options
)
)
end
end
test "returns a valid address from search" do
use_cassette "geospatial/addok/search" do
assert %Address{
country: "France",
region: "69, Rhône, Auvergne-Rhône-Alpes",
locality: "Lyon",
description: "10 Rue Jangot",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{coordinates: {4.842569, 45.751718}, properties: %{}, srid: 4326}
} == Addok.search("10 rue Jangot") |> hd
end
data =
File.read!("test/fixtures/geospatial/addok/search.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url: "https://api-adresse.data.gouv.fr/search/?q=10%20rue%20Jangot&limit=10"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert %Address{
country: "France",
region: "69, Rhône, Auvergne-Rhône-Alpes",
locality: "Lyon",
description: "10 Rue Jangot",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{coordinates: {4.842569, 45.751718}, properties: %{}, srid: 4326}
} == Addok.search("10 rue Jangot") |> hd
end
test "returns a valid address from reverse geocode" do
use_cassette "geospatial/addok/geocode" do
assert %Address{
country: "France",
region: "69, Rhône, Auvergne-Rhône-Alpes",
locality: "Lyon",
description: "10 Rue Jangot",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{coordinates: {4.842569, 45.751718}, properties: %{}, srid: 4326}
} == Addok.geocode(4.842569, 45.751718) |> hd
end
data =
File.read!("test/fixtures/geospatial/addok/geocode.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url: "https://api-adresse.data.gouv.fr/reverse/?lon=4.842569&lat=45.751718&limit=10"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert %Address{
country: "France",
region: "69, Rhône, Auvergne-Rhône-Alpes",
locality: "Lyon",
description: "10 Rue Jangot",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{coordinates: {4.842569, 45.751718}, properties: %{}, srid: 4326}
} == Addok.geocode(4.842569, 45.751718) |> hd
end
end
end

View File

@@ -1,17 +1,11 @@
defmodule Mobilizon.Service.Geospatial.GoogleMapsTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
import Mock
import Mox
alias Mobilizon.Addresses.Address
alias Mobilizon.Service.Geospatial.GoogleMaps
@http_options [
follow_redirect: true,
ssl: [{:versions, [:"tlsv1.2"]}]
]
alias Mobilizon.Service.HTTP.BaseClient.Mock
describe "search address" do
test "without API Key triggers an error" do
@@ -20,76 +14,117 @@ defmodule Mobilizon.Service.Geospatial.GoogleMapsTest do
end
end
test "produces a valid search address with options" do
with_mock HTTPoison,
get: fn _url, _headers, _options ->
{:ok,
%HTTPoison.Response{status_code: 200, body: "{\"status\": \"OK\", \"results\": []}"}}
end do
GoogleMaps.search("10 Rue Jangot",
limit: 5,
lang: "fr",
api_key: "toto"
)
assert_called(
HTTPoison.get(
"https://maps.googleapis.com/maps/api/geocode/json?limit=5&key=toto&language=fr&address=10%20Rue%20Jangot",
[],
@http_options
)
)
end
end
test "triggers an error with an invalid API Key" do
data =
File.read!("test/fixtures/geospatial/google_maps/api_key_invalid.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url:
"https://maps.googleapis.com/maps/api/geocode/json?limit=10&key=secret_key&language=en&address=10%20rue%20Jangot"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert_raise ArgumentError, "The provided API key is invalid.", fn ->
GoogleMaps.search("10 rue Jangot", api_key: "secret_key")
end
end
test "returns a valid address from search" do
use_cassette "geospatial/google_maps/search" do
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8424032, 45.75164940000001},
properties: %{},
srid: 4326
},
origin_id: "gm:ChIJtW0QikTq9EcRLI4Vy6bRx0U"
} ==
GoogleMaps.search("10 rue Jangot",
api_key: "toto"
)
|> hd
end
data =
File.read!("test/fixtures/geospatial/google_maps/search.json")
|> Jason.decode!()
data_2 =
File.read!("test/fixtures/geospatial/google_maps/search_2.json")
|> Jason.decode!()
Mock
|> expect(:call, 3, fn
%{
method: :get,
url:
"https://maps.googleapis.com/maps/api/geocode/json?limit=10&key=toto&language=en&address=10%20rue%20Jangot"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{
method: :get,
url: _url
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data_2}}
end)
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8424032, 45.75164940000001},
properties: %{},
srid: 4326
},
origin_id: "gm:ChIJtW0QikTq9EcRLI4Vy6bRx0U"
} ==
GoogleMaps.search("10 rue Jangot",
api_key: "toto"
)
|> hd
end
test "returns a valid address from reverse geocode" do
use_cassette "geospatial/google_maps/geocode" do
assert %Address{
locality: "Lyon",
description: "10bis Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10bis Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8424966, 45.751725},
properties: %{},
srid: 4326
},
origin_id: "gm:ChIJrW0QikTq9EcR96jk2OnO75w"
} ==
GoogleMaps.geocode(4.842569, 45.751718, api_key: "toto")
|> hd
end
data =
File.read!("test/fixtures/geospatial/google_maps/geocode.json")
|> Jason.decode!()
data_2 =
File.read!("test/fixtures/geospatial/google_maps/geocode_2.json")
|> Jason.decode!()
Mock
|> expect(:call, 3, fn
%{
method: :get,
url:
"https://maps.googleapis.com/maps/api/geocode/json?limit=10&key=toto&language=en&latlng=45.751718,4.842569&result_type=street_address"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{
method: :get,
url: _url
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data_2}}
end)
assert %Address{
locality: "Lyon",
description: "10bis Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10bis Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8424966, 45.751725},
properties: %{},
srid: 4326
},
origin_id: "gm:ChIJrW0QikTq9EcR96jk2OnO75w"
} ==
GoogleMaps.geocode(4.842569, 45.751718, api_key: "toto")
|> hd
end
end
end

View File

@@ -1,28 +1,11 @@
defmodule Mobilizon.Service.Geospatial.MapQuestTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
import Mock
import Mox
alias Mobilizon.Addresses.Address
alias Mobilizon.Config
alias Mobilizon.Service.Geospatial.MapQuest
@http_options [
follow_redirect: true,
ssl: [{:versions, [:"tlsv1.2"]}]
]
setup do
# Config.instance_user_agent/0 makes database calls so because of ownership connection
# we need to define it like this instead of a constant
# See https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html
{:ok,
httpoison_headers: [
{"User-Agent", Config.instance_user_agent()}
]}
end
alias Mobilizon.Service.HTTP.BaseClient.Mock
describe "search address" do
test "without API Key triggers an error" do
@@ -31,75 +14,88 @@ defmodule Mobilizon.Service.Geospatial.MapQuestTest do
end
end
test "produces a valid search address with options", %{httpoison_headers: httpoison_headers} do
with_mock HTTPoison,
get: fn _url, _headers, _options ->
{:ok,
%HTTPoison.Response{
status_code: 200,
body: "{\"info\": {\"statuscode\": 0}, \"results\": []}"
}}
end do
MapQuest.search("10 Rue Jangot",
limit: 5,
lang: "fr",
api_key: "toto"
)
assert_called(
HTTPoison.get(
"https://open.mapquestapi.com/geocoding/v1/address?key=toto&location=10%20Rue%20Jangot&maxResults=5",
httpoison_headers,
@http_options
)
)
end
end
test "triggers an error with an invalid API Key" do
Mock
|> expect(:call, fn
%{
method: :get,
url:
"https://open.mapquestapi.com/geocoding/v1/address?key=secret_key&location=10%20rue%20Jangot&maxResults=10"
},
_opts ->
{:ok,
%Tesla.Env{status: 403, body: "The AppKey submitted with this request is invalid."}}
end)
assert_raise ArgumentError, "The AppKey submitted with this request is invalid.", fn ->
MapQuest.search("10 rue Jangot", api_key: "secret_key")
end
end
test "returns a valid address from search" do
use_cassette "geospatial/map_quest/search" do
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "FR",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.842566, 45.751714},
properties: %{},
srid: 4326
}
} ==
MapQuest.search("10 rue Jangot", api_key: "secret_key")
|> hd
end
data =
File.read!("test/fixtures/geospatial/map_quest/search.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url:
"https://open.mapquestapi.com/geocoding/v1/address?key=secret_key&location=10%20rue%20Jangot&maxResults=10"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "FR",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.842566, 45.751714},
properties: %{},
srid: 4326
}
} ==
MapQuest.search("10 rue Jangot", api_key: "secret_key")
|> hd
end
test "returns a valid address from reverse geocode" do
use_cassette "geospatial/map_quest/geocode" do
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "FR",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.842569, 45.751718},
properties: %{},
srid: 4326
}
} ==
MapQuest.geocode(4.842569, 45.751718, api_key: "secret_key")
|> hd
end
data =
File.read!("test/fixtures/geospatial/map_quest/geocode.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url:
"https://open.mapquestapi.com/geocoding/v1/reverse?key=secret_key&location=45.751718,4.842569&maxResults=10"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "FR",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.842569, 45.751718},
properties: %{},
srid: 4326
}
} ==
MapQuest.geocode(4.842569, 45.751718, api_key: "secret_key")
|> hd
end
end
end

View File

@@ -1,91 +1,81 @@
defmodule Mobilizon.Service.Geospatial.NominatimTest do
use Mobilizon.DataCase, async: false
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
import Mock
use Mobilizon.DataCase
import Mox
alias Mobilizon.Addresses.Address
alias Mobilizon.Config
alias Mobilizon.Service.Geospatial.Nominatim
@http_options [
follow_redirect: true,
ssl: [{:versions, [:"tlsv1.2"]}]
]
setup do
# Config.instance_user_agent/0 makes database calls so because of ownership connection
# we need to define it like this instead of a constant
# See https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html
{:ok,
httpoison_headers: [
{"User-Agent", Config.instance_user_agent()}
]}
end
alias Mobilizon.Service.HTTP.BaseClient.Mock
describe "search address" do
test "produces a valid search address with options", %{httpoison_headers: httpoison_headers} do
with_mock HTTPoison,
get: fn _url, _headers, _options ->
{:ok, %HTTPoison.Response{status_code: 200, body: "[]"}}
end do
Nominatim.search("10 Rue Jangot",
limit: 5,
lang: "fr"
)
assert_called(
HTTPoison.get(
"https://nominatim.openstreetmap.org/search?format=geocodejson&q=10%20Rue%20Jangot&limit=5&accept-language=fr&addressdetails=1&namedetails=1",
httpoison_headers,
@http_options
)
)
end
end
test "returns a valid address from search" do
use_cassette "geospatial/nominatim/search" do
assert [
%Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8425657, 45.7517141},
properties: %{},
srid: 4326
},
origin_id: "nominatim:3078260611",
type: "house"
}
] == Nominatim.search("10 rue Jangot")
end
data =
File.read!("test/fixtures/geospatial/nominatim/search.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url:
"https://nominatim.openstreetmap.org/search?format=geocodejson&q=10%20rue%20Jangot&limit=10&accept-language=en&addressdetails=1&namedetails=1"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert [
%Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8425657, 45.7517141},
properties: %{},
srid: 4326
},
origin_id: "nominatim:3078260611",
type: "house"
}
] == Nominatim.search("10 rue Jangot")
end
test "returns a valid address from reverse geocode" do
use_cassette "geospatial/nominatim/geocode" do
assert [
%Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8425657, 45.7517141},
properties: %{},
srid: 4326
},
origin_id: "nominatim:3078260611",
type: "house"
}
] ==
Nominatim.geocode(4.842569, 45.751718)
end
data =
File.read!("test/fixtures/geospatial/nominatim/geocode.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url:
"https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=45.751718&lon=4.842569&accept-language=en&addressdetails=1&namedetails=1"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert [
%Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8425657, 45.7517141},
properties: %{},
srid: 4326
},
origin_id: "nominatim:3078260611",
type: "house"
}
] ==
Nominatim.geocode(4.842569, 45.751718)
end
end
end

View File

@@ -1,66 +1,41 @@
defmodule Mobilizon.Service.Geospatial.PhotonTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
import Mock
import Mox
alias Mobilizon.Addresses.Address
alias Mobilizon.Config
alias Mobilizon.Service.Geospatial.Photon
@http_options [
follow_redirect: true,
ssl: [{:versions, [:"tlsv1.2"]}]
]
setup do
# Config.instance_user_agent/0 makes database calls so because of ownership connection
# we need to define it like this instead of a constant
# See https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html
{:ok,
httpoison_headers: [
{"User-Agent", Config.instance_user_agent()}
]}
end
alias Mobilizon.Service.HTTP.BaseClient.Mock
describe "search address" do
test "produces a valid search address with options", %{httpoison_headers: httpoison_headers} do
with_mock HTTPoison,
get: fn _url, _headers, _options ->
{:ok, %HTTPoison.Response{status_code: 200, body: "{\"features\": []"}}
end do
Photon.search("10 Rue Jangot",
limit: 5,
lang: "fr"
)
assert_called(
HTTPoison.get(
"https://photon.komoot.de/api/?q=10%20Rue%20Jangot&lang=fr&limit=5",
httpoison_headers,
@http_options
)
)
end
end
test "returns a valid address from search" do
use_cassette "geospatial/photon/search" do
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8425657, 45.7517141},
properties: %{},
srid: 4326
}
} == Photon.search("10 rue Jangot") |> hd
end
data =
File.read!("test/fixtures/geospatial/photon/search.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{
method: :get,
url: "https://photon.komoot.de/api/?q=10%20rue%20Jangot&lang=en&limit=10"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
end)
assert %Address{
locality: "Lyon",
description: "10 Rue Jangot",
region: "Auvergne-Rhône-Alpes",
country: "France",
postal_code: "69007",
street: "10 Rue Jangot",
geom: %Geo.Point{
coordinates: {4.8425657, 45.7517141},
properties: %{},
srid: 4326
}
} == Photon.search("10 rue Jangot") |> hd
end
# Photon returns something quite wrong, so no need to test this right now.

View File

@@ -73,4 +73,7 @@ defmodule Mobilizon.DataCase do
:ok
end
Mox.defmock(Mobilizon.Service.HTTP.ActivityPub.Mock, for: Tesla.Adapter)
Mox.defmock(Mobilizon.Service.HTTP.BaseClient.Mock, for: Tesla.Adapter)
end

View File

@@ -118,12 +118,15 @@ defmodule Mobilizon.Factory do
def comment_factory do
uuid = Ecto.UUID.generate()
%Mobilizon.Conversations.Comment{
%Mobilizon.Discussions.Comment{
text: "My Comment",
actor: build(:actor),
event: build(:event),
uuid: uuid,
mentions: [],
attributed_to: nil,
local: true,
deleted_at: nil,
tags: build_list(3, :tag),
in_reply_to_comment: nil,
url: Routes.page_url(Endpoint, :comment, uuid)
@@ -142,6 +145,7 @@ defmodule Mobilizon.Factory do
begins_on: start,
ends_on: Timex.shift(start, hours: 2),
organizer_actor: actor,
attributed_to: nil,
category: sequence("something"),
physical_address: build(:address),
visibility: :public,
@@ -324,4 +328,50 @@ defmodule Mobilizon.Factory do
value: sequence("value")
}
end
def post_factory do
uuid = Ecto.UUID.generate()
%Mobilizon.Posts.Post{
body: "The <b>HTML</b>body for my Article",
title: "My Awesome article",
slug: "my-awesome-article-#{ShortUUID.encode!(uuid)}",
author: build(:actor),
attributed_to: build(:group),
id: uuid,
draft: false,
tags: build_list(3, :tag),
visibility: :public,
publish_at: DateTime.utc_now(),
url: Routes.page_url(Endpoint, :post, uuid)
}
end
def tombstone_factory do
uuid = Ecto.UUID.generate()
%Mobilizon.Tombstone{
uri: "https://mobilizon.test/comments/#{uuid}",
actor: build(:actor)
}
end
def discussion_factory do
uuid = Ecto.UUID.generate()
actor = build(:actor)
group = build(:group)
comment = build(:comment, actor: actor, attributed_to: group)
slug = "my-awesome-discussion-#{ShortUUID.encode!(uuid)}"
%Mobilizon.Discussions.Discussion{
title: "My Awesome discussion",
slug: slug,
creator: actor,
actor: group,
id: uuid,
last_comment: comment,
comments: [comment],
url: Routes.page_url(Endpoint, :discussion, group.preferred_username, slug)
}
end
end

View File

@@ -344,11 +344,12 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
Actors.create_group(%{
creator_actor_id: actor.id,
preferred_username: "my_group",
visibility: :public
local: true
})
result =
conn
|> assign(:actor, actor)
|> get(Actor.build_url(group.preferred_username, :members))
|> json_response(200)
@@ -358,15 +359,21 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
assert hd(result["first"]["orderedItems"])["type"] == "Member"
end
test "it returns no members for a private group", %{conn: conn} do
test "it returns no members when not a member of the group", %{conn: conn} do
actor = insert(:actor)
actor2 = insert(:actor)
assert {:ok, %Actor{} = group} =
Actors.create_group(%{creator_actor_id: actor.id, preferred_username: "my_group"})
Actors.create_group(%{
creator_actor_id: actor.id,
preferred_username: "my_group",
local: true
})
result =
conn
|> get(Actor.build_url(actor.preferred_username, :members))
|> assign(:actor, actor2)
|> get(Actor.build_url(group.preferred_username, :members))
|> json_response(200)
assert result["first"]["orderedItems"] == []
@@ -379,7 +386,7 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
Actors.create_group(%{
creator_actor_id: actor.id,
preferred_username: "my_group",
visibility: :public
local: true
})
Enum.each(1..15, fn _ ->
@@ -389,6 +396,7 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
result =
conn
|> assign(:actor, actor)
|> get(Actor.build_url(group.preferred_username, :members))
|> json_response(200)
@@ -398,6 +406,7 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
result =
conn
|> assign(:actor, actor)
|> get(Actor.build_url(group.preferred_username, :members, page: 2))
|> json_response(200)
@@ -411,7 +420,8 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
assert {:ok, %Actor{} = group} =
Actors.create_group(%{
creator_actor_id: actor_group_admin.id,
preferred_username: "my_group"
preferred_username: "my_group",
local: true
})
insert(:member, actor: actor_applicant, parent: group, role: :member)

View File

@@ -91,24 +91,26 @@ defmodule Mobilizon.Web.FeedControllerTest do
describe "/@:preferred_username/feed/ics" do
test "it returns an iCalendar representation of the actor's public events with an actor publicly visible",
%{conn: conn} do
actor = insert(:actor, visibility: :public)
actor = insert(:actor)
group = insert(:group, visibility: :public)
tag1 = insert(:tag, title: "iCalendar", slug: "icalendar")
tag2 = insert(:tag, title: "Apple", slug: "apple")
event1 = insert(:event, organizer_actor: actor, tags: [tag1])
event2 = insert(:event, organizer_actor: actor, tags: [tag1, tag2])
event1 = insert(:event, organizer_actor: actor, attributed_to: group, tags: [tag1])
event2 = insert(:event, organizer_actor: actor, attributed_to: group, tags: [tag1, tag2])
conn =
conn
|> get(
Endpoint
|> Routes.feed_url(:actor, actor.preferred_username, "ics")
|> Routes.feed_url(:actor, group.preferred_username, "ics")
|> URI.decode()
)
assert response(conn, 200) =~ "BEGIN:VCALENDAR"
assert res = response(conn, 200)
assert res =~ "BEGIN:VCALENDAR"
assert response_content_type(conn, :calendar) =~ "charset=utf-8"
[entry1, entry2] = entries = ExIcal.parse(conn.resp_body)
[entry1, entry2] = entries = ExIcal.parse(res)
Enum.each(entries, fn entry ->
assert entry.summary in [event1.title, event2.title]
@@ -120,7 +122,7 @@ defmodule Mobilizon.Web.FeedControllerTest do
test "it returns a 404 page for the actor's public events iCal feed with an actor not publicly visible",
%{conn: conn} do
actor = insert(:actor, visibility: :private)
actor = insert(:group, visibility: :private)
tag1 = insert(:tag, title: "iCalendar", slug: "icalendar")
tag2 = insert(:tag, title: "Apple", slug: "apple")
insert(:event, organizer_actor: actor, tags: [tag1])