summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/polyjuice/client.ex184
-rw-r--r--lib/polyjuice/client/endpoint.ex2
-rw-r--r--lib/polyjuice/client/endpoint/post_login.ex3
-rw-r--r--lib/polyjuice/client/endpoint/post_logout.ex3
-rw-r--r--lib/polyjuice/client/endpoint/post_rooms_receipt.ex3
-rw-r--r--lib/polyjuice/client/sync.ex33
-rw-r--r--lib/polyjuice/client/user.ex102
-rw-r--r--test/polyjuice/client/media_test.exs15
-rw-r--r--test/polyjuice/client/sync_test.exs19
-rw-r--r--test/polyjuice/client/user_test.exs105
-rw-r--r--test/polyjuice/client_test.exs189
11 files changed, 370 insertions, 288 deletions
diff --git a/lib/polyjuice/client.ex b/lib/polyjuice/client.ex
index 0d3bc75..84e44ca 100644
--- a/lib/polyjuice/client.ex
+++ b/lib/polyjuice/client.ex
@@ -16,13 +16,16 @@ defmodule Polyjuice.Client do
@moduledoc """
Matrix client functions.
+ To create a client, use `start/2`, and to destroy it, use
+ `stop/1`.
+
The struct in this module, or any struct that implements the
`Polyjuice.Client.API` protocol, can be used to connect to a Matrix server
using the functions from submodules.
- - `Polyjuice.Client.User`: user-related functions
- `Polyjuice.Client.Filter`: build filters for use with sync
- `Polyjuice.Client.Room`: interact with rooms, such as sending messages
+ - `Polyjuice.Client.Media`: use the media repository
- `Polyjuice.Client.MsgBuilder`: build message contents
To sync with the homeserver, start a process using the child spec returned by
@@ -31,35 +34,82 @@ defmodule Polyjuice.Client do
"""
require Logger
- @typedoc """
- Matrix client data.
-
- - `base_url` (string): Required. The base URL for the homeserver.
- - `access_token` (string): Required to call endpoints that require an
- authenticated user. The user's access token.
- - `user_id` (string): Required for some endpoints and for sync. The user's
- Matrix ID.
- - `storage` (`Polyjuice.Client.Storage`): Required for some endpoints and for
- sync. Storage for the client.
-
- """
+ @typedoc false
+ # Matrix client data.
+ # - `base_url` (string): Required. The base URL for the homeserver.
+ # - `pid` (string): PID for the agent that contains the state.
+ # - `user_id` (string): Required for some endpoints and for sync. The user's
+ # Matrix ID.
+ # - `storage` (`Polyjuice.Client.Storage`): Required for some endpoints and for
+ # sync. Storage for the client.
+ # - `test` (boolean): if the client is used for a unit test (converts POST
+ # requests to PUT, to make mod_esi happy)
@type t :: %__MODULE__{
base_url: String.t(),
- access_token: String.t(),
- user_id: String.t(),
+ pid: pid,
storage: Polyjuice.Client.Storage.t(),
test: boolean
}
- @enforce_keys [:base_url]
+ @enforce_keys [:base_url, :pid]
defstruct [
:base_url,
- :access_token,
- :user_id,
+ :pid,
:storage,
test: false
]
+ @doc """
+ Start a client.
+ """
+ @spec start(base_url :: String.t(), opts :: Keyword.t()) :: t()
+ def start(base_url, opts \\ []) when is_binary(base_url) do
+ {:ok, pid} =
+ Agent.start(fn ->
+ %{
+ access_token: Keyword.get(opts, :access_token),
+ user_id: Keyword.get(opts, :user_id),
+ device_id: Keyword.get(opts, :device_id)
+ }
+ end)
+
+ %__MODULE__{
+ base_url:
+ if(String.ends_with?(base_url, "/"), do: base_url, else: base_url <> "/")
+ |> URI.parse(),
+ pid: pid,
+ storage: Keyword.get(opts, :storage),
+ test: Keyword.get(opts, :test, false)
+ }
+ end
+
+ @doc """
+ Start a client, linking the client process to the calling process.
+ """
+ @spec start_link(base_url :: String.t(), opts :: Keyword.t()) :: t()
+ def start_link(base_url, opts \\ []) when is_binary(base_url) do
+ {:ok, pid} =
+ Agent.start_link(fn ->
+ %{
+ access_token: Keyword.get(opts, :access_token),
+ user_id: Keyword.get(opts, :user_id)
+ }
+ end)
+
+ %__MODULE__{
+ base_url:
+ if(String.ends_with?(base_url, "/"), do: base_url, else: base_url <> "/")
+ |> URI.parse(),
+ pid: pid,
+ storage: Keyword.get(opts, :storage),
+ test: Keyword.get(opts, :test, false)
+ }
+ end
+
+ def stop(%__MODULE__{pid: pid}) do
+ Process.exit(pid, :normal)
+ end
+
@doc "The r0 client URL prefix"
def prefix_r0, do: "_matrix/client/r0"
@doc "The unstable client URL prefix"
@@ -118,7 +168,7 @@ defmodule Polyjuice.Client do
end
defimpl Polyjuice.Client.API do
- def call(%{base_url: base_url, access_token: access_token, test: test}, endpoint) do
+ def call(%{base_url: base_url, pid: pid, test: test}, endpoint) do
%Polyjuice.Client.Endpoint.HttpSpec{
method: method,
headers: headers,
@@ -130,6 +180,13 @@ defmodule Polyjuice.Client do
Logger.debug("calling #{method} #{url}")
+ access_token =
+ if auth_required do
+ Agent.get(pid, fn %{access_token: access_token} -> access_token end)
+ else
+ nil
+ end
+
if auth_required and access_token == nil do
{:error, :auth_required}
else
@@ -193,8 +250,9 @@ defmodule Polyjuice.Client do
@doc """
Synchronize messages from the server.
- Normally, you should use `Polyjuice.Client.Sync` rather than calling this
- function, but this function may be used where more control is needed.
+ Normally, you should create a sync process using
+ `Polyjuice.Client.API.sync_child_spec/3` rather than calling this function, but
+ this function may be used where more control is needed.
`opts` is a keyword list of options:
@@ -222,4 +280,88 @@ defmodule Polyjuice.Client do
}
)
end
+
+ @doc """
+ Log in with a password.
+
+ `identifier` may be a single string (in which case it represents a username
+ -- either just the localpart or the full MXID), a tuple of the form
+ `{:email, "email@address"}`, a tuple of the form `{:phone, "country_code",
+ "phone_number"}`, or a map that is passed directly to the login endpoint.
+
+ `opts` is a keyword list of options:
+
+ - `device_id:` (string) the device ID to use
+ - `initial_device_display_name:` (string) the display name to use for the device
+ """
+ @spec log_in_with_password(
+ client :: Polyjuice.Client.t(),
+ identifier :: String.t() | tuple() | map(),
+ password :: String.t(),
+ opts :: list()
+ ) :: {:ok, map()} | any
+ def log_in_with_password(client, identifier, password, opts \\ [])
+ when (is_binary(identifier) or is_tuple(identifier) or is_map(identifier)) and
+ is_binary(password) and is_list(opts) do
+ id =
+ case identifier do
+ x when is_binary(x) ->
+ %{
+ "type" => "m.id.user",
+ "user" => identifier
+ }
+
+ {:email, address} ->
+ %{
+ "type" => "m.id.thirdparty",
+ "medium" => "email",
+ "address" => address
+ }
+
+ {:phone, country, phone} ->
+ %{
+ "type" => "m.id.phone",
+ "country" => country,
+ "phone" => phone
+ }
+
+ x when is_map(x) ->
+ identifier
+ end
+
+ ret =
+ {:ok, %{"access_token" => access_token, "user_id" => user_id, "device_id" => device_id}} =
+ Polyjuice.Client.API.call(
+ client,
+ %Polyjuice.Client.Endpoint.PostLogin{
+ type: "m.login.password",
+ identifier: id,
+ password: password,
+ device_id: Keyword.get(opts, :device_id),
+ initial_device_display_name: Keyword.get(opts, :initial_device_display_name)
+ }
+ )
+
+ Agent.cast(client.pid, fn state ->
+ %{state | access_token: access_token, user_id: user_id, device_id: device_id}
+ end)
+
+ ret
+ end
+
+ @doc """
+ Log out an existing session.
+ """
+ @spec log_out(client :: Polyjuice.Client.t()) :: {:ok} | any
+ def log_out(client) do
+ {:ok} =
+ Polyjuice.Client.API.call(
+ client,
+ %Polyjuice.Client.Endpoint.PostLogout{}
+ )
+
+ Agent.cast(client.pid, fn state -> %{state | access_token: nil} end)
+
+ {:ok}
+ end
end
diff --git a/lib/polyjuice/client/endpoint.ex b/lib/polyjuice/client/endpoint.ex
index b344743..bc7589b 100644
--- a/lib/polyjuice/client/endpoint.ex
+++ b/lib/polyjuice/client/endpoint.ex
@@ -56,7 +56,7 @@ defmodule Polyjuice.Client.Endpoint do
"""
@spec http_spec(
endpoint_args :: __MODULE__.t(),
- base_url :: String.t()
+ base_url :: URI.t()
) :: Polyjuice.Client.Endpoint.HttpSpec.t()
def http_spec(endpoint_args, base_url)
diff --git a/lib/polyjuice/client/endpoint/post_login.ex b/lib/polyjuice/client/endpoint/post_login.ex
index 0414cfc..808708a 100644
--- a/lib/polyjuice/client/endpoint/post_login.ex
+++ b/lib/polyjuice/client/endpoint/post_login.ex
@@ -50,8 +50,7 @@ defmodule Polyjuice.Client.Endpoint.PostLogin do
initial_device_display_name: initial_device_display_name
},
base_url
- )
- when is_binary(base_url) do
+ ) do
body =
[
[{"type", type}, {"identifier", identifier}],
diff --git a/lib/polyjuice/client/endpoint/post_logout.ex b/lib/polyjuice/client/endpoint/post_logout.ex
index d0e4e1a..f16889c 100644
--- a/lib/polyjuice/client/endpoint/post_logout.ex
+++ b/lib/polyjuice/client/endpoint/post_logout.ex
@@ -27,8 +27,7 @@ defmodule Polyjuice.Client.Endpoint.PostLogout do
def http_spec(
%Polyjuice.Client.Endpoint.PostLogout{},
base_url
- )
- when is_binary(base_url) do
+ ) do
%Polyjuice.Client.Endpoint.HttpSpec{
method: :post,
headers: [
diff --git a/lib/polyjuice/client/endpoint/post_rooms_receipt.ex b/lib/polyjuice/client/endpoint/post_rooms_receipt.ex
index 5b92803..9b83891 100644
--- a/lib/polyjuice/client/endpoint/post_rooms_receipt.ex
+++ b/lib/polyjuice/client/endpoint/post_rooms_receipt.ex
@@ -41,8 +41,7 @@ defmodule Polyjuice.Client.Endpoint.PostRoomsReceipt do
receipt_type: receipt_type
},
base_url
- )
- when is_binary(base_url) do
+ ) do
e = &URI.encode_www_form/1
%Polyjuice.Client.Endpoint.HttpSpec{
diff --git a/lib/polyjuice/client/sync.ex b/lib/polyjuice/client/sync.ex
index c898a6e..df1e13b 100644
--- a/lib/polyjuice/client/sync.ex
+++ b/lib/polyjuice/client/sync.ex
@@ -27,14 +27,13 @@ defmodule Polyjuice.Client.Sync do
Task.start_link(__MODULE__, :sync, [client, listener, opts])
end
- @enforce_keys [:send, :access_token, :homeserver_url, :uri, :user_id, :storage]
+ @enforce_keys [:send, :pid, :homeserver_url, :uri, :storage]
defstruct [
:send,
:conn_ref,
- :access_token,
+ :pid,
:homeserver_url,
:uri,
- :user_id,
:storage,
:since,
query_params: "",
@@ -51,19 +50,15 @@ defmodule Polyjuice.Client.Sync do
@doc false
def sync(
%Polyjuice.Client{
- access_token: access_token,
+ pid: pid,
base_url: homeserver_url,
- user_id: user_id,
storage: storage,
test: test
},
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 <> "/"
+ 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
@@ -103,9 +98,8 @@ defmodule Polyjuice.Client.Sync do
connect(%__MODULE__{
send: if(is_function(listener), do: listener, else: &send(listener, &1)),
- access_token: access_token,
+ pid: pid,
homeserver_url: homeserver_url,
- user_id: user_id,
uri: uri,
query_params: query_params,
storage: storage,
@@ -151,16 +145,21 @@ defmodule Polyjuice.Client.Sync do
e = &URI.encode_www_form/1
+ {access_token, user_id} =
+ Agent.get(state.pid, fn %{access_token: access_token, user_id: user_id} ->
+ {access_token, user_id}
+ end)
+
path =
URI.merge(
state.homeserver_url,
- "#{Polyjuice.Client.prefix_r0()}/user/#{e.(state.user_id)}/filter"
+ "#{Polyjuice.Client.prefix_r0()}/user/#{e.(user_id)}/filter"
).path
headers = [
{"Accept", "application/json"},
{"Content-Type", "application/json"},
- {"Authorization", "Bearer #{state.access_token}"}
+ {"Authorization", "Bearer #{access_token}"}
]
case :hackney.send_request(
@@ -224,9 +223,11 @@ defmodule Polyjuice.Client.Sync do
defp do_sync(state) do
if state.backoff, do: :timer.sleep(state.backoff * 1000)
+ access_token = Agent.get(state.pid, fn %{access_token: access_token} -> access_token end)
+
headers = [
{"Accept", "application/json"},
- {"Authorization", "Bearer #{state.access_token}"}
+ {"Authorization", "Bearer #{access_token}"}
]
path =
@@ -372,10 +373,12 @@ defmodule Polyjuice.Client.Sync do
end
)
+ user_id = Agent.get(state.pid, fn %{user_id: user_id} -> user_id end)
+
inviter =
invite_state
|> Map.get("m.room.member", %{})
- |> Map.get(state.user_id, %{})
+ |> Map.get(user_id, %{})
|> Map.get("sender")
if inviter do
diff --git a/lib/polyjuice/client/user.ex b/lib/polyjuice/client/user.ex
deleted file mode 100644
index dd0f921..0000000
--- a/lib/polyjuice/client/user.ex
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright 2019 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.User do
- @moduledoc """
- User-related functions.
- """
-
- @doc """
- Log in with a password.
-
- `client_or_url` may be either something that implements `Polyjuice.Client.API`
- or a homeserver base URL.
-
- `identifier` may be a single string (in which case it represents a username
- -- either just the localpart or the full MXID), a tuple of the form
- `{:email, "email@address"}`, a tuple of the form `{:phone, "country_code",
- "phone_number"}`, or a map that is passed directly to the login endpoint.
-
- `opts` is a keyword list of options:
-
- - `device_id:` (string) the device ID to use
- - `initial_device_display_name:` (string) the display name to use for the device
- """
- @spec log_in_with_password(
- client_or_url :: Polyjuice.Client.API.t() | String.t(),
- identifier :: String.t() | tuple() | map(),
- password :: String.t(),
- opts :: list()
- ) :: {:ok, map()} | any
- def log_in_with_password(client_or_url, identifier, password, opts \\ [])
- when (is_binary(identifier) or is_tuple(identifier) or is_map(identifier)) and
- is_binary(password) and is_list(opts) do
- client_api =
- if is_binary(client_or_url) do
- %Polyjuice.Client{
- base_url: client_or_url
- }
- else
- client_or_url
- end
-
- id =
- case identifier do
- x when is_binary(x) ->
- %{
- "type" => "m.id.user",
- "user" => identifier
- }
-
- {:email, address} ->
- %{
- "type" => "m.id.thirdparty",
- "medium" => "email",
- "address" => address
- }
-
- {:phone, country, phone} ->
- %{
- "type" => "m.id.phone",
- "country" => country,
- "phone" => phone
- }
-
- x when is_map(x) ->
- identifier
- end
-
- Polyjuice.Client.API.call(
- client_api,
- %Polyjuice.Client.Endpoint.PostLogin{
- type: "m.login.password",
- identifier: id,
- password: password,
- device_id: Keyword.get(opts, :device_id),
- initial_device_display_name: Keyword.get(opts, :initial_device_display_name)
- }
- )
- end
-
- @doc """
- Log out an existing session.
- """
- @spec log_out(client_api :: Polyjuice.Client.API.t()) :: {:ok} | any
- def log_out(client_api) do
- Polyjuice.Client.API.call(
- client_api,
- %Polyjuice.Client.Endpoint.PostLogout{}
- )
- end
-end
diff --git a/test/polyjuice/client/media_test.exs b/test/polyjuice/client/media_test.exs
index c9d8df9..b4899ca 100644
--- a/test/polyjuice/client/media_test.exs
+++ b/test/polyjuice/client/media_test.exs
@@ -86,12 +86,13 @@ defmodule Polyjuice.Client.MediaTest do
port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port)
- client = %Polyjuice.Client{
- base_url: "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.MediaTest.Httpd/",
- access_token: "an_access_token",
- user_id: "@alice:example.org",
- test: true
- }
+ client =
+ Polyjuice.Client.start(
+ "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.MediaTest.Httpd",
+ access_token: "an_access_token",
+ user_id: "@alice:example.org",
+ test: true
+ )
{:ok, url} = Polyjuice.Client.Media.upload(client, {:file, "mix.exs"})
@@ -104,6 +105,8 @@ defmodule Polyjuice.Client.MediaTest do
assert content_type == "text/plain"
assert Enum.join(body) == "foo"
+ Polyjuice.Client.stop(client)
+
:inets.stop(:httpd, httpd_pid)
after
File.rm_rf(tmpdir)
diff --git a/test/polyjuice/client/sync_test.exs b/test/polyjuice/client/sync_test.exs
index b1f2752..c1b4f64 100644
--- a/test/polyjuice/client/sync_test.exs
+++ b/test/polyjuice/client/sync_test.exs
@@ -17,7 +17,7 @@ defmodule Polyjuice.Client.SyncTest do
defmodule Httpd do
def _matrix(session_id, env, input) do
- # FIXME: check authorization header
+ assert Keyword.get(env, :http_authorization) == 'Bearer an_access_token'
# FIXME: check method
[path | _] =
Keyword.get(env, :path_info)
@@ -213,13 +213,14 @@ defmodule Polyjuice.Client.SyncTest do
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,
- test: true
- }
+ client =
+ Polyjuice.Client.start(
+ "http://127.0.0.1:#{port}/Elixir.Polyjuice.Client.SyncTest.Httpd",
+ access_token: "an_access_token",
+ user_id: "@alice:example.org",
+ storage: storage,
+ test: true
+ )
{:ok, sync_pid} =
Polyjuice.Client.API.sync_child_spec(client, self(), filter: %{})
@@ -307,6 +308,8 @@ defmodule Polyjuice.Client.SyncTest do
Process.unlink(sync_pid)
Process.exit(sync_pid, :kill)
+ Polyjuice.Client.stop(client)
+
:inets.stop(:httpd, httpd_pid)
after
Polyjuice.Client.Storage.close(storage)
diff --git a/test/polyjuice/client/user_test.exs b/test/polyjuice/client/user_test.exs
deleted file mode 100644
index 06b95bd..0000000
--- a/test/polyjuice/client/user_test.exs
+++ /dev/null
@@ -1,105 +0,0 @@
-# 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.UserTest do
- use ExUnit.Case
-
- test "log in" do
- with client = %DummyClient{
- response: {
- %Polyjuice.Client.Endpoint.PostLogin{
- type: "m.login.password",
- identifier: %{
- "type" => "m.id.user",
- "user" => "user"
- },
- password: "password"
- },
- {:ok, %{}}
- }
- } do
- {:ok, %{}} = Polyjuice.Client.User.log_in_with_password(client, "user", "password")
- end
-
- with client = %DummyClient{
- response: {
- %Polyjuice.Client.Endpoint.PostLogin{
- type: "m.login.password",
- identifier: %{
- "type" => "m.id.thirdparty",
- "medium" => "email",
- "address" => "user@example.com"
- },
- password: "password",
- device_id: "device_id",
- initial_device_display_name: "Device name"
- },
- {:ok, %{}}
- }
- } do
- {:ok, %{}} =
- Polyjuice.Client.User.log_in_with_password(
- client,
- {:email, "user@example.com"},
- "password",
- device_id: "device_id",
- initial_device_display_name: "Device name"
- )
- end
-
- with client = %DummyClient{
- response: {
- %Polyjuice.Client.Endpoint.PostLogin{
- type: "m.login.password",
- identifier: %{
- "type" => "m.id.phone",
- "country" => "CA",
- "phone" => "1234567890"
- },
- password: "password"
- },
- {:ok, %{}}
- }
- } do
- {:ok, %{}} =
- Polyjuice.Client.User.log_in_with_password(
- client,
- {:phone, "CA", "1234567890"},
- "password"
- )
-
- {:ok, %{}} =
- Polyjuice.Client.User.log_in_with_password(
- client,
- %{
- "type" => "m.id.phone",
- "country" => "CA",
- "phone" => "1234567890"
- },
- "password"
- )
- end
- end
-
- test "log out" do
- with client = %DummyClient{
- response: {
- %Polyjuice.Client.Endpoint.PostLogout{},
- {:ok}
- }
- } do
- {:ok} = Polyjuice.Client.User.log_out(client)
- end
- end
-end
diff --git a/test/polyjuice/client_test.exs b/test/polyjuice/client_test.exs
index 8b5b415..0c880c1 100644
--- a/test/polyjuice/client_test.exs
+++ b/test/polyjuice/client_test.exs
@@ -15,9 +15,67 @@
defmodule Polyjuice.ClientTest do
use ExUnit.Case
+ defmodule Httpd do
+ # for testing basic calls
+ def foo(session_id, _env, {_, input}) do
+ if input == 'foobar' do
+ :mod_esi.deliver(
+ session_id,
+ 'Content-Type: application/json\r\n\r\n{"foo":"bar"}'
+ )
+ else
+ :mod_esi.deliver(
+ session_id,
+ 'Status: 400 Bad Request\r\nContent-Type: application/json\r\n\r\n{"errcode":"M_UNKNOWN","error":"Wrong contents"}'
+ )
+ end
+ end
+
+ def _matrix(session_id, env, input) do
+ [path | _] =
+ Keyword.get(env, :path_info)
+ |> to_string()
+ |> String.split("?", parts: 2)
+
+ case path do
+ "client/r0/login" ->
+ handle_login(session_id, env, input)
+
+ "client/r0/logout" ->
+ handle_logout(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_login(session_id, _env, {_, input}) do
+ identifier_type =
+ to_string(input) |> Jason.decode!() |> Map.get("identifier") |> Map.get("type")
+
+ :mod_esi.deliver(
+ session_id,
+ 'Content-Type: application/json\r\n\r\n{"user_id":"@alice:example.org","access_token":"#{
+ identifier_type
+ }_login","device_id":"foo"}'
+ )
+ end
+
+ defp handle_logout(session_id, _env, _input) do
+ :mod_esi.deliver(
+ session_id,
+ 'Content-Type: application/json\r\n\r\n{}'
+ )
+ end
+ end
+
test "transaction_id is unique" do
client = %Polyjuice.Client{
- base_url: "http://localhost:8008"
+ base_url: "http://localhost:8008",
+ pid: nil
}
# the best that we can do is test that two calls to transaction_id return
@@ -28,7 +86,8 @@ defmodule Polyjuice.ClientTest do
test "sync child spec" do
client = %Polyjuice.Client{
- base_url: "http://localhost:8008"
+ base_url: "http://localhost:8008",
+ pid: nil
}
%{id: Polyjuice.Client.Sync, restart: :permanent, start: start} =
@@ -71,22 +130,6 @@ defmodule Polyjuice.ClientTest do
end
end
- defmodule Httpd do
- def foo(session_id, _env, {_, input}) do
- if input == 'foobar' do
- :mod_esi.deliver(
- session_id,
- 'Content-Type: application/json\r\n\r\n{"foo":"bar"}'
- )
- else
- :mod_esi.deliver(
- session_id,
- 'Status: 400 Bad Request\r\nContent-Type: application/json\r\n\r\n{"errcode":"M_UNKNOWN","error":"Wrong contents"}'
- )
- end
- end
- end
-
test "call" do
{:ok, tmpdir} = TestUtil.mktmpdir("sync-")
@@ -109,12 +152,13 @@ defmodule Polyjuice.ClientTest do
port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port)
- client = %Polyjuice.Client{
- base_url: "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd/",
- access_token: "an_access_token",
- user_id: "@alice:example.org",
- test: true
- }
+ client =
+ Polyjuice.Client.start(
+ "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd/",
+ access_token: "an_access_token",
+ user_id: "@alice:example.org",
+ test: true
+ )
# binary body
assert Polyjuice.Client.API.call(
@@ -144,6 +188,103 @@ defmodule Polyjuice.ClientTest do
}
) == {:ok, %{"foo" => "bar"}}
+ Polyjuice.Client.stop(client)
+
+ :inets.stop(:httpd, httpd_pid)
+ after
+ File.rm_rf(tmpdir)
+ end
+ end
+
+ test "login" do
+ {:ok, tmpdir} = TestUtil.mktmpdir("sync-")
+
+ 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.ClientTest.Httpd]}
+ )
+
+ port = :httpd.info(httpd_pid) |> Keyword.fetch!(:port)
+
+ client =
+ Polyjuice.Client.start(
+ "http://127.0.0.1:#{port}/Elixir.Polyjuice.ClientTest.Httpd/",
+ access_token: nil,
+ test: true
+ )
+
+ Polyjuice.Client.log_in_with_password(client, "@alice:example.org", "password")
+
+ assert Agent.get(client.pid, fn %{
+ access_token: access_token,
+ user_id: user_id,
+ device_id: device_id
+ } ->
+ {access_token, user_id, device_id}
+ end) == {"m.id.user_login", "@alice:example.org", "foo"}
+
+ Polyjuice.Client.log_out(client)
+
+ assert Agent.get(client.pid, fn %{access_token: access_token} -> access_token end) == nil
+
+ Polyjuice.Client.log_in_with_password(
+ client,
+ {:email, "user@example.com"},
+ "password"
+ )
+
+ assert Agent.get(client.pid, fn %{
+ access_token: access_token,
+ user_id: user_id,
+ device_id: device_id
+ } ->
+ {access_token, user_id, device_id}
+ end) == {"m.id.thirdparty_login", "@alice:example.org", "foo"}
+
+ Polyjuice.Client.log_in_with_password(
+ client,
+ {:phone, "CA", "1234567890"},
+ "password"
+ )
+
+ assert Agent.get(client.pid, fn %{
+ access_token: access_token,
+ user_id: user_id,
+ device_id: device_id
+ } ->
+ {access_token, user_id, device_id}
+ end) == {"m.id.phone_login", "@alice:example.org", "foo"}
+
+ Polyjuice.Client.log_in_with_password(
+ client,
+ %{
+ "type" => "ca.uhoreg.foo"
+ },
+ "password"
+ )
+
+ assert Agent.get(client.pid, fn %{
+ access_token: access_token,
+ user_id: user_id,
+ device_id: device_id
+ } ->
+ {access_token, user_id, device_id}
+ end) == {"ca.uhoreg.foo_login", "@alice:example.org", "foo"}
+
+ Polyjuice.Client.stop(client)
+
:inets.stop(:httpd, httpd_pid)
after
File.rm_rf(tmpdir)