--- /dev/null
+# Used by "mix format"
+[
+ inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]
--- /dev/null
+# The directory Mix will write compiled artifacts to.
+/_build/
+
+# If you run "mix test --cover", coverage assets end up here.
+/cover/
+
+# The directory Mix downloads your dependencies sources to.
+/deps/
+
+# Where third-party dependencies like ExDoc output generated docs.
+/doc/
+
+# Ignore .fetch files in case you like to edit your project deps locally.
+/.fetch
+
+# If the VM crashes, it generates a dump, let's ignore it too.
+erl_crash.dump
+
+# Also ignore archive artifacts (built via "mix archive.build").
+*.ez
+
+# Ignore package tarball (built via "mix hex.build").
+wampex_test_suite-*.tar
+
--- /dev/null
+# WampexTestSuite
+
+**TODO: Add description**
+
+## Installation
+
+If [available in Hex](https://hex.pm/docs/publish), the package can be installed
+by adding `wampex_test_suite` to your list of dependencies in `mix.exs`:
+
+```elixir
+def deps do
+ [
+ {:wampex_test_suite, "~> 0.1.0"}
+ ]
+end
+```
+
+Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
+and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
+be found at [https://hexdocs.pm/wampex_test_suite](https://hexdocs.pm/wampex_test_suite).
+
--- /dev/null
+use Mix.Config
+
+config :logger,
+ level: :info
--- /dev/null
+defmodule WampexTestSuite do
+ @moduledoc """
+ Documentation for WampexTestSuite.
+ """
+
+ @doc """
+ Hello world.
+
+ ## Examples
+
+ iex> WampexTestSuite.hello()
+ :world
+
+ """
+ def hello do
+ :world
+ end
+end
--- /dev/null
+defmodule WampexTestSuite.MixProject do
+ use Mix.Project
+
+ def project do
+ [
+ app: :wampex_test_suite,
+ version: "0.1.0",
+ elixir: "~> 1.9",
+ elixirc_paths: elixirc_paths(),
+ start_permanent: Mix.env() == :prod,
+ aliases: aliases(),
+ deps: deps(),
+ preferred_cli_env: [
+ coveralls: :test,
+ "coveralls.detail": :test,
+ "coveralls.post": :test,
+ "coveralls.html": :test,
+ all_tests: :test
+ ],
+ test_coverage: [tool: ExCoveralls]
+ ]
+ end
+
+ def application do
+ [
+ extra_applications: [:logger]
+ ]
+ end
+
+ defp elixirc_paths, do: ["lib", "test/support"]
+
+ def deps do
+ [
+ {:excoveralls, "~> 0.12.2", runtime: false}
+ ] ++ deps(Mix.env())
+ end
+
+ defp deps(:dev) do
+ [
+ {:cluster_kv, path: "../cluster_kv", override: true},
+ {:wampex, path: "../wampex", override: true},
+ {:wampex_client, path: "../wampex_client"},
+ {:wampex_router, path: "../wampex_router"}
+ ]
+ end
+
+ defp deps(:test) do
+ [
+ {:cluster_kv,
+ git: "https://gitlab.com/entropealabs/cluster_kv.git", tag: "dev", override: true},
+ {:wampex, git: "https://gitlab.com/entropealabs/wampex.git", tag: "dev", override: true},
+ {:wampex_client, git: "https://gitlab.com/entropealabs/wampex_client.git", tag: "dev"},
+ {:wampex_router, git: "https://gitlab.com/entropealabs/wampex_router.git", tag: "dev"}
+ ]
+ end
+
+ defp aliases do
+ [
+ all_tests: [
+ "compile --force --warnings-as-errors",
+ "coveralls"
+ ]
+ ]
+ end
+end
--- /dev/null
+%{
+ "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
+ "cluster_kv": {:git, "https://gitlab.com/entropealabs/cluster_kv.git", "bcb05440a2b39e921b7f0e333b2fbb98e35520ce", [tag: "dev"]},
+ "conv_case": {:hex, :conv_case, "0.2.2", "5a98b74ab8f7ddbad670e5c7bb39ff280e60699aa3b25c7062ceccf48137433c", [:mix], [], "hexpm", "561c550ab6d55b2a4d4c14449e58c9957798613eb26ea182e14a962965377bca"},
+ "cors_plug": {:hex, :cors_plug, "2.0.2", "2b46083af45e4bc79632bd951550509395935d3e7973275b2b743bd63cc942ce", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f0d0e13f71c51fd4ef8b2c7e051388e4dfb267522a83a22392c856de7e46465f"},
+ "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
+ "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
+ "elixpath": {:hex, :elixpath, "0.1.0", "f860e931db7bda6856dc68145694ca429643cc068ef30d7ff6b4096d4357963e", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "30ce06079b41f1f5216ea2cd11605cfe4c82239628555cb3fde9f10055a6eb67"},
+ "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"},
+ "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"},
+ "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
+ "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},
+ "json_xema": {:hex, :json_xema, "0.4.0", "377446cd5c0e2cbba52b9d7ab67c05579e6d4a788335220215a8870eac821996", [:mix], [{:conv_case, "~> 0.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:xema, "~> 0.11", [hex: :xema, repo: "hexpm", optional: false]}], "hexpm", "452724a5b2751cd69191edd3fd3da0c2194c164ebd49efd85f9abb64d6621b53"},
+ "libcluster": {:hex, :libcluster, "3.2.1", "b2cd5b447cde25d5897749bee6f7aaeb6c96ac379481024e9b6ba495dabeb97d", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "89f225612d135edce9def56f43bf18d575d88ac4680e3f6161283f2e55cadca4"},
+ "libring": {:hex, :libring, "1.5.0", "44313eb6862f5c9168594a061e9d5f556a9819da7c6444706a9e2da533396d70", [:mix], [], "hexpm", "04e843d4fdcff49a62d8e03778d17c6cb2a03fe2d14020d3825a1761b55bd6cc"},
+ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
+ "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
+ "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
+ "msgpax": {:hex, :msgpax, "2.2.4", "7b3790ef684089076b63c0f08c2f4b079c6311daeb006b69e4ed2bf67518291e", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b351b6d992d79624a8430a99d21a41b36b1b90edf84326a294e9f4a2de11f089"},
+ "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
+ "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
+ "pbkdf2": {:hex, :pbkdf2, "2.0.0", "11c23279fded5c0027ab3996cfae77805521d7ef4babde2bd7ec04a9086cf499", [:rebar3], [], "hexpm", "1e793ce6fdb0576613115714deae9dfc1d1537eaba74f07efb36de139774488d"},
+ "plug": {:hex, :plug, "1.10.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "422a9727e667be1bf5ab1de03be6fa0ad67b775b2d84ed908f3264415ef29d4a"},
+ "plug_cowboy": {:hex, :plug_cowboy, "2.1.3", "38999a3e85e39f0e6bdfdf820761abac61edde1632cfebbacc445cdcb6ae1333", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "056f41f814dbb38ea44613e0f613b3b2b2f2c6afce64126e252837669eba84db"},
+ "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
+ "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
+ "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
+ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
+ "states_language": {:hex, :states_language, "0.2.9", "5c8c6b7d37ea6952c4345960d22facb2159ae6f8b06514c0fdc320b8077e1277", [:mix], [{:elixpath, "~> 0.1.0", [hex: :elixpath, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:json_xema, "~> 0.4.0", [hex: :json_xema, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:xema, "~> 0.11.0", [hex: :xema, repo: "hexpm", optional: false]}], "hexpm", "2da0c7621e1574a2687790d321632a121eea6de4d86e77543f86a678cda65c40"},
+ "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
+ "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
+ "wampex": {:git, "https://gitlab.com/entropealabs/wampex.git", "ae6f2cfcc23dc9af94fa2c375b52bad6c1d0f75d", [tag: "dev"]},
+ "wampex_client": {:git, "https://gitlab.com/entropealabs/wampex_client.git", "b01f5f0e8db0309d7db010269493c284d97b6645", [tag: "dev"]},
+ "wampex_router": {:git, "https://gitlab.com/entropealabs/wampex_router.git", "73c8e72448fc726c994bcf9576a269b1809339a4", [tag: "dev"]},
+ "websockex": {:hex, :websockex, "0.4.2", "9a3b7dc25655517ecd3f8ff7109a77fce94956096b942836cdcfbc7c86603ecc", [:mix], [], "hexpm", "803cd76e91544b56f0e655e36790be797fa6436db9224f7c303db9b9df2a3df4"},
+ "xema": {:hex, :xema, "0.11.0", "7b5118418633cffc27092110d02d4faeea938149dd3f6c64299e41e747067e80", [:mix], [{:conv_case, "~> 0.2.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:decimal, "~> 1.7", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "51491c9a953d65069d4b30aa2f70bc45ff99fd1bc3345bc72ce4e644d01ea14e"},
+}
--- /dev/null
+defmodule ClusterKVTest do
+ use ExUnit.Case
+ doctest ClusterKV
+
+ @topologies [
+ test: [
+ strategy: Cluster.Strategy.Epmd,
+ config: [hosts: []]
+ ]
+ ]
+
+ @keyspace "com.myrealm"
+
+ setup_all do
+ ClusterKV.start_link(name: TestCluster, topologies: @topologies, replicas: 1, quorum: 1)
+ [db: TestCluster]
+ end
+
+ test "test", %{db: db} do
+ me = {self(), self()}
+ ClusterKV.put(db, @keyspace, "test1:test", :hello)
+ ClusterKV.put(db, @keyspace, "test1:test", :cruel)
+ ClusterKV.put(db, @keyspace, "test1:test", :world)
+
+ assert "test:1" = ClusterKV.get_wildcard_key(".test.light..status", ".", ":", "")
+
+ ClusterKV.put_wildcard(
+ db,
+ @keyspace,
+ "com.test..temp",
+ {1_003_039_494, me},
+ ".",
+ ":",
+ ""
+ )
+
+ assert {"test1:test", [:world, :cruel, :hello]} = ClusterKV.get(db, @keyspace, "test1:test")
+
+ assert [{["com", "test", "", "temp"], {1_003_039_494, me}}] =
+ ClusterKV.wildcard(
+ db,
+ @keyspace,
+ "com.test.data.temp",
+ ".",
+ ":",
+ ""
+ )
+
+ ClusterKV.update(db, @keyspace, "test1:test", :cruel, fn {id, values}, value ->
+ {id, List.delete(values, value)}
+ end)
+
+ assert {"test1:test", [:world, :hello]} = ClusterKV.get(db, @keyspace, "test1:test")
+ end
+
+ test "update_if", %{db: db} do
+ assert :updated =
+ ClusterKV.update_if(db, @keyspace, "test.profile", :initial_value, &update_if/2)
+
+ assert :noop =
+ ClusterKV.update_if(db, @keyspace, "test.profile", :initial_value, &update_if/2)
+
+ assert :updated = ClusterKV.update_if(db, @keyspace, "test.profile", :new_value, &update_if/2)
+ assert :noop = ClusterKV.update_if(db, @keyspace, "test.profile", :new_value, &update_if/2)
+ assert :noop = ClusterKV.update_if(db, @keyspace, "test.profile", :new_value, &update_if/2)
+ end
+
+ defp update_if({key, [old]}, val) do
+ case old == val do
+ true -> :noop
+ false -> {:update, {key, [val]}}
+ end
+ end
+end
--- /dev/null
+defmodule Wampex.Router.AuthenticationImpl do
+ @moduledoc false
+ @behaviour Wampex.Router.Authentication
+ require Logger
+
+ alias Wampex.Crypto
+ alias Wampex.Serializers.JSON
+
+ @wampcra "wampcra"
+ @auth_provider "userdb"
+ @auth_role "user"
+ @auth_password "bnASR5qF9y8k/sHF6S+NneCOhvVI0zFkvoKQpc2F+hA="
+ @salt "test"
+ @keylen 8
+ @iterations 1
+
+ @impl true
+ def authenticate?(methods) do
+ can_auth(methods)
+ end
+
+ @impl true
+ def method, do: @wampcra
+
+ @impl true
+ def challenge(_realm, authid, session_id) do
+ now = DateTime.to_iso8601(DateTime.utc_now())
+
+ %{
+ challenge:
+ JSON.serialize!(%{
+ nonce: Crypto.random_string(@keylen),
+ authprovider: @auth_provider,
+ authid: authid,
+ timestamp: now,
+ authrole: @auth_role,
+ authmethod: @wampcra,
+ session: session_id
+ }),
+ salt: @salt,
+ keylen: @keylen,
+ iterations: @iterations
+ }
+ end
+
+ @impl true
+ def parse_challenge(challenge) do
+ ch = JSON.deserialize!(challenge.challenge)
+
+ {get_in(ch, ["authid"]), get_in(ch, ["authrole"]), get_in(ch, ["authmethod"]),
+ get_in(ch, ["authprovider"])}
+ end
+
+ @impl true
+ def authenticate(signature, realm, authid, %{
+ challenge: challenge
+ }) do
+ authed =
+ authid
+ |> get_secret(realm)
+ |> Crypto.hash_challenge(challenge)
+ |> :pbkdf2.compare_secure(signature)
+
+ {authed, %{}}
+ end
+
+ defp get_secret(_authid, _uri), do: Crypto.pbkdf2(@auth_password, @salt, @iterations, @keylen)
+
+ defp can_auth([]), do: false
+ defp can_auth([@wampcra | _]), do: true
+ defp can_auth([_ | t]), do: can_auth(t)
+end
--- /dev/null
+defmodule Wampex.Router.AuthorizationImpl do
+ @moduledoc false
+ @behaviour Wampex.Router.Authorization
+
+ @impl true
+ def authorized?(_, _, _), do: true
+end
--- /dev/null
+defmodule TestCallee do
+ @moduledoc false
+ use GenServer
+ require Logger
+ alias Wampex.Client
+ alias Wampex.Roles.{Callee, Dealer}
+ alias Wampex.Roles.Peer.Error
+ alias Callee.{Register, Yield}
+ alias Dealer.Invocation
+
+ def start_link(test, name, device) do
+ GenServer.start_link(__MODULE__, {test, name, device})
+ end
+
+ def init({test, name, device}) do
+ Client.add(name, self())
+ {:ok, {test, name, device}}
+ end
+
+ def handle_info({:connected, _}, {test, name, device} = state) do
+ {:ok, _reg} = Client.register(name, %Register{procedure: "com.actuator.#{device}.light"})
+ {:ok, reg} = Client.register(name, %Register{procedure: "return.error"})
+ send(test, {:registered, reg})
+ {:noreply, state}
+ end
+
+ def handle_info(
+ %Invocation{
+ request_id: id,
+ details: %{"procedure" => "return.error"},
+ arg_list: al,
+ arg_kw: akw
+ },
+ {_test, name, _device} = state
+ ) do
+ Client.error(
+ name,
+ %Error{request_id: id, error: "this.is.an.error", arg_list: al, arg_kw: akw}
+ )
+
+ {:noreply, state}
+ end
+
+ def handle_info(
+ %Invocation{request_id: id, details: _dets, arg_kw: arg_kw} = invocation,
+ {test, name, _device} = state
+ ) do
+ send(test, invocation)
+
+ Client.yield(
+ name,
+ %Yield{
+ request_id: id,
+ arg_list: [:ok],
+ arg_kw: %{color: Map.get(arg_kw, "color")}
+ }
+ )
+
+ {:noreply, state}
+ end
+end
--- /dev/null
+defmodule TestSubscriber do
+ @moduledoc false
+ use GenServer
+ require Logger
+ alias Wampex.Client
+ alias Wampex.Roles.Subscriber.Subscribe
+
+ def start_link(test, name, topic) do
+ GenServer.start_link(__MODULE__, {test, name, topic})
+ end
+
+ def init({test, name, topic}) do
+ Client.add(name, self())
+ {:ok, {test, name, topic}}
+ end
+
+ def handle_info({:connected, _client}, {test, name, topic}) do
+ {:ok, _sub1} = Client.subscribe(name, %Subscribe{topic: topic})
+ {:ok, _sub2} = Client.subscribe(name, %Subscribe{topic: "com.data.test"})
+ {:ok, _sub3} = Client.subscribe(name, %Subscribe{topic: "com.data"})
+
+ {:ok, _sub4} =
+ Client.subscribe(name, %Subscribe{topic: "com...temp", options: %{"match" => "wildcard"}})
+
+ {:ok, sub5} =
+ Client.subscribe(name, %Subscribe{
+ topic: "com..test.temp",
+ options: %{"match" => "wildcard"}
+ })
+
+ send(test, {:subscribed, sub5})
+ {:noreply, {test, name, topic}}
+ end
+
+ def handle_info(event, {test, _name, _topic} = state) do
+ send(test, event)
+ {:noreply, state}
+ end
+end
--- /dev/null
+ExUnit.start()
--- /dev/null
+defmodule WampexRouterTest do
+ use ExUnit.Case, async: true
+
+ alias Wampex.Client
+ alias Wampex.Client.{Authentication, Realm, Session}
+ alias Wampex.Roles.Broker.Event
+ alias Wampex.Roles.{Callee, Caller, Peer, Publisher, Subscriber}
+ alias Wampex.Roles.Caller.Call
+ alias Wampex.Roles.Dealer.{Invocation, Result}
+ alias Wampex.Roles.Peer.{Error, Hello}
+ alias Wampex.Roles.Publisher.Publish
+ alias Wampex.Router
+ alias Wampex.Router.AuthenticationImpl
+ alias Wampex.Router.AuthorizationImpl
+ require Logger
+
+ @topologies [
+ test: [
+ strategy: Cluster.Strategy.Epmd,
+ config: [hosts: []]
+ ]
+ ]
+
+ @url "ws://localhost:5999/ws"
+ @authid "admin"
+ @auth_password "bnASR5qF9y8k/sHF6S+NneCOhvVI0zFkvoKQpc2F+hA="
+ @realm_uri "admin"
+ @auth %Authentication{authid: @authid, authmethods: ["wampcra"], secret: @auth_password}
+ @realm %Realm{name: @realm_uri, authentication: @auth}
+ @roles [Caller, Callee, Publisher, Subscriber]
+ @device "as987d9a8sd79a87ds"
+ @session %Session{url: @url, realm: @realm, roles: @roles}
+ @s Supervisor.child_spec(
+ {Router,
+ name: TestRouter,
+ port: 5999,
+ topologies: @topologies,
+ replicas: 1,
+ quorum: 1,
+ authentication_module: AuthenticationImpl,
+ authorization_module: AuthorizationImpl},
+ id: Test
+ )
+
+ setup do
+ start_supervised(@s)
+ :ok
+ end
+
+ test "role not supported" do
+ callee_name = TestCalleeAbortRegistration
+ Client.start_link(name: callee_name, session: @session, reconnect: false)
+ TestCallee.start_link(self(), callee_name, @device)
+
+ name = TestCallerFail
+ [_ | t] = @session.roles
+ sess = %Session{@session | roles: t}
+ {:ok, _pid} = Client.start_link(name: name, session: sess, reconnect: false)
+
+ Client.call(name, %Call{procedure: "com.actuator.#{@device}.light"})
+ catch
+ :exit, {:end, er} -> Logger.error(inspect(er))
+ :exit, {:shutdown, er} -> Logger.error(inspect(er))
+ end
+
+ test "callee registration" do
+ name = TestCalleeRegistration
+ Client.start_link(name: name, session: @session, reconnect: false)
+ TestCallee.start_link(self(), name, @device)
+ assert_receive {:registered, id}
+ end
+
+ @tag :abort
+ test "abort" do
+ callee_name = TestAbort
+ {:ok, _pid} = Client.start_link(name: callee_name, session: @session, reconnect: false)
+ Client.cast(callee_name, Peer.hello(%Hello{realm: "test", roles: [Callee]}))
+ catch
+ :exit, er -> assert {:normal, _} = er
+ end
+
+ @tag :client
+ test "caller receives error when calling unknown procedure" do
+ caller_name = TestExistCaller
+ Client.start_link(name: caller_name, session: @session, reconnect: false)
+
+ assert %Error{error: "wamp.error.no_registration"} =
+ Client.call(
+ caller_name,
+ %Call{
+ procedure: "this.should.not.exist",
+ arg_list: [1],
+ arg_kw: %{color: "#FFFFFF"}
+ }
+ )
+ end
+
+ @tag :client
+ test "caller receives error when callee returns an error" do
+ callee_name = TestErrorCalleeRespond
+ Client.start_link(name: callee_name, session: @session, reconnect: false)
+ TestCallee.start_link(self(), callee_name, @device)
+ assert_receive {_, _}
+ caller_name = TestErrorCaller
+ Client.start_link(name: caller_name, session: @session, reconnect: false)
+
+ assert %Error{error: "this.is.an.error", arg_list: [1], arg_kw: %{"color" => "#FFFFFF"}} =
+ Client.call(
+ caller_name,
+ %Call{
+ procedure: "return.error",
+ arg_list: [1],
+ arg_kw: %{color: "#FFFFFF"}
+ }
+ )
+ end
+
+ @tag :client
+ test "callee is invoked and responds and caller gets result" do
+ callee_name = TestCalleeRespond
+ Client.start_link(name: callee_name, session: @session, reconnect: false)
+ TestCallee.start_link(self(), callee_name, @device)
+ assert_receive {_, _}
+ caller_name = TestCaller
+ Client.start_link(name: caller_name, session: @session, reconnect: false)
+
+ assert %Result{arg_list: ["ok"], arg_kw: %{"color" => "#FFFFFF"}} =
+ Client.call(
+ caller_name,
+ %Call{
+ procedure: "com.actuator.#{@device}.light",
+ arg_list: [1],
+ arg_kw: %{color: "#FFFFFF"}
+ }
+ )
+
+ assert_receive %Invocation{}
+ end
+
+ @tag :client
+ test "subscriber registration" do
+ name = TestSubscriberRegister
+ Client.start_link(name: name, session: @session, reconnect: false)
+ TestSubscriber.start_link(self(), name, "com.data.temp")
+ assert_receive {:subscribed, id}
+ end
+
+ @tag :client
+ test "subscriber receives events from publisher" do
+ name = TestSubscriberEvents
+ Client.start_link(name: name, session: @session, reconnect: false)
+ TestSubscriber.start_link(self(), name, "com.data.test.temp")
+ assert_receive {:subscribed, id}
+
+ Client.start_link(name: TestPublisher, session: @session, reconnect: false)
+
+ Client.publish(
+ TestPublisher,
+ %Publish{
+ topic: "no.subscribers",
+ arg_list: [12.5, 45.6, 87.5],
+ arg_kw: %{loc: "60645"}
+ }
+ )
+
+ Client.publish(
+ TestPublisher,
+ %Publish{
+ topic: "com.data.test.temp",
+ arg_list: [12.5, 45.6, 87.5],
+ arg_kw: %{loc: "60645"}
+ }
+ )
+
+ assert_receive %Event{details: %{"topic" => "com.data.test.temp"}}, 2000
+ assert_receive %Event{details: %{"topic" => "com.data.test.temp"}}, 2000
+ assert_receive %Event{details: %{"topic" => "com.data.test.temp"}}, 2000
+ end
+end
--- /dev/null
+defmodule WampexTest do
+ use ExUnit.Case, async: true
+ doctest Wampex
+
+ alias Wampex.Roles.{Broker, Callee, Caller, Dealer, Peer, Publisher, Subscriber}
+ alias Wampex.Serializers.{JSON, MessagePack}
+ alias Broker.{Event, Published, Subscribed, Unsubscribed}
+ alias Callee.{Register, Unregister, Yield}
+ alias Caller.Call
+ alias Dealer.{Invocation, Registered, Result, Unregistered}
+ alias Peer.{Authenticate, Error, Goodbye, Hello}
+ alias Publisher.Publish
+ alias Subscriber.{Subscribe, Unsubscribe}
+ require Logger
+
+ test "Callee.add" do
+ assert %{callee: %{}} = Callee.add(%{})
+ end
+
+ test "Callee.invocation_error" do
+ assert [8, 68, 1234, %{}, "error", [], %{}] =
+ Callee.invocation_error(%Error{request_id: 1234, error: "error"})
+ end
+
+ test "Callee.register" do
+ assert [64, %{}, "test"] = Callee.register(%Register{procedure: "test"})
+ end
+
+ test "Callee.unregister" do
+ assert [66, 123_456] = Callee.unregister(%Unregister{registration_id: 123_456})
+ end
+
+ test "Callee.yield" do
+ assert [70, 1234, %{}, [], %{}] = Callee.yield(%Yield{request_id: 1234})
+ assert [70, 1234, %{}, ["1"], %{}] = Callee.yield(%Yield{request_id: 1234, arg_list: ["1"]})
+
+ assert [70, 1234, %{test: 1}, [2], %{}] =
+ Callee.yield(%Yield{
+ request_id: 1234,
+ arg_list: [2],
+ arg_kw: %{},
+ options: %{test: 1}
+ })
+ end
+
+ test "Caller.add" do
+ assert %{caller: %{}} = Caller.add(%{})
+ end
+
+ @tag :client
+ test "Caller.call" do
+ assert [48, %{}, "test", [], %{}] = Caller.call(%Call{procedure: "test"})
+ assert [48, %{}, "test", [1], %{}] = Caller.call(%Call{procedure: "test", arg_list: [1]})
+
+ assert [48, %{ok: 1}, "test", [1], %{test: 1}] =
+ Caller.call(%Call{
+ procedure: "test",
+ arg_list: [1],
+ arg_kw: %{test: 1},
+ options: %{ok: 1}
+ })
+ end
+
+ test "Peer.add" do
+ assert %{} = Caller.add(%{})
+ end
+
+ test "Peer.hello" do
+ assert [1, "test", %{agent: "WAMPex", roles: %{callee: %{}}}] =
+ Peer.hello(%Hello{realm: "test", roles: [Callee]})
+ end
+
+ test "Peer.authenticate" do
+ assert [5, "a-signa-ture", %{}] ==
+ Peer.authenticate(%Authenticate{signature: "a-signa-ture", extra: %{}})
+ end
+
+ test "Peer.gooodbye" do
+ assert [6, %{}, "test"] = Peer.goodbye(%Goodbye{reason: "test"})
+ end
+
+ test "Publisher.add" do
+ assert %{publisher: %{}} = Publisher.add(%{})
+ end
+
+ @tag :client
+ test "Publisher.publish" do
+ assert [16, %{}, "test", [], %{}] = Publisher.publish(%Publish{topic: "test"})
+ assert [16, %{}, "test", [1], %{}] = Publisher.publish(%Publish{topic: "test", arg_list: [1]})
+
+ assert [16, %{test: 2}, "test", [1], %{test: 1}] =
+ Publisher.publish(%Publish{
+ topic: "test",
+ arg_list: [1],
+ arg_kw: %{test: 1},
+ options: %{test: 2}
+ })
+ end
+
+ test "Subscriber.add" do
+ assert %{subscriber: %{}} = Subscriber.add(%{})
+ end
+
+ test "Subscriber.subscribe" do
+ assert [32, %{test: 1}, "test"] =
+ Subscriber.subscribe(%Subscribe{topic: "test", options: %{test: 1}})
+ end
+
+ test "Subscriber.unsubscribe" do
+ assert [34, 1234] = Subscriber.unsubscribe(%Unsubscribe{subscription_id: 1234})
+ end
+
+ test "Dealer.add" do
+ assert %{dealer: %{}} = Dealer.add(%{})
+ end
+
+ test "Dealer.registered" do
+ assert [65, 123_456, 765_432] =
+ Dealer.registered(%Registered{request_id: 123_456, registration_id: 765_432})
+ end
+
+ test "Dealer.unregistered" do
+ assert [67, 123_456] = Dealer.unregistered(%Unregistered{request_id: 123_456})
+ end
+
+ test "Dealer.call_error" do
+ assert [8, 48, 123_456, %{}, "error", [], %{}] =
+ Dealer.call_error(%Error{request_id: 123_456, details: %{}, error: "error"})
+ end
+
+ test "Dealer.register_error" do
+ assert [8, 64, 123_456, %{}, "error"] =
+ Dealer.register_error(%Error{request_id: 123_456, details: %{}, error: "error"})
+ end
+
+ test "Dealer.result" do
+ assert [50, 1234, %{}, [], %{}] = Dealer.result(%Result{request_id: 1234})
+ assert [50, 1234, %{}, ["1"], %{}] = Dealer.result(%Result{request_id: 1234, arg_list: ["1"]})
+
+ assert [50, 1234, %{test: 1}, [2], %{}] =
+ Dealer.result(%Result{
+ request_id: 1234,
+ arg_list: [2],
+ arg_kw: %{},
+ details: %{test: 1}
+ })
+ end
+
+ test "Dealer.invocation" do
+ assert [68, 1234, 1234, %{}, [], %{}] =
+ Dealer.invocation(%Invocation{request_id: 1234, registration_id: 1234})
+
+ assert [68, 1234, 1234, %{}, ["1"], %{}] =
+ Dealer.invocation(%Invocation{
+ request_id: 1234,
+ registration_id: 1234,
+ arg_list: ["1"]
+ })
+
+ assert [68, 1234, 1234, %{test: 1}, [2], %{test: 2}] =
+ Dealer.invocation(%Invocation{
+ request_id: 1234,
+ registration_id: 1234,
+ arg_list: [2],
+ arg_kw: %{test: 2},
+ details: %{test: 1}
+ })
+ end
+
+ test "Broker.add" do
+ assert %{broker: %{}} = Broker.add(%{})
+ end
+
+ test "Broker.event" do
+ assert [36, 123_456, 765_432, %{}, [], %{}] =
+ Broker.event(%Event{subscription_id: 123_456, publication_id: 765_432})
+
+ assert [36, 123_456, 765_432, %{}, [2], %{}] =
+ Broker.event(%Event{subscription_id: 123_456, publication_id: 765_432, arg_list: [2]})
+
+ assert [36, 123_456, 765_432, %{}, [2], %{test: 1}] =
+ Broker.event(%Event{
+ subscription_id: 123_456,
+ publication_id: 765_432,
+ arg_list: [2],
+ arg_kw: %{test: 1}
+ })
+ end
+
+ test "Broker.subscribed" do
+ assert [33, 123_456, 123_456] =
+ Broker.subscribed(%Subscribed{request_id: 123_456, subscription_id: 123_456})
+ end
+
+ test "Broker.unsubscribed" do
+ assert [35, 123_456] = Broker.unsubscribed(%Unsubscribed{request_id: 123_456})
+ end
+
+ test "Broker.subscribe_error" do
+ assert [8, 32, 123_456, %{}, "error"] =
+ Broker.subscribe_error(%Error{request_id: 123_456, details: %{}, error: "error"})
+ end
+
+ test "Broker.published" do
+ assert [17, 123_456, 654_321] =
+ Broker.published(%Published{request_id: 123_456, publication_id: 654_321})
+ end
+
+ test "MessagePack Serializer" do
+ assert :binary = MessagePack.data_type()
+
+ val = [
+ 5,
+ "this is a string/binary",
+ "123456789",
+ [%{"test" => "ok"}, 45, 54, 56, 77],
+ %{"key" => "true", "value" => 34}
+ ]
+
+ s = MessagePack.serialize!(val)
+ assert ^val = MessagePack.deserialize!(s)
+ end
+
+ test "JSON Serializer" do
+ assert :text = JSON.data_type()
+ assert "[5,\"123456789\",{}]" = JSON.serialize!([5, "123456789", %{}])
+ assert {:ok, "[5,\"123456789\",{}]"} = JSON.serialize([5, "123456789", %{}])
+ assert [5, "123456789", %{}] = JSON.deserialize!("[5,\"123456789\",{}]")
+ assert {:ok, [5, "123456789", %{}]} = JSON.deserialize("[5,\"123456789\",{}]")
+ end
+end
--- /dev/null
+defmodule WampexTestSuiteTest do
+ use ExUnit.Case
+ doctest WampexTestSuite
+
+ test "greets the world" do
+ assert WampexTestSuite.hello() == :world
+ end
+end