From: Christopher Date: Fri, 17 Apr 2020 18:37:04 +0000 (-0500) Subject: initial commit X-Git-Url: http://git.entropealabs.com/?a=commitdiff_plain;h=726d3c32fe3b68c0041d3ecd5ede6298f26bcb25;p=wampex_test_suite.git initial commit --- 726d3c32fe3b68c0041d3ecd5ede6298f26bcb25 diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c877a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# 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 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..50864e1 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# 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). + diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..61dbdf6 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,4 @@ +use Mix.Config + +config :logger, + level: :info diff --git a/lib/wampex_test_suite.ex b/lib/wampex_test_suite.ex new file mode 100644 index 0000000..6b86963 --- /dev/null +++ b/lib/wampex_test_suite.ex @@ -0,0 +1,18 @@ +defmodule WampexTestSuite do + @moduledoc """ + Documentation for WampexTestSuite. + """ + + @doc """ + Hello world. + + ## Examples + + iex> WampexTestSuite.hello() + :world + + """ + def hello do + :world + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..30cad0c --- /dev/null +++ b/mix.exs @@ -0,0 +1,65 @@ +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 diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..e30591c --- /dev/null +++ b/mix.lock @@ -0,0 +1,37 @@ +%{ + "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"}, +} diff --git a/test/cluster_kv_test.exs b/test/cluster_kv_test.exs new file mode 100644 index 0000000..3335fba --- /dev/null +++ b/test/cluster_kv_test.exs @@ -0,0 +1,74 @@ +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 diff --git a/test/support/authentication.ex b/test/support/authentication.ex new file mode 100644 index 0000000..26c906f --- /dev/null +++ b/test/support/authentication.ex @@ -0,0 +1,72 @@ +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 diff --git a/test/support/authorization.ex b/test/support/authorization.ex new file mode 100644 index 0000000..f0a55d7 --- /dev/null +++ b/test/support/authorization.ex @@ -0,0 +1,7 @@ +defmodule Wampex.Router.AuthorizationImpl do + @moduledoc false + @behaviour Wampex.Router.Authorization + + @impl true + def authorized?(_, _, _), do: true +end diff --git a/test/support/test_callee.ex b/test/support/test_callee.ex new file mode 100644 index 0000000..babdba2 --- /dev/null +++ b/test/support/test_callee.ex @@ -0,0 +1,61 @@ +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 diff --git a/test/support/test_subscriber.ex b/test/support/test_subscriber.ex new file mode 100644 index 0000000..bde5dfa --- /dev/null +++ b/test/support/test_subscriber.ex @@ -0,0 +1,39 @@ +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 diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/wampex_router_test.exs b/test/wampex_router_test.exs new file mode 100644 index 0000000..ebfda61 --- /dev/null +++ b/test/wampex_router_test.exs @@ -0,0 +1,179 @@ +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 diff --git a/test/wampex_test.exs b/test/wampex_test.exs new file mode 100644 index 0000000..88d5c6c --- /dev/null +++ b/test/wampex_test.exs @@ -0,0 +1,231 @@ +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 diff --git a/test/wampex_test_suite_test.exs b/test/wampex_test_suite_test.exs new file mode 100644 index 0000000..7b8f8bf --- /dev/null +++ b/test/wampex_test_suite_test.exs @@ -0,0 +1,8 @@ +defmodule WampexTestSuiteTest do + use ExUnit.Case + doctest WampexTestSuite + + test "greets the world" do + assert WampexTestSuite.hello() == :world + end +end