summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHubert Chathi <hubert@uhoreg.ca>2020-07-27 23:44:20 -0400
committerHubert Chathi <hubert@uhoreg.ca>2020-07-27 23:44:20 -0400
commit36239eb82a54af9c4881eb57269c5267358db018 (patch)
tree26a62f4b8f7b66279e6a393fcaeb17ec1b0bd306
parentuse Jason instead of Poison (diff)
add test for sync, and fix some bugs
-rw-r--r--lib/polyjuice/client/sync.ex9
-rw-r--r--test/polyjuice/client/sync_test.exs313
-rw-r--r--test/support/test_util.ex40
3 files changed, 360 insertions, 2 deletions
diff --git a/lib/polyjuice/client/sync.ex b/lib/polyjuice/client/sync.ex
index 67b83fd..416c336 100644
--- a/lib/polyjuice/client/sync.ex
+++ b/lib/polyjuice/client/sync.ex
@@ -1,4 +1,4 @@
-# Copyright 2019 Hubert Chathi <hubert@uhoreg.ca>
+# Copyright 2019-2020 Hubert Chathi <hubert@uhoreg.ca>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -58,6 +58,11 @@ defmodule Polyjuice.Client.Sync do
listener,
opts
) do
+ # Make sure the URL ends with a slash, so that URI.merge doesn't clobber
+ # the last path component. (URI.merge is smart enough to drop the double
+ # "/" if it already ends with a slash.)
+ homeserver_url = homeserver_url <> "/"
+
# Figure out how to handle the filter (if any): can we pass it in straight
# to the query, or do we need to get its ID. And if we get its ID, do we
# already have it, or do we need to send it to the server?
@@ -301,7 +306,7 @@ defmodule Polyjuice.Client.Sync do
timeline = Map.get(room, "timeline", %{})
if Map.get(timeline, "limited", false) do
- with {:ok, prev_batch} <- Map.get(timeline, "prev_batch") do
+ with {:ok, prev_batch} <- Map.fetch(timeline, "prev_batch") do
state.send.({:limited, roomname, prev_batch})
end
end
diff --git a/test/polyjuice/client/sync_test.exs b/test/polyjuice/client/sync_test.exs
new file mode 100644
index 0000000..d301ccc
--- /dev/null
+++ b/test/polyjuice/client/sync_test.exs
@@ -0,0 +1,313 @@
+# Copyright 2020 Hubert Chathi <hubert@uhoreg.ca>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+defmodule Polyjuice.Client.SyncTest do
+ use ExUnit.Case
+
+ defmodule Httpd do
+ def _matrix(session_id, env, input) do
+ # FIXME: check authorization header
+ # FIXME: check method
+ [path | _] =
+ Keyword.get(env, :path_info)
+ |> to_string()
+ |> String.split("?", parts: 2)
+
+ case path do
+ "client/r0/user/%40alice%3Aexample.org/filter" ->
+ handle_filter(session_id, env, input)
+
+ "client/r0/sync" ->
+ handle_sync(session_id, env, input)
+
+ _ ->
+ :mod_esi.deliver(
+ session_id,
+ 'Status: 404 Not Found\r\nContent-Type: application/json\r\n\r\n{"errcode":"M_NOT_FOUND","error":"Not found"}'
+ )
+ end
+ end
+
+ defp handle_filter(session_id, _env, _input) do
+ :mod_esi.deliver(
+ session_id,
+ 'Content-Type: application/json\r\n\r\n{"filter_id":"1"}'
+ )
+ end
+
+ defp handle_sync(session_id, env, _input) do
+ sync =
+ Keyword.get(env, :path_info)
+ |> to_string()
+ |> String.split("?", parts: 2)
+ |> Enum.at(1)
+ |> String.split("&")
+ |> Enum.find("since=", &String.starts_with?(&1, "since="))
+ |> String.replace_prefix("since=", "")
+
+ response =
+ case sync do
+ "" ->
+ %{
+ "next_batch" => "1",
+ "presence" => %{},
+ "account_data" => %{},
+ "rooms" => %{
+ "invite" => %{
+ "!room_id" => %{
+ "invite_state" => %{
+ "events" => [
+ %{
+ "content" => %{
+ "membership" => "invite"
+ },
+ "type" => "m.room.member",
+ "state_key" => "@alice:example.org",
+ "event_id" => "$invite_event",
+ "room_id" => "!room_id",
+ "sender" => "@bob:example.org"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+
+ "1" ->
+ %{
+ "next_batch" => "2",
+ "presence" => %{},
+ "account_data" => %{},
+ "rooms" => %{
+ "join" => %{
+ "!room_id" => %{
+ "state" => %{
+ "events" => [
+ %{
+ "content" => %{
+ "membership" => "join"
+ },
+ "type" => "m.room.member",
+ "state_key" => "@alice:example.org",
+ "event_id" => "$join_event",
+ "room_id" => "!room_id",
+ "sender" => "@alice:example.org"
+ }
+ ]
+ },
+ "timeline" => %{
+ "limited" => true,
+ "prev_batch" => "p1",
+ "events" => []
+ }
+ }
+ }
+ }
+ }
+
+ "2" ->
+ %{
+ "next_batch" => "3",
+ "presence" => %{},
+ "account_data" => %{},
+ "rooms" => %{
+ "join" => %{
+ "!room_id" => %{
+ "timeline" => %{
+ "limited" => false,
+ "prev_batch" => "p2",
+ "events" => [
+ %{
+ "content" => %{
+ "msgtype" => "m.text",
+ "body" => "Hello World!"
+ },
+ "type" => "m.room.message",
+ "event_id" => "$message_event",
+ "room_id" => "!room_id",
+ "sender" => "@alice:example.org"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+
+ "3" ->
+ %{
+ "next_batch" => "4",
+ "presence" => %{},
+ "account_data" => %{},
+ "rooms" => %{
+ "leave" => %{
+ "!room_id" => %{
+ "timeline" => %{
+ "limited" => false,
+ "prev_batch" => "p2",
+ "events" => [
+ %{
+ "content" => %{
+ "membership" => "leave",
+ "reason" => "Goodbye Cruel World!"
+ },
+ "type" => "m.room.member",
+ "state_key" => "@alice:example.org",
+ "event_id" => "$leave_event",
+ "room_id" => "!room_id",
+ "sender" => "@alice:example.org"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+
+ _ ->
+ nil
+ end
+
+ if response do
+ :mod_esi.deliver(
+ session_id,
+ 'Content-Type: application/json\r\n\r\n#{Jason.encode!(response)}'
+ )
+ end
+ end
+ end
+
+ test "sync" do
+ {:ok, tmpdir} = TestUtil.mktmpdir("sync-")
+
+ storage = Polyjuice.Client.Storage.Ets.open()
+
+ try do
+ tmpdir_charlist = to_charlist(tmpdir)
+
+ :inets.start()
+
+ {:ok, httpd_pid} =
+ :inets.start(
+ :httpd,
+ port: 0,
+ server_name: 'sync.test',
+ server_root: tmpdir_charlist,
+ document_root: tmpdir_charlist,
+ bind_address: {127, 0, 0, 1},
+ modules: [:mod_esi],
+ erl_script_alias: {'', [Polyjuice.Client.SyncTest.Httpd]}
+ )
+
+ port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port)
+
+ client = %Polyjuice.Client{
+ base_url: "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.SyncTest.Httpd",
+ access_token: "an_access_token",
+ user_id: "@alice:example.org",
+ storage: storage
+ }
+
+ {:ok, sync_pid} =
+ Polyjuice.Client.API.sync_child_spec(client, self())
+ |> (fn %{start: {module, func, args}} -> apply(module, func, args) end).()
+
+ assert_receive({:connected})
+
+ assert_receive(
+ {:invite, "!room_id", "@bob:example.org",
+ %{
+ "m.room.member" => %{
+ "@alice:example.org" => %{
+ "content" => %{
+ "membership" => "invite"
+ },
+ "type" => "m.room.member",
+ "state_key" => "@alice:example.org",
+ "event_id" => "$invite_event",
+ "room_id" => "!room_id",
+ "sender" => "@bob:example.org"
+ }
+ }
+ }},
+ 1000
+ )
+
+ assert_receive({:initial_sync_completed})
+
+ assert_receive(
+ {:limited, "!room_id", "p1"},
+ 1000
+ )
+
+ assert_receive(
+ {:state, "!room_id",
+ %{
+ "content" => %{
+ "membership" => "join"
+ },
+ "type" => "m.room.member",
+ "state_key" => "@alice:example.org",
+ "event_id" => "$join_event",
+ "room_id" => "!room_id",
+ "sender" => "@alice:example.org"
+ }},
+ 1000
+ )
+
+ assert_receive(
+ {:message, "!room_id",
+ %{
+ "content" => %{
+ "msgtype" => "m.text",
+ "body" => "Hello World!"
+ },
+ "type" => "m.room.message",
+ "event_id" => "$message_event",
+ "room_id" => "!room_id",
+ "sender" => "@alice:example.org"
+ }},
+ 1000
+ )
+
+ assert_receive(
+ {:state, "!room_id",
+ %{
+ "content" => %{
+ "membership" => "leave",
+ "reason" => "Goodbye Cruel World!"
+ },
+ "type" => "m.room.member",
+ "state_key" => "@alice:example.org",
+ "event_id" => "$leave_event",
+ "room_id" => "!room_id",
+ "sender" => "@alice:example.org"
+ }},
+ 1000
+ )
+
+ assert_receive(
+ {:left, "!room_id"},
+ 1000
+ )
+
+ Process.unlink(sync_pid)
+ Process.exit(sync_pid, :kill)
+ after
+ Polyjuice.Client.Storage.close(storage)
+ File.rm_rf(tmpdir)
+ end
+ end
+end
diff --git a/test/support/test_util.ex b/test/support/test_util.ex
new file mode 100644
index 0000000..2724678
--- /dev/null
+++ b/test/support/test_util.ex
@@ -0,0 +1,40 @@
+# Copyright 2020 Hubert Chathi <hubert@uhoreg.ca>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+defmodule TestUtil do
+ def mktmpdir(prefix) do
+ Enum.find_value(
+ 1..5,
+ {:error, :efail},
+ fn _ ->
+ random =
+ Enum.map(
+ Range.new(0, 12),
+ fn _ -> Enum.random('abcdefghijklmnopqrstuvwxyz1234567890') end
+ )
+ |> to_string()
+
+ tmpdir = Path.join(System.tmp_dir!(), prefix <> random)
+
+ case File.mkdir(tmpdir) do
+ :ok -> {:ok, tmpdir}
+ # try again if the name is already taken
+ {:error, :eexist} -> nil
+ # pass through any other errors
+ {:error, _} = err -> err
+ end
+ end
+ )
+ end
+end