├── .gitignore ├── LICENSE ├── README.md ├── concourse.yml ├── config └── config.exs ├── lib ├── janus.ex └── janus │ ├── plugin.ex │ ├── session.ex │ └── util.ex ├── mix.exs ├── mix.lock └── test ├── janus_test.exs ├── support └── bypass.ex └── test_helper.exs /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # If the VM crashes, it generates a dump, let's ignore it too. 14 | erl_crash.dump 15 | 16 | # Also ignore archive artifacts (built via "mix archive.build"). 17 | *.ez 18 | Tmuxfile 19 | credentials.yml 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Nolan Darilek 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Janus 2 | 3 | **TODO: Add description** 4 | 5 | ## Installation 6 | 7 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed as: 8 | 9 | 1. Add `janus` to your list of dependencies in `mix.exs`: 10 | 11 | ```elixir 12 | def deps do 13 | [{:janus, "~> 0.1.0"}] 14 | end 15 | ``` 16 | 17 | 2. Ensure `janus` is started before your application: 18 | 19 | ```elixir 20 | def application do 21 | [applications: [:janus]] 22 | end 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /concourse.yml: -------------------------------------------------------------------------------- 1 | resources: 2 | 3 | - name: repo 4 | type: git 5 | source: 6 | uri: https://github.com/ndarilek/elixir-janus 7 | branch: master 8 | 9 | jobs: 10 | 11 | - name: test 12 | public: true 13 | plan: 14 | 15 | - get: repo 16 | trigger: true 17 | 18 | - task: run tests 19 | config: 20 | platform: linux 21 | image_resource: 22 | type: docker-image 23 | source: {repository: elixir} 24 | inputs: 25 | - name: repo 26 | run: 27 | path: sh 28 | args: 29 | - -exc 30 | - | 31 | cd repo 32 | mix local.hex --force 33 | mix deps.get 34 | mix test 35 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure for your application as: 12 | # 13 | # config :janus, key: :value 14 | # 15 | # And access this configuration in your application as: 16 | # 17 | # Application.get_env(:janus, :key) 18 | # 19 | # Or configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /lib/janus.ex: -------------------------------------------------------------------------------- 1 | import Janus.Util 2 | 3 | defmodule Janus do 4 | 5 | @moduledoc """ 6 | This library is a client for the [Janus REST API](https://janus.conf.meetecho.com/docs/rest.html). 7 | """ 8 | 9 | @doc """ 10 | Retrieves details on the Janus server located at `url` 11 | """ 12 | 13 | def info(url), do: get("#{url}/info") 14 | 15 | end 16 | -------------------------------------------------------------------------------- /lib/janus/plugin.ex: -------------------------------------------------------------------------------- 1 | import Janus.Util 2 | 3 | defmodule Janus.Plugin do 4 | 5 | @moduledoc """ 6 | Send messages, trickle candidates, and detach plugins from Janus sessions. 7 | """ 8 | 9 | @enforce_keys [:id, :base_url, :event_manager] 10 | defstruct [ 11 | :id, 12 | :base_url, 13 | :event_manager 14 | ] 15 | 16 | @doc """ 17 | Send `body` as a message to the specified plugin, along with an optional JSEP payload. 18 | """ 19 | 20 | def message(pid, body, jsep \\ nil) do 21 | plugin = Agent.get(pid, &(&1)) 22 | msg = %{body: body, janus: "message"} 23 | post(plugin.base_url, maybe_add_key(msg, :jsep, jsep)) 24 | end 25 | 26 | @doc """ 27 | Hang up any Web RTC connections associated with this plugin. 28 | """ 29 | 30 | def hangup(pid) do 31 | plugin = Agent.get(pid, &(&1)) 32 | case post(plugin.base_url, %{janus: :hangup}) do 33 | {:ok, _} -> :ok 34 | v -> v 35 | end 36 | end 37 | 38 | @doc """ 39 | Trickle ICE candidates to this plugin. 40 | 41 | `candidates` is one or more of the following: 42 | * A single ICE candidate as a map. 43 | * A list of ICE candidate maps. 44 | * `nil`, meaning trickling is completed. 45 | """ 46 | 47 | def trickle(pid, candidates \\ nil) do 48 | msg = %{janus: :trickle} 49 | msg = case candidates do 50 | nil -> Map.put(msg, :candidate, %{completed: true}) 51 | v when is_list(v) -> Map.put(msg, :candidates, v) 52 | v when is_map(v) -> Map.put(msg, :candidate, v) 53 | end 54 | plugin = Agent.get(pid, &(&1)) 55 | post(plugin.base_url, msg) 56 | end 57 | 58 | @doc """ 59 | Detaches this plugin from its session. 60 | 61 | Once this plugin is detached, it is invalid and can no longer be used. 62 | """ 63 | 64 | def detach(pid) do 65 | base_url = Agent.get(pid, &(&1.base_url)) 66 | post(base_url, %{janus: :detach}) 67 | Agent.stop(pid) 68 | end 69 | 70 | @doc "See `GenEvent.add_handler/3`." 71 | def add_handler(plugin, handler, args), do: Agent.get plugin, &(GenEvent.add_handler(&1.event_manager, handler, args)) 72 | 73 | @doc "See `GenEvent.add_mon_handler/3`." 74 | def add_mon_handler(plugin, handler, args), do: Agent.get plugin, &(GenEvent.add_mon_handler(&1.event_manager, handler, args)) 75 | 76 | @doc "See `GenEvent.call/4`." 77 | def call(plugin, handler, timeout, request \\ 5000), do: Agent.get plugin, &(GenEvent.call(&1.event_manager, handler, request, timeout)) 78 | 79 | @doc "See `GenEvent.remove_handler/3`." 80 | def remove_handler(plugin, handler, args), do: Agent.get plugin, &(GenEvent.remove_handler(&1.event_manager, handler, args)) 81 | 82 | @doc "See `GenEvent.stream/2`." 83 | def stream(plugin, options \\ []), do: Agent.get plugin, &(GenEvent.stream(&1.event_manager, options)) 84 | 85 | @doc "See `GenEvent.swap_handler/5`." 86 | def swap_handler(plugin, handler1, args1, handler2, args2), do: Agent.get plugin, &(GenEvent.swap_handler(&1.event_manager, handler1, args1, handler2, args2)) 87 | 88 | @doc "See `GenEvent.swap_mon_handler/5`." 89 | def swap_mon_handler(plugin, handler1, args1, handler2, args2), do: Agent.get plugin, &(GenEvent.swap_mon_handler(&1.event_manager, handler1, args1, handler2, args2)) 90 | 91 | @doc "See `GenEvent.which_handlers/1`." 92 | def which_handlers(plugin), do: Agent.get plugin, &(GenEvent.which_handlers(&1.event_manager)) 93 | 94 | end 95 | -------------------------------------------------------------------------------- /lib/janus/session.ex: -------------------------------------------------------------------------------- 1 | require Logger 2 | 3 | import Janus.Util 4 | 5 | defmodule Janus.Session do 6 | 7 | @moduledoc """ 8 | Sessions are connections to the Janus server to which plugins are attached, and from which events are retrieved. 9 | """ 10 | 11 | @enforce_keys [:id, :base_url, :event_manager] 12 | defstruct [ 13 | :id, 14 | :base_url, 15 | :event_manager, 16 | handles: %{} 17 | ] 18 | 19 | @doc """ 20 | Creates an unstarted session with the server at `url`. 21 | 22 | Note that if `start/1` is not called within the session timeout, then the session will be invalid. 23 | Use this function if you must perform additional configuration steps before the session is started. 24 | """ 25 | 26 | def init(url) do 27 | case post(url, %{janus: :create}) do 28 | {:ok, body} -> 29 | id = body.data.id 30 | {:ok, event_manager} = GenEvent.start_link() 31 | session = %Janus.Session{ 32 | id: id, 33 | base_url: "#{url}/#{id}", 34 | event_manager: event_manager 35 | } 36 | Agent.start(fn -> session end) 37 | v -> v 38 | end 39 | end 40 | 41 | @doc "Starts an existing session previously created via `init/1`." 42 | 43 | def start(pid) when is_pid(pid), do: poll(pid) 44 | 45 | def start(url) do 46 | case init(url) do 47 | {:ok, pid} -> 48 | start(pid) 49 | {:ok, pid} 50 | v -> v 51 | end 52 | end 53 | 54 | @doc """ 55 | Attaches the plugin identified by `id` to the specified session. 56 | 57 | ## Examples 58 | 59 | {:ok, session} = Janus.Session.start("http://localhost:8088/janus") 60 | session |> Janus.Session.attach_plugin("janus.plugin.echotest") 61 | """ 62 | 63 | def attach_plugin(pid, id) do 64 | base_url = Agent.get(pid, &(&1.base_url)) 65 | v = case post(base_url, %{janus: :attach, plugin: id}) do 66 | {:ok, body} -> 67 | id = body.data.id 68 | {:ok, event_manager} = GenEvent.start_link() 69 | plugin = %Janus.Plugin{ 70 | id: id, 71 | base_url: "#{base_url}/#{id}", 72 | event_manager: event_manager 73 | } 74 | {:ok, plugin_pid} = Agent.start(fn -> plugin end) 75 | Agent.update pid, fn(session) -> 76 | new_handles = Map.put(session.handles, id, plugin_pid) 77 | %{ session | handles: new_handles} 78 | end 79 | {:ok, plugin_pid} 80 | v -> v 81 | end 82 | v 83 | end 84 | 85 | @doc "Destroys the session, detaching all plugins, and freeing all allocated resources." 86 | 87 | def destroy(pid) do 88 | base_url = Agent.get(pid, &(&1.base_url)) 89 | plugin_pids = Agent.get(pid, &(&1.handles)) |> Map.values() 90 | Enum.each (plugin_pids), &(Janus.Plugin.detach(&1)) 91 | Agent.stop(pid) 92 | post(base_url, %{janus: :destroy}) 93 | end 94 | 95 | @doc "See `GenEvent.add_handler/3`." 96 | def add_handler(session, handler, args), do: Agent.get session, &(GenEvent.add_handler(&1.event_manager, handler, args)) 97 | 98 | @doc "See `GenEvent.add_mon_handler/3`." 99 | def add_mon_handler(session, handler, args), do: Agent.get session, &(GenEvent.add_mon_handler(&1.event_manager, handler, args)) 100 | 101 | @doc "See `GenEvent.call/4`." 102 | def call(session, handler, timeout, request \\ 5000), do: Agent.get session, &(GenEvent.call(&1.event_manager, handler, request, timeout)) 103 | 104 | @doc "See `GenEvent.remove_handler/3`." 105 | def remove_handler(session, handler, args), do: Agent.get session, &(GenEvent.remove_handler(&1.event_manager, handler, args)) 106 | 107 | @doc "See `GenEvent.stream/2`." 108 | def stream(session, options \\ []), do: Agent.get session, &(GenEvent.stream(&1.event_manager, options)) 109 | 110 | @doc "See `GenEvent.swap_handler/5`." 111 | def swap_handler(session, handler1, args1, handler2, args2), do: Agent.get session, &(GenEvent.swap_handler(&1.event_manager, handler1, args1, handler2, args2)) 112 | 113 | @doc "See `GenEvent.swap_mon_handler/5`." 114 | def swap_mon_handler(session, handler1, args1, handler2, args2), do: Agent.get session, &(GenEvent.swap_mon_handler(&1.event_manager, handler1, args1, handler2, args2)) 115 | 116 | @doc "See `GenEvent.which_handlers/1`." 117 | def which_handlers(session), do: Agent.get session, &(GenEvent.which_handlers(&1.event_manager)) 118 | 119 | defp poll(pid) do 120 | session = Agent.get pid, &(&1) 121 | spawn fn -> 122 | case get(session.base_url) do 123 | {:ok, data} -> 124 | event_manager = session.event_manager 125 | case data do 126 | %{janus: "keepalive"} -> GenEvent.notify(event_manager, {:keepalive, pid}) 127 | %{sender: sender} -> 128 | # Refetch the session in case we've added new plugins while this poll was in flight. 129 | session = Agent.get pid, &(&1) 130 | plugin_pid = session.handles[sender] 131 | if plugin_pid do 132 | case data do 133 | %{janus: "event", plugindata: plugindata} -> 134 | jsep = data[:jsep] 135 | Agent.get plugin_pid, &(GenEvent.notify(&1.event_manager, {:event, pid, plugin_pid, plugindata.data, jsep})) 136 | %{janus: "webrtcup"} -> Agent.get plugin_pid, &(GenEvent.notify(&1.event_manager, {:webrtcup, pid, plugin_pid})) 137 | %{janus: "media", type: type, receiving: receiving} -> 138 | Agent.get plugin_pid, &(GenEvent.notify(&1.event_manager, {:media, pid, plugin_pid, type, receiving})) 139 | %{janus: "slowlink", uplink: uplink, nacks: nacks} -> 140 | Agent.get plugin_pid, &(GenEvent.notify(&1.event_manager, {:slowlink, pid, plugin_pid, uplink, nacks})) 141 | %{janus: "hangup"} -> 142 | reason = data[:reason] 143 | Agent.get plugin_pid, &(GenEvent.notify(&1.event_manager, {:hangup, pid, plugin_pid, reason})) 144 | %{janus: "detached"} -> 145 | Agent.get plugin_pid, &(GenEvent.notify(&1.event_manager, {:detached, pid, plugin_pid})) 146 | end 147 | end 148 | _ -> nil 149 | end 150 | poll(pid) 151 | {:error, reason} -> Logger.error(reason) 152 | {:error, :invalid, _} -> nil 153 | end 154 | end 155 | end 156 | 157 | end 158 | -------------------------------------------------------------------------------- /lib/janus/util.ex: -------------------------------------------------------------------------------- 1 | require Logger 2 | 3 | defmodule Janus.Util do 4 | 5 | @moduledoc false 6 | 7 | def get(url) do 8 | Logger.debug("GET #{url}") 9 | case HTTPoison.get(url, [], recv_timeout: :infinity) do 10 | {:ok, %HTTPoison.Response{body: body}} -> 11 | case Poison.decode(body, keys: :atoms) do 12 | {:ok, %{janus: "error", error: error}} -> 13 | Logger.error(error.reason) 14 | {:error, error.reason} 15 | {:error, error} -> 16 | Logger.error(error) 17 | {:error, error} 18 | {:ok, v} -> 19 | Logger.debug(inspect(v)) 20 | {:ok, v} 21 | end 22 | {:error, %HTTPoison.Error{reason: reason}} -> 23 | Logger.error(reason) 24 | {:error, reason} 25 | end 26 | end 27 | 28 | def post(url, body) do 29 | Logger.debug("POST #{url} (#{inspect(body)})") 30 | case HTTPoison.post(url, Poison.encode!(add_transaction_id(body))) do 31 | {:ok, %HTTPoison.Response{body: body}} -> 32 | case Poison.decode(body, keys: :atoms) do 33 | {:ok, %{janus: "error", error: error}} -> 34 | Logger.error(error.reason) 35 | {:error, error.reason} 36 | {:error, error} -> 37 | Logger.error(error) 38 | {:error, error} 39 | {:ok, v} -> 40 | Logger.debug(inspect(v)) 41 | {:ok, v} 42 | end 43 | {:error, %HTTPoison.Error{reason: reason}} -> 44 | Logger.error(reason) 45 | {:error, reason} 46 | end 47 | end 48 | 49 | def maybe_add_key(map, _key, nil), do: map 50 | def maybe_add_key(map, key, value), do: Map.put(map, key, value) 51 | 52 | defp transaction_id, do: :rand.uniform(1000000000) |> to_string 53 | 54 | defp add_transaction_id(body) do 55 | case Map.get(body, :transaction) do 56 | nil -> Map.merge(body, %{transaction: transaction_id()}) 57 | _ -> body 58 | end 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Janus.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :janus, 7 | version: "0.1.0", 8 | elixir: "~> 1.3", 9 | elixirc_paths: elixirc_paths(Mix.env), 10 | build_embedded: Mix.env == :prod, 11 | start_permanent: Mix.env == :prod, 12 | deps: deps(), 13 | name: "Janus", 14 | source_url: "https://github.com/ndarilek/elixir-janus", 15 | homepage_url: "https://github.com/ndarilek/elixir-janus", 16 | docs: [ 17 | main: "Janus", 18 | extras: ["README.md"] 19 | ] 20 | ] 21 | end 22 | 23 | # Configuration for the OTP application 24 | # 25 | # Type "mix help compile.app" for more information 26 | def application do 27 | [applications: [:logger, :httpoison]] 28 | end 29 | 30 | # Specifies which paths to compile per environment. 31 | defp elixirc_paths(:test), do: ["lib", "test/support"] 32 | defp elixirc_paths(_), do: ["lib"] 33 | 34 | # Dependencies can be Hex packages: 35 | # 36 | # {:mydep, "~> 0.3.0"} 37 | # 38 | # Or git/path repositories: 39 | # 40 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 41 | # 42 | # Type "mix help deps" for more examples and options 43 | defp deps do 44 | [ 45 | {:httpoison, "~> 0.10"}, 46 | {:poison, "~> 3.1"}, 47 | {:ex_doc, "~> 0.14", only: :dev}, 48 | {:bypass, "~> 0.1", only: :test} 49 | ] 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"bypass": {:hex, :bypass, "0.6.0", "fd0a8004fada4464e2ba98497755310b892a097f2fd975f4f787cf264066a335", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, 2 | "certifi": {:hex, :certifi, "1.0.0", "1c787a85b1855ba354f0b8920392c19aa1d06b0ee1362f9141279620a5be2039", [:rebar3], []}, 3 | "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, optional: false]}]}, 4 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, 5 | "earmark": {:hex, :earmark, "1.1.1", "433136b7f2e99cde88b745b3a0cfc3fbc81fe58b918a09b40fce7f00db4d8187", [:mix], []}, 6 | "ex_doc": {:hex, :ex_doc, "0.15.0", "e73333785eef3488cf9144a6e847d3d647e67d02bd6fdac500687854dd5c599f", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]}, 7 | "hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, optional: false]}, {:idna, "4.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, 8 | "httpoison": {:hex, :httpoison, "0.11.1", "d06c571274c0e77b6cc50e548db3fd7779f611fbed6681fd60a331f66c143a0b", [:mix], [{:hackney, "~> 1.7.0", [hex: :hackney, optional: false]}]}, 9 | "idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []}, 10 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, 11 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []}, 12 | "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, 13 | "plug": {:hex, :plug, "1.3.2", "8391d8ba2e2c187de069211110a882599e851f64550c556163b5130e1e2dbc1b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, 14 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}, 15 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []}, 16 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []}} 17 | -------------------------------------------------------------------------------- /test/janus_test.exs: -------------------------------------------------------------------------------- 1 | defmodule JanusTest do 2 | import Janus.Bypass 3 | use ExUnit.Case, async: true 4 | doctest Janus 5 | 6 | setup do 7 | bypass = Bypass.open 8 | {:ok, bypass: bypass} 9 | end 10 | 11 | describe "Janus.info/1" do 12 | 13 | test "returns information on the Janus server", %{bypass: bypass} do 14 | {:ok, response} = Poison.encode(%{}) 15 | Bypass.expect bypass, fn(conn) -> 16 | assert conn.request_path == "/janus/info" 17 | assert conn.method == "GET" 18 | Plug.Conn.resp(conn, 200, response) 19 | end 20 | {:ok, info} = Janus.info(endpoint_url(bypass)) 21 | assert is_map(info) 22 | end 23 | 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /test/support/bypass.ex: -------------------------------------------------------------------------------- 1 | defmodule Janus.Bypass do 2 | 3 | def endpoint_url(bypass), do: "http://localhost:#{bypass.port}/janus" 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | Application.ensure_all_started(:bypass) 3 | --------------------------------------------------------------------------------