diff options
Diffstat (limited to 'deps/rabbitmq_cli/test/ctl')
94 files changed, 8681 insertions, 0 deletions
diff --git a/deps/rabbitmq_cli/test/ctl/add_user_command_test.exs b/deps/rabbitmq_cli/test/ctl/add_user_command_test.exs new file mode 100644 index 0000000000..ec21691da9 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/add_user_command_test.exs @@ -0,0 +1,86 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule AddUserCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.AddUserCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + on_exit(context, fn -> delete_user(context[:user]) end) + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: no positional arguments fails" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: too many positional arguments fails" do + assert @command.validate(["user", "password", "extra"], %{}) == + {:validation_failure, :too_many_args} + end + + test "validate: two arguments passes" do + assert @command.validate(["user", "password"], %{}) == :ok + end + + test "validate: one argument passes" do + assert @command.validate(["user"], %{}) == :ok + end + + @tag user: "", password: "password" + test "validate: an empty username fails", context do + assert match?({:validation_failure, {:bad_argument, _}}, @command.validate([context[:user], context[:password]], context[:opts])) + end + + # Blank passwords are currently allowed, they make sense + # e.g. when a user only authenticates using X.509 certificates. + # Credential validators can be used to require passwords of a certain length + # or following a certain pattern. This is a core server responsibility. MK. + @tag user: "some_rando", password: "" + test "validate: an empty password is allowed", context do + assert @command.validate([context[:user], context[:password]], context[:opts]) == :ok + end + + @tag user: "someone", password: "password" + test "run: request to a non-existent node returns a badrpc", context do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([context[:user], context[:password]], opts)) + end + + @tag user: "someone", password: "password" + test "run: default case completes successfully", context do + assert @command.run([context[:user], context[:password]], context[:opts]) == :ok + assert list_users() |> Enum.count(fn(record) -> record[:user] == context[:user] end) == 1 + end + + @tag user: "someone", password: "password" + test "run: adding an existing user returns an error", context do + add_user(context[:user], context[:password]) + assert @command.run([context[:user], context[:password]], context[:opts]) == {:error, {:user_already_exists, context[:user]}} + assert list_users() |> Enum.count(fn(record) -> record[:user] == context[:user] end) == 1 + end + + @tag user: "someone", password: "password" + test "banner", context do + assert @command.banner([context[:user], context[:password]], context[:opts]) + =~ ~r/Adding user \"#{context[:user]}\" \.\.\./ + end + + @tag user: "someone" + test "output: formats a user_already_exists error", context do + {:error, 70, "User \"someone\" already exists"} = + @command.output({:error, {:user_already_exists, context[:user]}}, %{}) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/add_vhost_command_test.exs b/deps/rabbitmq_cli/test/ctl/add_vhost_command_test.exs new file mode 100644 index 0000000000..f9f6362c19 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/add_vhost_command_test.exs @@ -0,0 +1,68 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule AddVhostCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.AddVhostCommand + @vhost "test" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + setup context do + on_exit(context, fn -> delete_vhost(context[:vhost]) end) + :ok + end + + test "validate: no arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: too many arguments fails validation" do + assert @command.validate(["test", "extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: one argument passes validation" do + assert @command.validate(["new-vhost"], %{}) == :ok + assert @command.validate(["new-vhost"], %{description: "Used by team A"}) == :ok + assert @command.validate(["new-vhost"], %{description: "Used by team A for QA purposes", tags: "qa,team-a"}) == :ok + end + + @tag vhost: @vhost + test "run: passing a valid vhost name to a running RabbitMQ node succeeds", context do + assert @command.run([context[:vhost]], context[:opts]) == :ok + assert list_vhosts() |> Enum.count(fn(record) -> record[:name] == context[:vhost] end) == 1 + end + + @tag vhost: "" + test "run: passing an empty string for vhost name with a running RabbitMQ node still succeeds", context do + assert @command.run([context[:vhost]], context[:opts]) == :ok + assert list_vhosts() |> Enum.count(fn(record) -> record[:name] == context[:vhost] end) == 1 + end + + test "run: attempt to use an unreachable node returns a nodedown" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["na"], opts)) + end + + test "run: adding the same host twice is idempotent", context do + add_vhost context[:vhost] + + assert @command.run([context[:vhost]], context[:opts]) == :ok + assert list_vhosts() |> Enum.count(fn(record) -> record[:name] == context[:vhost] end) == 1 + end + + @tag vhost: @vhost + test "banner", context do + assert @command.banner([context[:vhost]], context[:opts]) + =~ ~r/Adding vhost \"#{context[:vhost]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/authenticate_user_command_test.exs b/deps/rabbitmq_cli/test/ctl/authenticate_user_command_test.exs new file mode 100644 index 0000000000..506dfad367 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/authenticate_user_command_test.exs @@ -0,0 +1,81 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule AuthenticateUserCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands. AuthenticateUserCommand + @user "user1" + @password "password" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_user(@user, @password) + on_exit(context, fn -> delete_user(@user) end) + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: no positional arguments fails" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: too many positional arguments fails" do + assert @command.validate(["user", "password", "extra"], %{}) == + {:validation_failure, :too_many_args} + end + + test "validate: one argument passes" do + assert @command.validate(["user"], %{}) == :ok + end + + test "validate: two arguments passes" do + assert @command.validate(["user", "password"], %{}) == :ok + end + + @tag user: @user, password: @password + test "run: a valid username and password returns okay", context do + assert {:ok, _} = @command.run([context[:user], context[:password]], context[:opts]) + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["user", "password"], opts)) + end + + @tag user: @user, password: "treachery" + test "run: a valid username and invalid password returns refused", context do + assert {:refused, _, _, _} = @command.run([context[:user], context[:password]], context[:opts]) + end + + @tag user: "interloper", password: @password + test "run: an invalid username returns refused", context do + assert {:refused, _, _, _} = @command.run([context[:user], context[:password]], context[:opts]) + end + + @tag user: @user, password: @password + test "banner", context do + assert @command.banner([context[:user], context[:password]], context[:opts]) + =~ ~r/Authenticating user/ + assert @command.banner([context[:user], context[:password]], context[:opts]) + =~ ~r/"#{context[:user]}"/ + end + + test "output: refused error", context do + user = "example_user" + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_dataerr + assert match?({:error, ^exit_code, + "Error: failed to authenticate user \"example_user\"\n" <> + "Unable to foo"}, + @command.output({:refused, user, "Unable to ~s", ["foo"]}, context[:opts])) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/autocomplete_command_test.exs b/deps/rabbitmq_cli/test/ctl/autocomplete_command_test.exs new file mode 100644 index 0000000000..52b3c8d026 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/autocomplete_command_test.exs @@ -0,0 +1,52 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule AutocompleteCommandTest do + use ExUnit.Case, async: true + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.AutocompleteCommand + setup do + {:ok, opts: %{ + script_name: "rabbitmqctl", + node: get_rabbit_hostname() + }} + end + + test "shows up in help" do + s = @command.usage() + assert s =~ ~r/autocomplete/ + end + + test "enforces --silent" do + assert @command.merge_defaults(["list_"], %{}) == {["list_"], %{silent: true}} + end + + test "validate: providing no arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing two or more arguments fails validation" do + assert @command.validate(["list_", "extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: providing a single argument passes validation" do + assert @command.validate(["list_c"], %{}) == :ok + end + + test "run: lists completion options", context do + {:stream, completion_options} = @command.run(["list_c"], context[:opts]) + + assert Enum.member?(completion_options, "list_channels") + assert Enum.member?(completion_options, "list_connections") + assert Enum.member?(completion_options, "list_consumers") + end + + test "banner shows that the name is being set", context do + assert @command.banner(["list_"], context[:opts]) == nil + end +end diff --git a/deps/rabbitmq_cli/test/ctl/await_online_nodes_command_test.exs b/deps/rabbitmq_cli/test/ctl/await_online_nodes_command_test.exs new file mode 100644 index 0000000000..bf9eeb574d --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/await_online_nodes_command_test.exs @@ -0,0 +1,44 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule AwaitOnlineNodesCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.AwaitOnlineNodesCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 300_000}} + end + + setup context do + on_exit(context, fn -> delete_vhost(context[:vhost]) end) + :ok + end + + test "validate: wrong number of arguments results in arg count errors" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["1", "1"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: a call with node count of 1 with a running RabbitMQ node succeeds", context do + assert @command.run(["1"], context[:opts]) == :ok + end + + test "run: a call to an unreachable RabbitMQ node returns a nodedown" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["1"], opts)) + end + + test "banner", context do + assert @command.banner(["1"], context[:opts]) + =~ ~r/Will wait for at least 1 nodes to join the cluster of #{context[:opts][:node]}. Timeout: 300 seconds./ + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/await_startup_command_test.exs b/deps/rabbitmq_cli/test/ctl/await_startup_command_test.exs new file mode 100644 index 0000000000..554ec5ee77 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/await_startup_command_test.exs @@ -0,0 +1,49 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +# Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule AwaitStartupCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.AwaitStartupCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 300_000}} + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "merge_defaults: default timeout is 5 minutes" do + assert @command.merge_defaults([], %{}) == {[], %{timeout: 300_000}} + end + + test "validate: accepts no arguments", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "run: request to a fully booted node succeeds", context do + # this timeout value is in seconds + assert @command.run([], Map.merge(context[:opts], %{timeout: 5})) == :ok + end + + test "empty banner", context do + nil = @command.banner([], context[:opts]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/cancel_sync_command_test.exs b/deps/rabbitmq_cli/test/ctl/cancel_sync_command_test.exs new file mode 100644 index 0000000000..8503e6ab5f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/cancel_sync_command_test.exs @@ -0,0 +1,64 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule CancelSyncQueueCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.CancelSyncQueueCommand + + @vhost "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{ + node: get_rabbit_hostname(), + vhost: @vhost + }} + end + + test "validate: specifying no queue name is reported as an error", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate: specifying two queue names is reported as an error", context do + assert @command.validate(["q1", "q2"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: specifying three queue names is reported as an error", context do + assert @command.validate(["q1", "q2", "q3"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: specifying one queue name succeeds", context do + assert @command.validate(["q1"], context[:opts]) == :ok + end + + test "run: request to a non-existent RabbitMQ node returns a nodedown" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + assert match?({:badrpc, _}, @command.run(["q1"], opts)) + end + + test "banner", context do + s = @command.banner(["q1"], context[:opts]) + + assert s =~ ~r/Stopping synchronising queue/ + assert s =~ ~r/q1/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/change_cluster_node_type_command_test.exs b/deps/rabbitmq_cli/test/ctl/change_cluster_node_type_command_test.exs new file mode 100644 index 0000000000..8fcb7de3ae --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/change_cluster_node_type_command_test.exs @@ -0,0 +1,84 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ChangeClusterNodeTypeCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ChangeClusterNodeTypeCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{ + node: get_rabbit_hostname() + }} + end + + test "validate: node type of disc, disk, and ram pass validation", context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["foo"], context[:opts])) + + assert :ok == @command.validate(["ram"], context[:opts]) + assert :ok == @command.validate(["disc"], context[:opts]) + assert :ok == @command.validate(["disk"], context[:opts]) + end + + test "validate: providing no arguments fails validation", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + test "validate: providing too many arguments fails validation", context do + assert @command.validate(["a", "b", "c"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + # TODO + #test "run: change ram node to disc node", context do + #end + + # TODO + #test "run: change disk node to ram node", context do + #end + + test "run: request to a node with running RabbitMQ app fails", context do + assert match?( + {:error, :mnesia_unexpectedly_running}, + @command.run(["ram"], context[:opts])) + end + + test "run: request to an unreachable node returns a badrpc", _context do + opts = %{node: :jake@thedog, timeout: 200} + assert match?( + {:badrpc, :nodedown}, + @command.run(["ram"], opts)) + end + + test "banner", context do + assert @command.banner(["ram"], context[:opts]) =~ + ~r/Turning #{get_rabbit_hostname()} into a ram node/ + end + + test "output mnesia is running error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, + "Mnesia is still running on node " <> _}, + @command.output({:error, :mnesia_unexpectedly_running}, context[:opts])) + + end +end diff --git a/deps/rabbitmq_cli/test/ctl/change_password_command_test.exs b/deps/rabbitmq_cli/test/ctl/change_password_command_test.exs new file mode 100644 index 0000000000..3a415085dd --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/change_password_command_test.exs @@ -0,0 +1,80 @@ +## at https://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ChangePasswordCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands. ChangePasswordCommand + @user "user1" + @password "password" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_user(@user, @password) + on_exit(context, fn -> delete_user(@user) end) + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: no positional arguments fails" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: too many positional arguments fails" do + assert @command.validate(["user", "password", "extra"], %{}) == + {:validation_failure, :too_many_args} + end + + test "validate: two arguments passes" do + assert @command.validate(["user", "password"], %{}) == :ok + end + + test "validate: one argument passes" do + assert @command.validate(["user"], %{}) == :ok + end + + @tag user: @user, password: "new_password" + test "run: a valid username and new password return ok", context do + assert @command.run([context[:user], context[:password]], context[:opts]) == :ok + assert {:ok, _} = authenticate_user(context[:user], context[:password]) + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["user", "password"], opts)) + end + + @tag user: @user, password: @password + test "run: changing password to the same thing is ok", context do + assert @command.run([context[:user], context[:password]], context[:opts]) == :ok + assert {:ok, _} = authenticate_user(context[:user], context[:password]) + end + + @tag user: "interloper", password: "new_password" + test "run: an invalid user returns an error", context do + assert @command.run([context[:user], context[:password]], context[:opts]) == {:error, {:no_such_user, "interloper"}} + end + + @tag user: @user, password: @password + test "banner", context do + assert @command.banner([context[:user], context[:password]], context[:opts]) + =~ ~r/Changing password for user/ + assert @command.banner([context[:user], context[:password]], context[:opts]) + =~ ~r/"#{context[:user]}"/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_global_parameter_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_global_parameter_command_test.exs new file mode 100644 index 0000000000..adadc3c223 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_global_parameter_command_test.exs @@ -0,0 +1,86 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearGlobalParameterCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClearGlobalParameterCommand + @key :mqtt_default_vhosts + @value "{\"O=client,CN=dummy\":\"somevhost\"}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + on_exit(context, fn -> + clear_global_parameter context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + } + } + end + + test "validate: expects a single argument" do + assert @command.validate(["one"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this is", "too many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag key: @key + test "run: when global parameter does not exist, returns an error", context do + assert @command.run( + [context[:key]], + context[:opts] + ) == {:error_string, 'Parameter does not exist'} + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([@key], opts)) + end + + @tag key: @key + test "run: clears the parameter", context do + set_global_parameter(context[:key], @value) + + assert @command.run( + [context[:key]], + context[:opts] + ) == :ok + + assert_parameter_empty(context) + end + + @tag key: @key, value: @value + test "banner", context do + set_global_parameter(context[:key], @value) + + s = @command.banner( + [context[:key]], + context[:opts] + ) + + assert s =~ ~r/Clearing global runtime parameter/ + assert s =~ ~r/"#{context[:key]}"/ + end + + defp assert_parameter_empty(context) do + parameter = list_global_parameters() + |> Enum.filter(fn(param) -> + param[:key] == context[:key] + end) + assert parameter === [] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_operator_policy_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_operator_policy_command_test.exs new file mode 100644 index 0000000000..834caf89f7 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_operator_policy_command_test.exs @@ -0,0 +1,127 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearOperatorPolicyCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClearOperatorPolicyCommand + @vhost "test1" + @key "message-expiry" + @pattern "^queue\." + @value "{\"message-ttl\":10}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + on_exit(context, fn -> + clear_operator_policy(context[:vhost], context[:key]) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + } + } + end + + test "merge_defaults: adds default vhost if missing" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + end + + test "merge_defaults: does not change provided vhost" do + assert @command.merge_defaults([], %{vhost: "test_vhost"}) == {[], %{vhost: "test_vhost"}} + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: providing one argument and no options passes validation" do + assert @command.validate(["a-policy"], %{}) == :ok + end + + @tag pattern: @pattern, key: @key, vhost: @vhost + test "run: if policy does not exist, returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + assert match?({:badrpc, _}, @command.run([@key], opts)) + end + + + @tag pattern: @pattern, key: @key, vhost: @vhost + test "run: if policy exists, returns ok and removes it", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + set_operator_policy(context[:vhost], context[:key], context[:pattern], @value) + + assert @command.run( + [context[:key]], + vhost_opts + ) == :ok + + assert_operator_policy_does_not_exist(context) + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: "bad-vhost" + test "run: a non-existent vhost returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + set_operator_policy(context[:vhost], context[:key], context[:pattern], @value) + + s = @command.banner( + [context[:key]], + vhost_opts + ) + + assert s =~ ~r/Clearing operator policy/ + assert s =~ ~r/"#{context[:key]}"/ + end + + defp assert_operator_policy_does_not_exist(context) do + policy = context[:vhost] + |> list_operator_policies + |> Enum.filter(fn(param) -> + param[:pattern] == context[:pattern] and + param[:key] == context[:key] + end) + assert policy === [] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_parameter_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_parameter_command_test.exs new file mode 100644 index 0000000000..4f08234cb6 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_parameter_command_test.exs @@ -0,0 +1,138 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearParameterCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClearParameterCommand + @vhost "test1" + @root "/" + @component_name "federation-upstream" + @key "reconnect-delay" + @value "{\"uri\":\"amqp://127.0.0.1:5672\"}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + enable_federation_plugin() + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + on_exit(context, fn -> + clear_parameter context[:vhost], context[:component_name], context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + } + } + end + + test "merge_defaults: adds default vhost if missing" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: argument validation" do + assert @command.validate(["one", "two"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this", "is", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag component_name: @component_name, key: @key, vhost: @vhost + test "run: returns error, if parameter does not exist", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:component_name], context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + assert match?({:badrpc, _}, @command.run([@component_name, @key], opts)) + end + + + @tag component_name: @component_name, key: @key, vhost: @vhost + test "run: returns ok and clears parameter, if it exists", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + set_parameter(context[:vhost], context[:component_name], context[:key], @value) + + assert @command.run( + [context[:component_name], context[:key]], + vhost_opts + ) == :ok + + assert_parameter_empty(context) + end + + @tag component_name: "bad-component-name", key: @key, value: @value, vhost: @root + test "run: an invalid component_name returns a 'parameter does not exist' error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + assert @command.run( + [context[:component_name], context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + + assert list_parameters(context[:vhost]) == [] + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: "bad-vhost" + test "run: an invalid vhost returns a 'parameter does not exist' error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:component_name], context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + set_parameter(context[:vhost], context[:component_name], context[:key], @value) + + s = @command.banner( + [context[:component_name], context[:key]], + vhost_opts + ) + + assert s =~ ~r/Clearing runtime parameter/ + assert s =~ ~r/"#{context[:key]}"/ + assert s =~ ~r/"#{context[:component_name]}"/ + assert s =~ ~r/"#{context[:vhost]}"/ + end + + defp assert_parameter_empty(context) do + parameter = context[:vhost] + |> list_parameters + |> Enum.filter(fn(param) -> + param[:component_name] == context[:component_name] and + param[:key] == context[:key] + end) + assert parameter === [] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_password_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_password_command_test.exs new file mode 100644 index 0000000000..0843ca3970 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_password_command_test.exs @@ -0,0 +1,64 @@ +## at https://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearPasswordCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands. ClearPasswordCommand + @user "user1" + @password "password" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_user(@user, @password) + on_exit(context, fn -> delete_user(@user) end) + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: argument count is correct" do + assert @command.validate(["username"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["username", "extra"], %{}) == + {:validation_failure, :too_many_args} + end + + @tag user: @user, password: @password + test "run: a valid username clears the password and returns okay", context do + assert @command.run([context[:user]], context[:opts]) == :ok + assert {:refused, _, _, _} = authenticate_user(context[:user], context[:password]) + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["user"], opts)) + end + + @tag user: "interloper" + test "run: An invalid username returns a no-such-user error message", context do + assert @command.run([context[:user]], context[:opts]) == {:error, {:no_such_user, "interloper"}} + end + + @tag user: @user + test "banner", context do + s = @command.banner([context[:user]], context[:opts]) + + assert s =~ ~r/Clearing password/ + assert s =~ ~r/"#{context[:user]}"/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_permissions_command_test.exs new file mode 100644 index 0000000000..89bfe8c457 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_permissions_command_test.exs @@ -0,0 +1,100 @@ +## at https://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearPermissionsTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands. ClearPermissionsCommand + @user "user1" + @password "password" + @default_vhost "/" + @specific_vhost "vhost1" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_user(@user, @password) + add_vhost(@specific_vhost) + + on_exit([], fn -> + delete_user(@user) + delete_vhost(@specific_vhost) + end) + + :ok + end + + setup context do + set_permissions(@user, @default_vhost, ["^#{@user}-.*", ".*", ".*"]) + set_permissions(@user, @specific_vhost, ["^#{@user}-.*", ".*", ".*"]) + + { + :ok, + opts: %{node: get_rabbit_hostname(), vhost: context[:vhost]} + } + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: argument count validates" do + assert @command.validate(["one"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag user: "fake_user" + test "run: can't clear permissions for non-existing user", context do + assert @command.run([context[:user]], context[:opts]) == {:error, {:no_such_user, context[:user]}} + end + + @tag user: @user, vhost: @default_vhost + test "run: a valid username clears permissions", context do + assert @command.run([context[:user]], context[:opts]) == :ok + + assert list_permissions(@default_vhost) + |> Enum.filter(fn(record) -> record[:user] == context[:user] end) == [] + end + + test "run: on an invalid node, return a badrpc message" do + arg = ["some_name"] + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run(arg, opts)) + end + + @tag user: @user, vhost: @specific_vhost + test "run: on a valid specified vhost, clear permissions", context do + assert @command.run([context[:user]], context[:opts]) == :ok + + assert list_permissions(context[:vhost]) + |> Enum.filter(fn(record) -> record[:user] == context[:user] end) == [] + end + + @tag user: @user, vhost: "bad_vhost" + test "run: on an invalid vhost, return no_such_vhost error", context do + assert @command.run([context[:user]], context[:opts]) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag user: @user, vhost: @specific_vhost + test "banner", context do + s = @command.banner([context[:user]], context[:opts]) + + assert s =~ ~r/Clearing permissions/ + assert s =~ ~r/\"#{context[:user]}\"/ + assert s =~ ~r/\"#{context[:vhost]}\"/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_policy_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_policy_command_test.exs new file mode 100644 index 0000000000..f36f65d25f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_policy_command_test.exs @@ -0,0 +1,129 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearPolicyCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClearPolicyCommand + @vhost "test1" + @key "federate" + @pattern "^fed\." + @value "{\"federation-upstream-set\":\"all\"}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + enable_federation_plugin() + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + on_exit(context, fn -> + clear_policy context[:vhost], context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + } + } + end + + test "merge_defaults: adds default vhost if missing" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + end + + test "merge_defaults: does not change defined vhost" do + assert @command.merge_defaults([], %{vhost: "test_vhost"}) == {[], %{vhost: "test_vhost"}} + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: providing one argument and no options passes validation" do + assert @command.validate(["a-policy"], %{}) == :ok + end + + @tag pattern: @pattern, key: @key, vhost: @vhost + test "run: if policy does not exist, returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + assert match?({:badrpc, _}, @command.run([@key], opts)) + end + + + @tag pattern: @pattern, key: @key, vhost: @vhost + test "run: if policy exists, returns ok and removes it", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + set_policy(context[:vhost], context[:key], context[:pattern], @value) + + assert @command.run( + [context[:key]], + vhost_opts + ) == :ok + + assert_policy_does_not_exist(context) + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: "bad-vhost" + test "run: a non-existent vhost returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key]], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + set_policy(context[:vhost], context[:key], context[:pattern], @value) + + s = @command.banner( + [context[:key]], + vhost_opts + ) + + assert s =~ ~r/Clearing policy/ + assert s =~ ~r/"#{context[:key]}"/ + end + + defp assert_policy_does_not_exist(context) do + policy = context[:vhost] + |> list_policies + |> Enum.filter(fn(param) -> + param[:pattern] == context[:pattern] and + param[:key] == context[:key] + end) + assert policy === [] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_topic_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_topic_permissions_command_test.exs new file mode 100644 index 0000000000..2b5fb6e12a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_topic_permissions_command_test.exs @@ -0,0 +1,107 @@ +## at https://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearTopicPermissionsTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands. ClearTopicPermissionsCommand + @user "user1" + @password "password" + @specific_vhost "vhost1" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_user(@user, @password) + add_vhost(@specific_vhost) + + on_exit([], fn -> + clear_topic_permissions(@user, @specific_vhost) + delete_user(@user) + delete_vhost(@specific_vhost) + end) + + :ok + end + + setup context do + set_topic_permissions(@user, @specific_vhost, "amq.topic", "^a", "^b") + set_topic_permissions(@user, @specific_vhost, "topic1", "^a", "^b") + { + :ok, + opts: %{node: get_rabbit_hostname(), vhost: context[:vhost]} + } + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: expects username and optional exchange" do + assert @command.validate(["username"], %{}) == :ok + assert @command.validate(["username", "exchange"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag user: "fake_user" + test "run: can't clear topic permissions for non-existing user", context do + assert @command.run([context[:user]], context[:opts]) == {:error, {:no_such_user, context[:user]}} + end + + @tag user: @user, vhost: "bad_vhost" + test "run: on an invalid vhost, return no_such_vhost error", context do + assert @command.run([context[:user]], context[:opts]) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag user: @user, vhost: @specific_vhost + test "run: with a valid username clears all permissions for vhost", context do + assert Enum.count(list_user_topic_permissions(@user)) == 2 + assert @command.run([context[:user]], context[:opts]) == :ok + + assert Enum.count(list_user_topic_permissions(@user)) == 0 + end + + @tag user: @user, vhost: @specific_vhost + test "run: with a valid username and exchange clears only exchange permissions", context do + assert Enum.count(list_user_topic_permissions(@user)) == 2 + assert @command.run([context[:user], "amq.topic"], context[:opts]) == :ok + + assert Enum.count(list_user_topic_permissions(@user)) == 1 + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + arg = ["some_name"] + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run(arg, opts)) + end + + @tag user: @user, vhost: @specific_vhost + test "banner with username only", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:user]], vhost_opts) + =~ ~r/Clearing topic permissions for user \"#{context[:user]}\" in vhost \"#{context[:vhost]}\" \.\.\./ + end + + @tag user: @user, vhost: @specific_vhost + test "banner with username and exchange name", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:user], "amq.topic"], vhost_opts) + =~ ~r/Clearing topic permissions on \"amq.topic\" for user \"#{context[:user]}\" in vhost \"#{context[:vhost]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_user_limits_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_user_limits_command_test.exs new file mode 100644 index 0000000000..eb05a875bc --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_user_limits_command_test.exs @@ -0,0 +1,115 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ClearUserLimitsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClearUserLimitsCommand + + @user "someone" + @password "password" + @limittype "max-channels" + @channel_definition "{\"max-channels\":100}" + @definition "{\"max-channels\":500, \"max-connections\":100}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_user @user, @password + + on_exit([], fn -> + delete_user @user + end) + + :ok + end + + setup context do + on_exit(context, fn -> + clear_user_limits(context[:user]) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + } + } + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not-enough"], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([@user, @limittype], opts)) + end + + test "run: if limit exists, returns ok and clears it", context do + :ok = set_user_limits(@user, @channel_definition) + + assert get_user_limits(@user) != [] + + assert @command.run( + [@user, @limittype], + context[:opts] + ) == :ok + + assert get_user_limits(@user) == %{} + end + + test "run: if limit exists, returns ok and clears all limits for the given user", context do + :ok = set_user_limits(@user, @definition) + + assert get_user_limits(@user) != [] + + assert @command.run( + [@user, "all"], + context[:opts] + ) == :ok + + assert get_user_limits(@user) == %{} + end + + @tag user: "bad-user" + test "run: a non-existent user returns an error", context do + + assert @command.run( + [context[:user], @limittype], + context[:opts] + ) == {:error, {:no_such_user, "bad-user"}} + end + + test "banner: for a limit type", context do + + s = @command.banner( + [@user, @limittype], + context[:opts] + ) + + assert s == "Clearing \"#{@limittype}\" limit for user \"#{@user}\" ..." + end + + test "banner: for all", context do + + s = @command.banner( + [@user, "all"], + context[:opts] + ) + + assert s == "Clearing all limits for user \"#{@user}\" ..." + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/clear_vhost_limits_command_test.exs b/deps/rabbitmq_cli/test/ctl/clear_vhost_limits_command_test.exs new file mode 100644 index 0000000000..4dd681c901 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/clear_vhost_limits_command_test.exs @@ -0,0 +1,103 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClearVhostLimitsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClearVhostLimitsCommand + @vhost "test1" + @definition "{\"max-connections\":100}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + on_exit(context, fn -> + clear_vhost_limits(context[:vhost]) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + } + } + end + + test "merge_defaults: adds default vhost if missing" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + end + + test "merge_defaults: does not change defined vhost" do + assert @command.merge_defaults([], %{vhost: "test_vhost"}) == {[], %{vhost: "test_vhost"}} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: providing zero arguments and no options passes validation" do + assert @command.validate([], %{}) == :ok + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + + @tag vhost: @vhost + test "run: if limits exist, returns ok and clears them", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + :ok = set_vhost_limits(context[:vhost], @definition) + + assert get_vhost_limits(context[:vhost]) != [] + + assert @command.run( + [], + vhost_opts + ) == :ok + + assert get_vhost_limits(context[:vhost]) == %{} + end + + @tag vhost: "bad-vhost" + test "run: a non-existent vhost returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [], + vhost_opts + ) == {:error_string, 'Parameter does not exist'} + end + + @tag vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + s = @command.banner( + [], + vhost_opts + ) + + assert s =~ ~r/Clearing vhost \"#{context[:vhost]}\" limits .../ + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/close_all_connections_command_test.exs b/deps/rabbitmq_cli/test/ctl/close_all_connections_command_test.exs new file mode 100644 index 0000000000..f08969f319 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/close_all_connections_command_test.exs @@ -0,0 +1,147 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule CloseAllConnectionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + alias RabbitMQ.CLI.Ctl.RpcStream + @helpers RabbitMQ.CLI.Core.Helpers + @command RabbitMQ.CLI.Ctl.Commands.CloseAllConnectionsCommand + + @vhost "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + close_all_connections(get_rabbit_hostname()) + + on_exit([], fn -> + close_all_connections(get_rabbit_hostname()) + end) + + :ok + end + + setup context do + node_name = get_rabbit_hostname() + close_all_connections(node_name) + await_no_client_connections(node_name, 5_000) + + {:ok, context} + end + + test "validate: with an invalid number of arguments returns an arg count error", context do + assert @command.validate(["random", "explanation"], context[:opts]) == {:validation_failure, :too_many_args} + assert @command.validate([], context[:opts]) == {:validation_failure, :not_enough_args} + end + + test "validate: with the correct number of arguments returns ok", context do + assert @command.validate(["explanation"], context[:opts]) == :ok + end + + test "run: a close connections request in an existing vhost with all defaults closes all connections", context do + with_connection(@vhost, fn(_) -> + node = @helpers.normalise_node(context[:node], :shortnames) + nodes = @helpers.nodes_in_cluster(node) + [[vhost: @vhost]] = fetch_connection_vhosts(node, nodes) + opts = %{node: node, vhost: @vhost, global: false, per_connection_delay: 0, limit: 0} + assert {:ok, "Closed 1 connections"} == @command.run(["test"], opts) + + await_no_client_connections(node, 5_000) + assert fetch_connection_vhosts(node, nodes) == [] + end) + end + + test "run: close a limited number of connections in an existing vhost closes a subset of connections", context do + with_connections([@vhost, @vhost, @vhost], fn(_) -> + node = @helpers.normalise_node(context[:node], :shortnames) + nodes = @helpers.nodes_in_cluster(node) + [[vhost: @vhost], [vhost: @vhost], [vhost: @vhost]] = fetch_connection_vhosts(node, nodes) + opts = %{node: node, vhost: @vhost, global: false, per_connection_delay: 0, limit: 2} + assert {:ok, "Closed 2 connections"} == @command.run(["test"], opts) + Process.sleep(100) + assert fetch_connection_vhosts(node, nodes) == [[vhost: @vhost]] + end) + end + + test "run: a close connections request for a non-existing vhost does nothing", context do + with_connection(@vhost, fn(_) -> + node = @helpers.normalise_node(context[:node], :shortnames) + nodes = @helpers.nodes_in_cluster(node) + [[vhost: @vhost]] = fetch_connection_vhosts(node, nodes) + opts = %{node: node, vhost: "non_existent-9288737", global: false, per_connection_delay: 0, limit: 0} + assert {:ok, "Closed 0 connections"} == @command.run(["test"], opts) + assert fetch_connection_vhosts(node, nodes) == [[vhost: @vhost]] + end) + end + + test "run: a close connections request to an existing node with --global (all vhosts)", context do + with_connection(@vhost, fn(_) -> + node = @helpers.normalise_node(context[:node], :shortnames) + nodes = @helpers.nodes_in_cluster(node) + [[vhost: @vhost]] = fetch_connection_vhosts(node, nodes) + opts = %{node: node, global: true, per_connection_delay: 0, limit: 0} + assert {:ok, "Closed 1 connections"} == @command.run(["test"], opts) + await_no_client_connections(node, 5_000) + assert fetch_connection_vhosts(node, nodes) == [] + end) + end + + test "run: a close_all_connections request to non-existent RabbitMQ node returns a badrpc" do + opts = %{node: :jake@thedog, vhost: @vhost, global: true, per_connection_delay: 0, limit: 0, timeout: 200} + assert match?({:badrpc, _}, @command.run(["test"], opts)) + end + + test "banner for vhost option", context do + node = @helpers.normalise_node(context[:node], :shortnames) + opts = %{node: node, vhost: "burrow", global: false, per_connection_delay: 0, limit: 0} + s = @command.banner(["some reason"], opts) + assert s =~ ~r/Closing all connections in vhost burrow/ + assert s =~ ~r/some reason/ + end + + test "banner for vhost option with limit", context do + node = @helpers.normalise_node(context[:node], :shortnames) + opts = %{node: node, vhost: "burrow", global: false, per_connection_delay: 0, limit: 2} + s = @command.banner(["some reason"], opts) + assert s =~ ~r/Closing 2 connections in vhost burrow/ + assert s =~ ~r/some reason/ + end + + test "banner for global option" do + opts = %{node: :test@localhost, vhost: "burrow", global: true, per_connection_delay: 0, limit: 0} + s = @command.banner(["some reason"], opts) + assert s =~ ~r/Closing all connections to node test@localhost/ + assert s =~ ~r/some reason/ + end + + defp fetch_connection_vhosts(node, nodes) do + fetch_connection_vhosts(node, nodes, 50) + end + + defp fetch_connection_vhosts(node, nodes, retries) do + stream = RpcStream.receive_list_items(node, + :rabbit_networking, + :emit_connection_info_all, + [nodes, [:vhost]], + :infinity, + [:vhost], + Kernel.length(nodes)) + xs = Enum.to_list(stream) + + case {xs, retries} do + {xs, 0} -> + xs + {[], n} when n >= 0 -> + Process.sleep(10) + fetch_connection_vhosts(node, nodes, retries - 1) + _ -> + xs + end + end +end diff --git a/deps/rabbitmq_cli/test/ctl/close_connection_command_test.exs b/deps/rabbitmq_cli/test/ctl/close_connection_command_test.exs new file mode 100644 index 0000000000..0d1271a67f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/close_connection_command_test.exs @@ -0,0 +1,96 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule CloseConnectionCommandTest do + use ExUnit.Case, async: false + import TestHelper + + alias RabbitMQ.CLI.Ctl.RpcStream + + @helpers RabbitMQ.CLI.Core.Helpers + + @command RabbitMQ.CLI.Ctl.Commands.CloseConnectionCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + close_all_connections(get_rabbit_hostname()) + + on_exit([], fn -> + close_all_connections(get_rabbit_hostname()) + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: :infinity}} + end + + test "validate: with an invalid number of arguments returns an arg count error", context do + assert @command.validate(["pid", "explanation", "extra"], context[:opts]) == {:validation_failure, :too_many_args} + assert @command.validate(["pid"], context[:opts]) == {:validation_failure, :not_enough_args} + end + + test "validate: with the correct number of arguments returns ok", context do + assert @command.validate(["pid", "test"], context[:opts]) == :ok + end + + test "run: a close connection request on an existing connection", context do + with_connection("/", fn(_) -> + Process.sleep(500) + node = @helpers.normalise_node(context[:node], :shortnames) + nodes = @helpers.nodes_in_cluster(node) + [[pid: pid]] = fetch_connection_pids(node, nodes) + assert :ok == @command.run([:rabbit_misc.pid_to_string(pid), "test"], %{node: node}) + Process.sleep(500) + assert fetch_connection_pids(node, nodes) == [] + end) + end + + test "run: a close connection request on for a non existing connection returns successfully", context do + assert match?(:ok, + @command.run(["<#{node()}.2.121.12>", "test"], %{node: @helpers.normalise_node(context[:node], :shortnames)})) + end + + test "run: a close_connection request on nonexistent RabbitMQ node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["<rabbit@localhost.1.2.1>", "test"], opts)) + end + + test "banner", context do + s = @command.banner(["<rabbit@bananas.1.2.3>", "some reason"], context[:opts]) + assert s =~ ~r/Closing connection/ + assert s =~ ~r/<rabbit@bananas.1.2.3>/ + end + + defp fetch_connection_pids(node, nodes) do + fetch_connection_pids(node, nodes, 10) + end + + defp fetch_connection_pids(node, nodes, retries) do + stream = RpcStream.receive_list_items(node, + :rabbit_networking, + :emit_connection_info_all, + [nodes, [:pid]], + :infinity, + [:pid], + Kernel.length(nodes)) + xs = Enum.to_list(stream) + + case {xs, retries} do + {xs, 0} -> + xs + {[], n} when n >= 0 -> + Process.sleep(100) + fetch_connection_pids(node, nodes, retries - 1) + _ -> + xs + end + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/cluster_status_command_test.exs b/deps/rabbitmq_cli/test/ctl/cluster_status_command_test.exs new file mode 100644 index 0000000000..e582355e7a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/cluster_status_command_test.exs @@ -0,0 +1,50 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ClusterStatusCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ClusterStatusCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 12000}} + end + + test "validate: argument count validates", context do + assert @command.validate([], context[:opts]) == :ok + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: status request to a reachable node returns cluster information", context do + n = context[:opts][:node] + res = @command.run([], context[:opts]) + + assert Enum.member?(res[:nodes][:disc], n) + assert res[:partitions] == [] + assert res[:alarms][n] == [] + end + + test "run: status request on nonexistent RabbitMQ node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + s = @command.banner([], context[:opts]) + + assert s =~ ~r/Cluster status of node/ + assert s =~ ~r/#{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/decode_command_test.exs b/deps/rabbitmq_cli/test/ctl/decode_command_test.exs new file mode 100644 index 0000000000..79850d7786 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/decode_command_test.exs @@ -0,0 +1,95 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule DecodeCommandTest do + use ExUnit.Case, async: false + @command RabbitMQ.CLI.Ctl.Commands.DecodeCommand + + setup _context do + {:ok, opts: %{ + cipher: :rabbit_pbe.default_cipher, + hash: :rabbit_pbe.default_hash, + iterations: :rabbit_pbe.default_iterations + }} + end + + test "validate: providing exactly 2 positional arguments passes", context do + assert :ok == @command.validate(["value", "secret"], context[:opts]) + end + + test "validate: providing zero or one positional argument fails", context do + assert match?({:validation_failure, {:not_enough_args, _}}, + @command.validate([], context[:opts])) + assert match?({:validation_failure, {:not_enough_args, _}}, + @command.validate(["value"], context[:opts])) + end + + test "validate: providing three or more positional argument fails", context do + assert match?({:validation_failure, :too_many_args}, + @command.validate(["value", "secret", "incorrect"], context[:opts])) + end + + test "validate: hash and cipher must be supported", context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{cipher: :funny_cipher})) + ) + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{hash: :funny_hash})) + ) + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{cipher: :funny_cipher, hash: :funny_hash})) + ) + assert :ok == @command.validate(["value", "secret"], context[:opts]) + end + + test "validate: number of iterations must greater than 0", context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: 0})) + ) + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: -1})) + ) + assert :ok == @command.validate(["value", "secret"], context[:opts]) + end + + test "run: encrypt/decrypt", context do + # an Erlang list/bitstring + encrypt_decrypt(to_charlist("foobar"), context) + # a binary + encrypt_decrypt("foobar", context) + # a tuple + encrypt_decrypt({:password, "secret"}, context) + end + + defp encrypt_decrypt(secret, context) do + passphrase = "passphrase" + cipher = context[:opts][:cipher] + hash = context[:opts][:hash] + iterations = context[:opts][:iterations] + output = {:encrypted, _encrypted} = :rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, secret) + + {:encrypted, encrypted} = output + # decode plain value + assert {:ok, secret} === @command.run([format_as_erlang_term(encrypted), passphrase], context[:opts]) + # decode {encrypted, ...} tuple form + assert {:ok, secret} === @command.run([format_as_erlang_term(output), passphrase], context[:opts]) + + # wrong passphrase + assert match?({:error, _}, + @command.run([format_as_erlang_term(encrypted), "wrong/passphrase"], context[:opts])) + assert match?({:error, _}, + @command.run([format_as_erlang_term(output), "wrong passphrase"], context[:opts])) + end + + defp format_as_erlang_term(value) do + :io_lib.format("~p", [value]) |> :lists.flatten() |> to_string() + end +end diff --git a/deps/rabbitmq_cli/test/ctl/delete_queue_command_test.exs b/deps/rabbitmq_cli/test/ctl/delete_queue_command_test.exs new file mode 100644 index 0000000000..b0971b8961 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/delete_queue_command_test.exs @@ -0,0 +1,119 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule DeleteQueueCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.DeleteQueueCommand + @user "guest" + @vhost "delete-queue-vhost" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + {:ok, opts: %{ + node: get_rabbit_hostname(), + vhost: @vhost, + timeout: context[:test_timeout], + if_empty: false, + if_unused: false + }} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/", if_empty: false, if_unused: false}} + assert @command.merge_defaults([], %{vhost: "non_default", if_empty: true}) == + {[], %{vhost: "non_default", if_empty: true, if_unused: false}} + end + + test "validate: providing no queue name fails validation", context do + assert match?( + {:validation_failure, :not_enough_args}, + @command.validate([], context[:opts]) + ) + end + + test "validate: providing an empty queue name fails validation", context do + assert match?( + {:validation_failure, {:bad_argument, "queue name cannot be an empty string"}}, + @command.validate([""], context[:opts]) + ) + end + + test "validate: providing a non-blank queue name and -u succeeds", context do + assert @command.validate(["a-queue"], %{ + node: get_rabbit_hostname(), + vhost: @vhost, + timeout: context[:test_timeout], + if_unused: false + }) == :ok + end + + @tag test_timeout: 30000 + test "run: request to an existent queue on active node succeeds", context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(context, fn -> delete_vhost(@vhost) end) + + q = "foo" + n = 20 + + declare_queue(q, @vhost) + publish_messages(@vhost, q, n) + + assert @command.run([q], context[:opts]) == {:ok, n} + {:error, :not_found} = lookup_queue(q, @vhost) + end + + @tag test_timeout: 30000 + test "run: request to a non-existent queue on active node returns not found", context do + assert @command.run(["non-existent"], context[:opts]) == {:error, :not_found} + end + + @tag test_timeout: 0 + test "run: timeout causes command to return a bad RPC", context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(context, fn -> delete_vhost(@vhost) end) + + q = "foo" + declare_queue(q, @vhost) + assert @command.run([q], context[:opts]) == {:badrpc, :timeout} + end + + test "shows up in help" do + s = @command.usage() + assert s =~ ~r/delete_queue/ + end + + test "defaults to vhost /" do + assert @command.merge_defaults(["foo"], %{bar: "baz"}) == {["foo"], %{bar: "baz", vhost: "/", if_unused: false, if_empty: false}} + end + + test "validate: with extra arguments returns an arg count error" do + assert @command.validate(["queue-name", "extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: with no arguments returns an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: with correct args returns ok" do + assert @command.validate(["q"], %{}) == :ok + end + + test "banner informs that vhost's queue is deleted" do + assert @command.banner(["my-q"], %{vhost: "/foo", if_empty: false, if_unused: false}) == "Deleting queue 'my-q' on vhost '/foo' ..." + assert @command.banner(["my-q"], %{vhost: "/foo", if_empty: true, if_unused: false}) == "Deleting queue 'my-q' on vhost '/foo' if queue is empty ..." + assert @command.banner(["my-q"], %{vhost: "/foo", if_empty: true, if_unused: true}) == "Deleting queue 'my-q' on vhost '/foo' if queue is empty and if queue is unused ..." + end +end diff --git a/deps/rabbitmq_cli/test/ctl/delete_user_command_test.exs b/deps/rabbitmq_cli/test/ctl/delete_user_command_test.exs new file mode 100644 index 0000000000..97f09654a9 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/delete_user_command_test.exs @@ -0,0 +1,59 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule DeleteUserCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.DeleteUserCommand + @user "username" + @password "password" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + :ok + end + + setup context do + add_user(context[:user], @password) + on_exit(context, fn -> delete_user(context[:user]) end) + + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + @tag user: @user + test "validate: argument count validates" do + assert @command.validate(["one"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag user: @user + test "run: A valid username returns ok", context do + assert @command.run([context[:user]], context[:opts]) == :ok + + assert list_users() |> Enum.count(fn(record) -> record[:user] == context[:user] end) == 0 + end + + test "run: An invalid Rabbit node returns a bad rpc message" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(["username"], opts)) + end + + @tag user: @user + test "run: An invalid username returns an error", context do + assert @command.run(["no_one"], context[:opts]) == {:error, {:no_such_user, "no_one"}} + end + + @tag user: @user + test "banner", context do + s = @command.banner([context[:user]], context[:opts]) + assert s =~ ~r/Deleting user/ + assert s =~ ~r/\"#{context[:user]}\"/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/delete_vhost_command_test.exs b/deps/rabbitmq_cli/test/ctl/delete_vhost_command_test.exs new file mode 100644 index 0000000000..057f0789dc --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/delete_vhost_command_test.exs @@ -0,0 +1,67 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule DeleteVhostCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.DeleteVhostCommand + @vhost "test" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_vhost(context[:vhost]) + on_exit(context, fn -> delete_vhost(context[:vhost]) end) + + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: argument count validates" do + assert @command.validate(["tst"], %{}) == :ok + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["test", "extra"], %{}) == {:validation_failure, :too_many_args} + end + + @tag vhost: @vhost + test "run: A valid name to an active RabbitMQ node is successful", context do + assert @command.run([context[:vhost]], context[:opts]) == :ok + + assert list_vhosts() |> Enum.count(fn(record) -> record[:name] == context[:vhost] end) == 0 + end + + @tag vhost: "" + test "run: An empty string to an active RabbitMQ node is successful", context do + assert @command.run([context[:vhost]], context[:opts]) == :ok + + assert list_vhosts() |> Enum.count(fn(record) -> record[:name] == context[:vhost] end) == 0 + end + + test "run: A call to invalid or inactive RabbitMQ node returns a nodedown" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(["na"], opts)) + end + + @tag vhost: @vhost + test "run: Deleting the same host twice results in a host not found message", context do + @command.run([context[:vhost]], context[:opts]) + assert @command.run([context[:vhost]], context[:opts]) == + {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag vhost: @vhost + test "banner", context do + s = @command.banner([context[:vhost]], context[:opts]) + assert s =~ ~r/Deleting vhost/ + assert s =~ ~r/\"#{context[:vhost]}\"/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/enable_feature_flag_test.exs b/deps/rabbitmq_cli/test/ctl/enable_feature_flag_test.exs new file mode 100644 index 0000000000..f8a3e62920 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/enable_feature_flag_test.exs @@ -0,0 +1,70 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2018-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule EnableFeatureFlagCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.EnableFeatureFlagCommand + @feature_flag :ff_from_enable_ff_testsuite + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + # Define an arbitrary feature flag for the test. + node = get_rabbit_hostname() + new_feature_flags = %{ + @feature_flag => + %{desc: "My feature flag", + provided_by: :EnableFeatureFlagCommandTest, + stability: :stable}} + :ok = :rabbit_misc.rpc_call( + node, :rabbit_feature_flags, :initialize_registry, [new_feature_flags]) + + { + :ok, + opts: %{node: get_rabbit_hostname()}, + feature_flag: @feature_flag + } + end + + test "validate: wrong number of arguments results in arg count errors" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["ff_from_enable_ff_testsuite", "whoops"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: passing an empty string for feature_flag name is an arg error", context do + assert match?({:validation_failure, {:bad_argument, _}}, @command.validate([""], context[:opts])) + end + + test "run: passing a valid feature_flag name to a running RabbitMQ node succeeds", context do + assert @command.run([Atom.to_string(context[:feature_flag])], context[:opts]) == :ok + assert list_feature_flags(:enabled) |> Map.has_key?(context[:feature_flag]) + end + + test "run: attempt to use an unreachable node returns a nodedown" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["na"], opts)) + end + + test "run: enabling the same feature flag twice is idempotent", context do + enable_feature_flag context[:feature_flag] + assert @command.run([Atom.to_string(context[:feature_flag])], context[:opts]) == :ok + assert list_feature_flags(:enabled) |> Map.has_key?(context[:feature_flag]) + end + + test "run: enabling all feature flags succeeds", context do + enable_feature_flag context[:feature_flag] + assert @command.run(["all"], context[:opts]) == :ok + assert list_feature_flags(:enabled) |> Map.has_key?(context[:feature_flag]) + end + + test "banner", context do + assert @command.banner([context[:feature_flag]], context[:opts]) + =~ ~r/Enabling feature flag \"#{context[:feature_flag]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/encode_command_test.exs b/deps/rabbitmq_cli/test/ctl/encode_command_test.exs new file mode 100644 index 0000000000..550e4b24da --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/encode_command_test.exs @@ -0,0 +1,92 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule EncodeCommandTest do + use ExUnit.Case, async: false + + @command RabbitMQ.CLI.Ctl.Commands.EncodeCommand + + setup _context do + {:ok, opts: %{ + cipher: :rabbit_pbe.default_cipher, + hash: :rabbit_pbe.default_hash, + iterations: :rabbit_pbe.default_iterations + }} + end + + test "validate: providing exactly 2 positional arguments passes", context do + assert :ok == @command.validate(["value", "secret"], context[:opts]) + end + + test "validate: providing zero or one positional argument fails", context do + assert match?({:validation_failure, {:not_enough_args, _}}, + @command.validate([], context[:opts])) + assert match?({:validation_failure, {:not_enough_args, _}}, + @command.validate(["value"], context[:opts])) + end + + test "validate: providing three or more positional argument fails", context do + assert match?({:validation_failure, :too_many_args}, + @command.validate(["value", "secret", "incorrect"], context[:opts])) + end + + test "validate: hash and cipher must be supported", context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{cipher: :funny_cipher})) + ) + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{hash: :funny_hash})) + ) + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{cipher: :funny_cipher, hash: :funny_hash})) + ) + assert :ok == @command.validate(["value", "secret"], context[:opts]) + end + + test "validate: number of iterations must greater than 0", context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: 0})) + ) + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["value", "secret"], Map.merge(context[:opts], %{iterations: -1})) + ) + assert :ok == @command.validate(["value", "secret"], context[:opts]) + end + + test "run: encrypt/decrypt", context do + # an Erlang list/bitstring + encrypt_decrypt(to_charlist("foobar"), context) + # a binary + encrypt_decrypt("foobar", context) + # a tuple + encrypt_decrypt({:password, "secret"}, context) + end + + defp encrypt_decrypt(secret, context) do + secret_as_erlang_term = format_as_erlang_term(secret) + passphrase = "passphrase" + + cipher = context[:opts][:cipher] + hash = context[:opts][:hash] + iterations = context[:opts][:iterations] + + {:ok, output} = @command.run([secret_as_erlang_term, passphrase], context[:opts]) + {:encrypted, encrypted} = output + # decode plain value + assert secret === :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, {:plaintext, secret}) + # decode {encrypted, ...} tuple form + assert secret === :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, {:encrypted, encrypted}) + end + + defp format_as_erlang_term(value) do + :io_lib.format("~p", [value]) |> :lists.flatten() |> to_string() + end +end diff --git a/deps/rabbitmq_cli/test/ctl/environment_command_test.exs b/deps/rabbitmq_cli/test/ctl/environment_command_test.exs new file mode 100644 index 0000000000..7f801d54dc --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/environment_command_test.exs @@ -0,0 +1,45 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule EnvironmentCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.EnvironmentCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: argument count validates" do + assert @command.validate([], %{}) == :ok + assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args} + end + + @tag target: get_rabbit_hostname() + test "run: environment request on a named, active RMQ node is successful", context do + assert @command.run([], context[:opts])[:kernel] != nil + assert @command.run([], context[:opts])[:rabbit] != nil + end + + test "run: environment request on nonexistent RabbitMQ node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) + =~ ~r/Application environment of node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/eval_command_test.exs b/deps/rabbitmq_cli/test/ctl/eval_command_test.exs new file mode 100644 index 0000000000..92a2d77667 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/eval_command_test.exs @@ -0,0 +1,74 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule EvalCommandTest do + use ExUnit.Case, async: false + import TestHelper + import ExUnit.CaptureIO + + @command RabbitMQ.CLI.Ctl.Commands.EvalCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + :ok + end + + setup _ do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: providing no arguments succeeds" do + # expression is expected to be provided via standard input + assert @command.validate([], %{}) == :ok + end + + test "validate: empty expression to eval fails validation" do + assert @command.validate([""], %{}) == {:validation_failure, "Expression must not be blank"} + assert @command.validate(["", "foo"], %{}) == {:validation_failure, "Expression must not be blank"} + end + + test "validate: syntax error in expression to eval fails validation" do + assert @command.validate(["foo bar"], %{}) == {:validation_failure, "syntax error before: bar"} + assert @command.validate(["foo bar", "foo"], %{}) == {:validation_failure, "syntax error before: bar"} + end + + test "run: request to a non-existent node returns a badrpc", _context do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(["ok."], opts)) + end + + test "run: evaluates provided Erlang expression", context do + assert @command.run(["foo."], context[:opts]) == {:ok, :foo} + assert @command.run(["length([1,2,3])."], context[:opts]) == {:ok, 3} + assert @command.run(["lists:sum([1,2,3])."], context[:opts]) == {:ok, 6} + {:ok, apps} = @command.run(["application:loaded_applications()."], context[:opts]) + assert is_list(apps) + end + + test "run: evaluates provided expression on the target server node", context do + {:ok, apps} = @command.run(["application:loaded_applications()."], context[:opts]) + assert is_list(apps) + assert List.keymember?(apps, :rabbit, 0) + end + + test "run: returns stdout output", context do + assert capture_io(fn -> + assert @command.run(["io:format(\"output\")."], context[:opts]) == {:ok, :ok} + end) == "output" + end + + test "run: passes parameters to the expression as positional/numerical variables", context do + assert @command.run(["binary_to_atom(_1, utf8).", "foo"], context[:opts]) == {:ok, :foo} + assert @command.run(["{_1, _2}.", "foo", "bar"], context[:opts]) == {:ok, {"foo", "bar"}} + end + + test "run: passes globally recognised options as named variables", context do + assert @command.run(["{_vhost, _node}."], Map.put(context[:opts], :vhost, "a-node")) == + {:ok, {"a-node", context[:opts][:node]}} + end +end diff --git a/deps/rabbitmq_cli/test/ctl/eval_file_command_test.exs b/deps/rabbitmq_cli/test/ctl/eval_file_command_test.exs new file mode 100644 index 0000000000..74cb272f98 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/eval_file_command_test.exs @@ -0,0 +1,72 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule EvalFileCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.EvalFileCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + :ok + end + + setup _ do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: providing no arguments fails validation" do + # expression is expected to be provided via standard input + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: empty file path fails validation" do + assert @command.validate([""], %{}) == {:validation_failure, "File path must not be blank"} + end + + test "validate: path to a non-existent file fails validation" do + path = "/tmp/rabbitmq/cli-tests/12937293782368263726.lolz.escript" + assert @command.validate([path], %{}) == {:validation_failure, "File #{path} does not exist"} + end + + test "run: request to a non-existent node returns a badrpc", _context do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([valid_file_path()], opts)) + end + + test "run: evaluates expressions in the file on the target server node", context do + {:ok, apps} = @command.run([loaded_applications_file_path()], context[:opts]) + assert is_list(apps) + assert List.keymember?(apps, :rabbit, 0) + end + + test "run: returns evaluation result", context do + assert {:ok, 2} == @command.run([valid_file_path()], context[:opts]) + end + + test "run: reports invalid syntax errors", context do + assert match?({:error, _}, @command.run([invalid_file_path()], context[:opts])) + end + + # + # Implementation + # + + defp valid_file_path() do + Path.join([File.cwd!(), "test", "fixtures", "files", "valid_erl_expression.escript"]) + end + + defp invalid_file_path() do + Path.join([File.cwd!(), "test", "fixtures", "files", "invalid_erl_expression.escript"]) + end + + defp loaded_applications_file_path() do + Path.join([File.cwd!(), "test", "fixtures", "files", "loaded_applications.escript"]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/exec_command_test.exs b/deps/rabbitmq_cli/test/ctl/exec_command_test.exs new file mode 100644 index 0000000000..bb839f5434 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/exec_command_test.exs @@ -0,0 +1,47 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ExecCommandTest do + use ExUnit.Case, async: false + + @command RabbitMQ.CLI.Ctl.Commands.ExecCommand + + setup _ do + {:ok, opts: %{}} + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: there should be only one argument" do + assert @command.validate(["foo", "bar"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["", "bar"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: empty expression to exec fails validation" do + assert @command.validate([""], %{}) == {:validation_failure, "Expression must not be blank"} + end + + test "validate: success" do + :ok = @command.validate([":ok"], %{}) + end + + test "run: executes elixir code" do + {:ok, :ok} = @command.run([":ok"], %{}) + node = Node.self() + {:ok, ^node} = @command.run(["Node.self()"], %{}) + {:ok, 3} = @command.run(["1 + 2"], %{}) + end + + test "run: binds options variable" do + opts = %{my: :custom, option: 123} + {:ok, ^opts} = @command.run(["options"], opts) + {:ok, 123} = @command.run(["options[:option]"], opts) + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/export_definitions_command_test.exs b/deps/rabbitmq_cli/test/ctl/export_definitions_command_test.exs new file mode 100644 index 0000000000..3506b1ea80 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/export_definitions_command_test.exs @@ -0,0 +1,138 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ExportDefinitionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ExportDefinitionsCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + {:ok, opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || 30000, + format: context[:format] || "json" + }} + end + + test "merge_defaults: defaults to JSON for format" do + assert @command.merge_defaults([valid_file_path()], %{}) == + {[valid_file_path()], %{format: "json"}} + end + + test "merge_defaults: defaults to --silent if target is stdout" do + assert @command.merge_defaults(["-"], %{}) == {["-"], %{format: "json", silent: true}} + end + + test "merge_defaults: format is case insensitive" do + assert @command.merge_defaults([valid_file_path()], %{format: "JSON"}) == + {[valid_file_path()], %{format: "json"}} + assert @command.merge_defaults([valid_file_path()], %{format: "Erlang"}) == + {[valid_file_path()], %{format: "erlang"}} + end + + test "merge_defaults: format can be overridden" do + assert @command.merge_defaults([valid_file_path()], %{format: "erlang"}) == + {[valid_file_path()], %{format: "erlang"}} + end + + test "validate: accepts a file path argument", context do + assert @command.validate([valid_file_path()], context[:opts]) == :ok + end + + test "validate: accepts a dash for stdout", context do + assert @command.validate(["-"], context[:opts]) == :ok + end + + test "validate: unsupported format fails validation", context do + assert match?({:validation_failure, {:bad_argument, _}}, + @command.validate([valid_file_path()], Map.merge(context[:opts], %{format: "yolo"}))) + end + + test "validate: no positional arguments fails validation", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate: more than one positional argument fails validation", context do + assert @command.validate([valid_file_path(), "extra-arg"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: supports JSON and Erlang formats", context do + assert @command.validate([valid_file_path()], Map.merge(context[:opts], %{format: "json"})) == :ok + assert @command.validate([valid_file_path()], Map.merge(context[:opts], %{format: "erlang"})) == :ok + end + + @tag test_timeout: 3000 + test "run: targeting an unreachable node throws a badrpc", context do + result = @command.run([valid_file_path()], + %{node: :jake@thedog, + timeout: context[:test_timeout], + format: "json"}) + assert match?({:badrpc, _}, result) + end + + @tag format: "json" + test "run: returns a list of definitions when target is stdout and format is JSON", context do + {:ok, map} = @command.run(["-"], context[:opts]) + assert Map.has_key?(map, :rabbitmq_version) + end + + @tag format: "erlang" + test "run: returns a list of definitions when target is stdout and format is Erlang Terms", context do + {:ok, map} = @command.run(["-"], context[:opts]) + assert Map.has_key?(map, :rabbitmq_version) + end + + @tag format: "json" + test "run: writes to a file and returns nil when target is a file and format is JSON", context do + File.rm(valid_file_path()) + {:ok, nil} = @command.run([valid_file_path()], context[:opts]) + + {:ok, bin} = File.read(valid_file_path()) + {:ok, map} = JSON.decode(bin) + assert Map.has_key?(map, "rabbitmq_version") + end + + @tag format: "json" + test "run: correctly formats runtime parameter values", context do + File.rm(valid_file_path()) + imported_file_path = Path.join([File.cwd!(), "test", "fixtures", "files", "definitions.json"]) + # prepopulate some runtime parameters + RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand.run([imported_file_path], context[:opts]) + + {:ok, nil} = @command.run([valid_file_path()], context[:opts]) + + # clean up the state we've modified + clear_parameter("/", "federation-upstream", "up-1") + + {:ok, bin} = File.read(valid_file_path()) + {:ok, map} = JSON.decode(bin) + assert Map.has_key?(map, "rabbitmq_version") + params = map["parameters"] + assert is_map(hd(params)["value"]) + end + + @tag format: "erlang" + test "run: writes to a file and returns nil when target is a file and format is Erlang Terms", context do + File.rm(valid_file_path()) + {:ok, nil} = @command.run([valid_file_path()], context[:opts]) + + {:ok, bin} = File.read(valid_file_path()) + map = :erlang.binary_to_term(bin) + assert Map.has_key?(map, :rabbitmq_version) + end + + defp valid_file_path(), do: "#{System.tmp_dir()}/definitions" +end diff --git a/deps/rabbitmq_cli/test/ctl/force_boot_command_test.exs b/deps/rabbitmq_cli/test/ctl/force_boot_command_test.exs new file mode 100644 index 0000000000..a33d7b2e89 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/force_boot_command_test.exs @@ -0,0 +1,63 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ForceBootCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ForceBootCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup _ do + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: 1000 + } + } + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: the rabbit app running on target node fails validation", context do + assert @command.validate_execution_environment([], context[:opts]) == + {:validation_failure, :rabbit_app_is_running} + end + + test "run: sets a force boot marker file on target node", context do + stop_rabbitmq_app() + on_exit(fn -> start_rabbitmq_app() end) + assert @command.run([], context[:opts]) == :ok + mnesia_dir = :rpc.call(get_rabbit_hostname(), :rabbit_mnesia, :dir, []) + + path = Path.join(mnesia_dir, "force_load") + assert File.exists?(path) + File.rm(path) + end + + test "run: if RABBITMQ_MNESIA_DIR is defined, creates a force boot marker file" do + node = :unknown@localhost + temp_dir = "#{Mix.Project.config()[:elixirc_paths]}/tmp" + File.mkdir_p!(temp_dir) + on_exit(fn -> File.rm_rf!(temp_dir) end) + System.put_env("RABBITMQ_MNESIA_DIR", temp_dir) + + assert @command.run([], %{node: node}) == :ok + assert File.exists?(Path.join(temp_dir, "force_load")) + + System.delete_env("RABBITMQ_MNESIA_DIR") + File.rm_rf(temp_dir) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/force_gc_command_test.exs b/deps/rabbitmq_cli/test/ctl/force_gc_command_test.exs new file mode 100644 index 0000000000..b9583931d3 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/force_gc_command_test.exs @@ -0,0 +1,46 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ForceGcCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ForceGcCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + reset_vm_memory_high_watermark() + + on_exit([], fn -> + reset_vm_memory_high_watermark() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 200}} + end + + + test "merge_defaults: merge not defaults" do + assert @command.merge_defaults([], %{}) == {[], %{}} + end + + test "validate: with extra arguments returns an error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: request to a non-existent node returns a badrpc" do + assert match?({:badrpc, _}, @command.run([], %{node: :jake@thedog, timeout: 200})) + end + + test "run: request to a named, active node succeeds", context do + assert @command.run([], context[:opts]) == :ok + end +end diff --git a/deps/rabbitmq_cli/test/ctl/force_reset_command_test.exs b/deps/rabbitmq_cli/test/ctl/force_reset_command_test.exs new file mode 100644 index 0000000000..5b695302f4 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/force_reset_command_test.exs @@ -0,0 +1,68 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ForceResetCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ForceResetCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: force reset request to an active node with a stopped rabbit app succeeds", context do + add_vhost "some_vhost" + # ensure the vhost really does exist + assert vhost_exists? "some_vhost" + stop_rabbitmq_app() + assert :ok == @command.run([], context[:opts]) + start_rabbitmq_app() + # check that the created vhost no longer exists + assert match?([_], list_vhosts()) + end + + test "run: reset request to an active node with a running rabbit app fails", context do + add_vhost "some_vhost" + assert vhost_exists? "some_vhost" + assert match?({:error, :mnesia_unexpectedly_running}, @command.run([], context[:opts])) + assert vhost_exists? "some_vhost" + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Forcefully resetting node #{get_rabbit_hostname()}/ + end + + test "output mnesia is running error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, + "Mnesia is still running on node " <> _}, + @command.output({:error, :mnesia_unexpectedly_running}, context[:opts])) + + end +end diff --git a/deps/rabbitmq_cli/test/ctl/forget_cluster_node_command_test.exs b/deps/rabbitmq_cli/test/ctl/forget_cluster_node_command_test.exs new file mode 100644 index 0000000000..0f09e4fee8 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/forget_cluster_node_command_test.exs @@ -0,0 +1,132 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ForgetClusterNodeCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ForgetClusterNodeCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + node = get_rabbit_hostname() + + start_rabbitmq_app() + + {:ok, plugins_dir} = + :rabbit_misc.rpc_call(node, :application, :get_env, [:rabbit, :plugins_dir]) + + rabbitmq_home = :rabbit_misc.rpc_call(node, :code, :lib_dir, [:rabbit]) + mnesia_dir = :rabbit_misc.rpc_call(node, :rabbit_mnesia, :dir, []) + + feature_flags_file = + :rabbit_misc.rpc_call(node, :rabbit_feature_flags, :enabled_feature_flags_list_file, []) + + on_exit([], fn -> + start_rabbitmq_app() + end) + + {:ok, + opts: %{ + rabbitmq_home: rabbitmq_home, + plugins_dir: plugins_dir, + mnesia_dir: mnesia_dir, + feature_flags_file: feature_flags_file, + offline: false + }} + end + + setup context do + {:ok, opts: Map.merge(context[:opts], %{node: get_rabbit_hostname()})} + end + + test "validate: specifying no target node is reported as an error", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate: specifying multiple target nodes is reported as an error", context do + assert @command.validate(["a", "b", "c"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate_execution_environment: offline request to a running node fails", context do + assert match?( + {:validation_failure, :node_running}, + @command.validate_execution_environment( + ["other_node@localhost"], + Map.merge(context[:opts], %{offline: true}) + ) + ) + end + + test "validate_execution_environment: offline forget without mnesia dir fails", context do + offline_opts = + Map.merge( + context[:opts], + %{offline: true, node: :non_exist@localhost} + ) + + opts_without_mnesia = Map.delete(offline_opts, :mnesia_dir) + Application.put_env(:mnesia, :dir, "/tmp") + on_exit(fn -> Application.delete_env(:mnesia, :dir) end) + + assert match?( + :ok, + @command.validate_execution_environment( + ["other_node@localhost"], + opts_without_mnesia + ) + ) + + Application.delete_env(:mnesia, :dir) + System.put_env("RABBITMQ_MNESIA_DIR", "/tmp") + on_exit(fn -> System.delete_env("RABBITMQ_MNESIA_DIR") end) + + assert match?( + :ok, + @command.validate_execution_environment( + ["other_node@localhost"], + opts_without_mnesia + ) + ) + + System.delete_env("RABBITMQ_MNESIA_DIR") + + assert match?( + :ok, + @command.validate_execution_environment(["other_node@localhost"], offline_opts) + ) + end + + test "validate_execution_environment: online mode does not fail is mnesia is not loaded", + context do + opts_without_mnesia = Map.delete(context[:opts], :mnesia_dir) + + assert match?( + :ok, + @command.validate_execution_environment( + ["other_node@localhost"], + opts_without_mnesia + ) + ) + end + + test "run: online request to a non-existent node returns a badrpc", context do + assert match?( + {:badrpc, :nodedown}, + @command.run( + [context[:opts][:node]], + Map.merge(context[:opts], %{node: :non_exist@localhost}) + ) + ) + end + + test "banner", context do + assert @command.banner(["a"], context[:opts]) =~ + ~r/Removing node a from the cluster/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/help_command_test.exs b/deps/rabbitmq_cli/test/ctl/help_command_test.exs new file mode 100644 index 0000000000..d30a4d98c7 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/help_command_test.exs @@ -0,0 +1,76 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule HelpCommandTest do + use ExUnit.Case, async: false + import TestHelper + + alias RabbitMQ.CLI.Core.{CommandModules} + + @command RabbitMQ.CLI.Ctl.Commands.HelpCommand + + setup_all do + set_scope(:all) + :ok + end + + test "validate: providing no position arguments passes validation" do + assert @command.validate([], %{}) == :ok + end + + test "validate: providing one position argument passes validation" do + assert @command.validate(["status"], %{}) == :ok + end + + test "validate: providing two or more position arguments fails validation" do + assert @command.validate(["extra1", "extra2"], %{}) == + {:validation_failure, :too_many_args} + end + + test "run: prints basic usage info" do + {:ok, lines} = @command.run([], %{}) + output = Enum.join(lines, "\n") + assert output =~ ~r/[-n <node>] [-t <timeout>]/ + assert output =~ ~r/commands/i + end + + test "run: ctl command usage info is printed if command is specified" do + ctl_commands = CommandModules.module_map + |> Enum.filter(fn({_name, command_mod}) -> + to_string(command_mod) =~ ~r/^RabbitMQ\.CLI\.Ctl\.Commands/ + end) + |> Enum.map(fn({name, _}) -> name end) + + IO.inspect(ctl_commands) + Enum.each( + ctl_commands, + fn(command) -> + assert @command.run([command], %{}) =~ ~r/#{command}/ + end) + end + + test "run prints command info" do + ctl_commands = CommandModules.module_map + |> Enum.filter(fn({_name, command_mod}) -> + to_string(command_mod) =~ ~r/^RabbitMQ\.CLI\.Ctl\.Commands/ + end) + |> Enum.map(fn({name, _}) -> name end) + + Enum.each( + ctl_commands, + fn(command) -> + {:ok, lines} = @command.run([], %{}) + output = Enum.join(lines, "\n") + assert output =~ ~r/\n\s+#{command}.*\n/ + end) + end + + test "run: exits with the code of OK" do + assert @command.output({:ok, "Help string"}, %{}) == + {:ok, "Help string"} + end +end diff --git a/deps/rabbitmq_cli/test/ctl/import_definitions_command_test.exs b/deps/rabbitmq_cli/test/ctl/import_definitions_command_test.exs new file mode 100644 index 0000000000..fb7f975ec5 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/import_definitions_command_test.exs @@ -0,0 +1,88 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ImportDefinitionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ImportDefinitionsCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + {:ok, opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || 30000, + format: context[:format] || "json" + }} + end + + test "merge_defaults: defaults to JSON for format" do + assert @command.merge_defaults([valid_file_path()], %{}) == + {[valid_file_path()], %{format: "json"}} + end + + test "merge_defaults: defaults to --silent if target is stdout" do + assert @command.merge_defaults(["-"], %{}) == {["-"], %{format: "json", silent: true}} + end + + test "merge_defaults: format is case insensitive" do + assert @command.merge_defaults([valid_file_path()], %{format: "JSON"}) == + {[valid_file_path()], %{format: "json"}} + assert @command.merge_defaults([valid_file_path()], %{format: "Erlang"}) == + {[valid_file_path()], %{format: "erlang"}} + end + + test "merge_defaults: format can be overridden" do + assert @command.merge_defaults([valid_file_path()], %{format: "erlang"}) == + {[valid_file_path()], %{format: "erlang"}} + end + + test "validate: accepts a file path argument", context do + assert @command.validate([valid_file_path()], context[:opts]) == :ok + end + + test "validate: unsupported format fails validation", context do + assert match?({:validation_failure, {:bad_argument, _}}, + @command.validate([valid_file_path()], Map.merge(context[:opts], %{format: "yolo"}))) + end + + test "validate: more than one positional argument fails validation", context do + assert @command.validate([valid_file_path(), "extra-arg"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: supports JSON and Erlang formats", context do + assert @command.validate([valid_file_path()], Map.merge(context[:opts], %{format: "json"})) == :ok + assert @command.validate([valid_file_path()], Map.merge(context[:opts], %{format: "erlang"})) == :ok + end + + @tag test_timeout: 3000 + test "run: targeting an unreachable node throws a badrpc", context do + result = @command.run([valid_file_path()], + %{node: :jake@thedog, + timeout: context[:test_timeout], + format: "json"}) + assert match?({:badrpc, _}, result) + end + + @tag format: "json" + test "run: imports definitions from a file", context do + assert :ok == @command.run([valid_file_path()], context[:opts]) + + # clean up the state we've modified + clear_parameter("/", "federation-upstream", "up-1") + end + + defp valid_file_path() do + Path.join([File.cwd!(), "test", "fixtures", "files", "definitions.json"]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/join_cluster_command_test.exs b/deps/rabbitmq_cli/test/ctl/join_cluster_command_test.exs new file mode 100644 index 0000000000..2a9c7ec861 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/join_cluster_command_test.exs @@ -0,0 +1,104 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule JoinClusterCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.JoinClusterCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{ + node: get_rabbit_hostname(), + disc: true, + ram: false, + }} + end + + test "validate: specifying both --disc and --ram is reported as invalid", context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate(["a"], Map.merge(context[:opts], %{disc: true, ram: true})) + ) + end + test "validate: specifying no target node is reported as an error", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + test "validate: specifying multiple target nodes is reported as an error", context do + assert @command.validate(["a", "b", "c"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + # TODO + #test "run: successful join as a disc node", context do + #end + + # TODO + #test "run: successful join as a RAM node", context do + #end + + test "run: joining self is invalid", context do + stop_rabbitmq_app() + assert match?( + {:error, :cannot_cluster_node_with_itself}, + @command.run([context[:opts][:node]], context[:opts])) + start_rabbitmq_app() + end + + # TODO + test "run: request to an active node fails", context do + assert match?( + {:error, :mnesia_unexpectedly_running}, + @command.run([context[:opts][:node]], context[:opts])) + end + + test "run: request to a non-existent node returns a badrpc", context do + opts = %{ + node: :jake@thedog, + disc: true, + ram: false, + timeout: 200 + } + assert match?( + {:badrpc, _}, + @command.run([context[:opts][:node]], opts)) + end + + test "run: joining a non-existent node returns a badrpc", context do + stop_rabbitmq_app() + assert match?( + {:badrpc_multi, _, [_]}, + @command.run([:jake@thedog], context[:opts])) + start_rabbitmq_app() + end + + test "banner", context do + assert @command.banner(["a"], context[:opts]) =~ + ~r/Clustering node #{get_rabbit_hostname()} with a/ + end + + test "output mnesia is running error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, + "Mnesia is still running on node " <> _}, + @command.output({:error, :mnesia_unexpectedly_running}, context[:opts])) + + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_bindings_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_bindings_command_test.exs new file mode 100644 index 0000000000..dae2377322 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_bindings_command_test.exs @@ -0,0 +1,85 @@ +defmodule ListBindingsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListBindingsCommand + @vhost "test1" + @user "guest" + @default_timeout :infinity + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(fn -> + delete_vhost @vhost + end) + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || @default_timeout, + vhost: @vhost + } + } + end + + test "merge_defaults: adds all keys if none specificed", context do + default_keys = ~w(source_name source_kind destination_name destination_kind routing_key arguments) + declare_queue("test_queue", @vhost) + :timer.sleep(100) + + {keys, _} = @command.merge_defaults([], context[:opts]) + assert default_keys == keys + end + + test "merge_defaults: includes table headers by default", _context do + {_, opts} = @command.merge_defaults([], %{}) + assert opts[:table_headers] + end + + test "validate: returns bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: returns multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "source_name"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["source_name", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["source_kind", "oink", "source_name"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + @tag test_timeout: 0 + test "run: timeout causes command to return badrpc", context do + assert run_command_to_list(@command, [["source_name"], context[:opts]]) == + [{:badrpc, {:timeout, 0.0}}] + end + + test "run: no bindings for no queues", context do + [] = run_command_to_list(@command, [["source_name"], context[:opts]]) + end + + test "run: can filter info keys", context do + wanted_keys = ~w(source_name destination_name routing_key) + declare_queue("test_queue", @vhost) + assert run_command_to_list(@command, [wanted_keys, context[:opts]]) == + [[source_name: "", destination_name: "test_queue", routing_key: "test_queue"]] + end + + test "banner" do + assert String.starts_with?(@command.banner([], %{vhost: "some_vhost"}), "Listing bindings") + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_channels_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_channels_command_test.exs new file mode 100644 index 0000000000..6ccf602211 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_channels_command_test.exs @@ -0,0 +1,118 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ListChannelsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListChannelsCommand + @default_timeout :infinity + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + close_all_connections(get_rabbit_hostname()) + + on_exit([], fn -> + close_all_connections(get_rabbit_hostname()) + end) + + :ok + end + + setup context do + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || @default_timeout + } + } + end + + test "merge_defaults: default channel info keys are pid, user, consumer_count, and messages_unacknowledged", context do + assert match?({~w(pid user consumer_count messages_unacknowledged), _}, @command.merge_defaults([], context[:opts])) + end + + test "validate: returns bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: returns multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: returns bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "pid"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["user", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["user", "oink", "pid"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + @tag test_timeout: 0 + test "run: zero timeout causes command to return badrpc", context do + assert run_command_to_list(@command, [["user"], context[:opts]]) == + [{:badrpc, {:timeout, 0.0}}] + end + + test "run: multiple channels on multiple connections", context do + node_name = get_rabbit_hostname() + close_all_connections(node_name) + existent_channels = :rabbit_misc.rpc_call(node_name,:rabbit_channel, :list, []) + with_channel("/", fn(_channel1) -> + with_channel("/", fn(_channel2) -> + all_channels = run_command_to_list(@command, [["pid", "user", "connection"], context[:opts]]) + channels = Enum.filter(all_channels, + fn(ch) -> + not Enum.member?(existent_channels, ch[:pid]) + end) + chan1 = Enum.at(channels, 0) + chan2 = Enum.at(channels, 1) + assert Keyword.keys(chan1) == ~w(pid user connection)a + assert Keyword.keys(chan2) == ~w(pid user connection)a + assert "guest" == chan1[:user] + assert "guest" == chan2[:user] + assert chan1[:pid] !== chan2[:pid] + end) + end) + end + + test "run: multiple channels on single connection", context do + node_name = get_rabbit_hostname() + close_all_connections(get_rabbit_hostname()) + with_connection("/", fn(conn) -> + existent_channels = :rabbit_misc.rpc_call(node_name,:rabbit_channel, :list, []) + {:ok, _} = AMQP.Channel.open(conn) + {:ok, _} = AMQP.Channel.open(conn) + all_channels = run_command_to_list(@command, [["pid", "user", "connection"], context[:opts]]) + channels = Enum.filter(all_channels, + fn(ch) -> + not Enum.member?(existent_channels, ch[:pid]) + end) + + chan1 = Enum.at(channels, 0) + chan2 = Enum.at(channels, 1) + assert Keyword.keys(chan1) == ~w(pid user connection)a + assert Keyword.keys(chan2) == ~w(pid user connection)a + assert "guest" == chan1[:user] + assert "guest" == chan2[:user] + assert chan1[:pid] !== chan2[:pid] + end) + end + + test "run: info keys order is preserved", context do + close_all_connections(get_rabbit_hostname()) + with_channel("/", fn(_channel) -> + channels = run_command_to_list(@command, [~w(connection vhost name pid number user), context[:opts]]) + chan = Enum.at(channels, 0) + assert Keyword.keys(chan) == ~w(connection vhost name pid number user)a + end) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_ciphers_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_ciphers_command_test.exs new file mode 100644 index 0000000000..6f600ba5d8 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_ciphers_command_test.exs @@ -0,0 +1,29 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ListCiphersCommandTest do + use ExUnit.Case + @command RabbitMQ.CLI.Ctl.Commands.ListCiphersCommand + + test "merge_defaults: nothing to do" do + assert @command.merge_defaults([], %{}) == {[], %{}} + end + + test "validate: treats positional arguments as a failure" do + assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: treats empty positional arguments and default switches as a success" do + assert @command.validate([], %{}) == :ok + end + + test "run: lists ciphers", _context do + assert match?( + {:ok, _}, + @command.run([], %{}) + ) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_connections_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_connections_command_test.exs new file mode 100644 index 0000000000..9cfcb8787f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_connections_command_test.exs @@ -0,0 +1,90 @@ +defmodule ListConnectionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListConnectionsCommand + @user "guest" + @default_timeout 15000 + @default_options %{table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + close_all_connections(get_rabbit_hostname()) + + on_exit([], fn -> + close_all_connections(get_rabbit_hostname()) + end) + + :ok + end + + setup context do + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || @default_timeout + } + } + end + + test "merge_defaults: user, peer_host, peer_port and state by default" do + assert @command.merge_defaults([], %{}) == {~w(user peer_host peer_port state), @default_options} + end + + test "merge_defaults: includes table headers by default", _context do + {_, opts} = @command.merge_defaults([], %{}) + assert opts[:table_headers] + end + + test "validate: returns bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "peer_host"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["user", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["user", "oink", "peer_host"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + @tag test_timeout: 0 + test "run: timeout causes command to return badrpc", context do + assert run_command_to_list(@command, [["name"], context[:opts]]) == + [{:badrpc, {:timeout, 0.0}}] + end + + test "run: filter single key", context do + vhost = "/" + with_connection(vhost, fn(_conn) -> + conns = run_command_to_list(@command, [["name"], context[:opts]]) + assert (Enum.map(conns, &Keyword.keys/1) |> Enum.uniq) == [[:name]] + assert Enum.any?(conns, fn(conn) -> conn[:name] != nil end) + end) + end + + test "run: show connection vhost", context do + vhost = "custom_vhost" + add_vhost vhost + set_permissions @user, vhost, [".*", ".*", ".*"] + on_exit(fn -> + delete_vhost vhost + end) + with_connection(vhost, fn(_conn) -> + conns = run_command_to_list(@command, [["vhost"], context[:opts]]) + assert (Enum.map(conns, &Keyword.keys/1) |> Enum.uniq) == [[:vhost]] + assert Enum.any?(conns, fn(conn) -> conn[:vhost] == vhost end) + end) + end + + +end diff --git a/deps/rabbitmq_cli/test/ctl/list_consumers_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_consumers_command_test.exs new file mode 100644 index 0000000000..d49313162a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_consumers_command_test.exs @@ -0,0 +1,213 @@ +defmodule ListConsumersCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListConsumersCommand + + @vhost "test1" + @user "guest" + @default_timeout :infinity + @info_keys ~w(queue_name channel_pid consumer_tag ack_required prefetch_count active arguments) + @default_options %{vhost: "/", table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(fn -> + delete_vhost @vhost + end) + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || @default_timeout, + vhost: @vhost + } + } + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {@info_keys, @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {@info_keys, %{vhost: "non_default", + table_headers: true}} + end + + test "validate: returns bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: returns multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "queue_name"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["queue_name", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["channel_pid", "oink", "queue_name"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + @tag test_timeout: 0 + test "run: zero timeout causes command to return badrpc", context do + assert run_command_to_list(@command, [["queue_name"], context[:opts]]) == + [{:badrpc, {:timeout, 0.0}}] + end + + test "run: no consumers for no open connections", context do + close_all_connections(get_rabbit_hostname()) + [] = run_command_to_list(@command, [["queue_name"], context[:opts]]) + end + + test "run: defaults test", context do + queue_name = "test_queue1" + consumer_tag = "i_am_consumer" + info_keys_s = ~w(queue_name channel_pid consumer_tag ack_required prefetch_count arguments) + info_keys_a = Enum.map(info_keys_s, &String.to_atom/1) + declare_queue(queue_name, @vhost) + with_channel(@vhost, fn(channel) -> + {:ok, _} = AMQP.Basic.consume(channel, queue_name, nil, [consumer_tag: consumer_tag]) + :timer.sleep(100) + [[consumer]] = run_command_to_list(@command, [info_keys_s, context[:opts]]) + assert info_keys_a == Keyword.keys(consumer) + assert consumer[:consumer_tag] == consumer_tag + assert consumer[:queue_name] == queue_name + assert Keyword.delete(consumer, :channel_pid) == + [queue_name: queue_name, consumer_tag: consumer_tag, + ack_required: true, prefetch_count: 0, arguments: []] + + end) + end + + test "run: consumers are grouped by queues (multiple consumer per queue)", context do + queue_name1 = "test_queue1" + queue_name2 = "test_queue2" + declare_queue("test_queue1", @vhost) + declare_queue("test_queue2", @vhost) + with_channel(@vhost, fn(channel) -> + {:ok, tag1} = AMQP.Basic.consume(channel, queue_name1) + {:ok, tag2} = AMQP.Basic.consume(channel, queue_name2) + {:ok, tag3} = AMQP.Basic.consume(channel, queue_name2) + :timer.sleep(100) + try do + consumers = run_command_to_list(@command, [["queue_name", "consumer_tag"], context[:opts]]) + {[[consumer1]], [consumers2]} = Enum.split_with(consumers, fn([_]) -> true; ([_,_]) -> false end) + assert [queue_name: queue_name1, consumer_tag: tag1] == consumer1 + assert Keyword.equal?([{tag2, queue_name2}, {tag3, queue_name2}], + for([queue_name: q, consumer_tag: t] <- consumers2, do: {t, q})) + after + AMQP.Basic.cancel(channel, tag1) + AMQP.Basic.cancel(channel, tag2) + AMQP.Basic.cancel(channel, tag3) + end + end) + end + + test "run: active and activity status fields are set properly when requested", context do + queue_types = ["classic", "quorum"] + Enum.each queue_types, fn queue_type -> + queue_name = "active-activity-status-fields-" <> queue_type + declare_queue(queue_name, @vhost, true, false, [{"x-queue-type", :longstr, queue_type}]) + :timer.sleep(200) + with_channel(@vhost, fn(channel) -> + {:ok, tag1} = AMQP.Basic.consume(channel, queue_name) + {:ok, tag2} = AMQP.Basic.consume(channel, queue_name) + {:ok, tag3} = AMQP.Basic.consume(channel, queue_name) + :timer.sleep(100) + try do + consumers = List.first(run_command_to_list(@command, [["queue_name", "consumer_tag", "active", "activity_status"], context[:opts]])) + assert Keyword.equal?([{tag1, queue_name, true, :up}, + {tag2, queue_name, true, :up}, {tag3, queue_name, true, :up}], + for([queue_name: q, consumer_tag: t, active: a, activity_status: as] <- consumers, do: {t, q, a, as})) + after + AMQP.Basic.cancel(channel, tag1) + AMQP.Basic.cancel(channel, tag2) + AMQP.Basic.cancel(channel, tag3) + :timer.sleep(100) + delete_queue(queue_name, @vhost) + end + end) + end + end + + test "run: active and activity status fields are set properly when requested and single active consumer is enabled", context do + queue_types = ["classic", "quorum"] + Enum.each queue_types, fn queue_type -> + queue_name = "single-active-consumer-" <> queue_type + declare_queue(queue_name, @vhost, true, false, + [{"x-single-active-consumer", :bool, true}, {"x-queue-type", :longstr, queue_type}]) + :timer.sleep(200) + with_channel(@vhost, fn(channel) -> + {:ok, tag1} = AMQP.Basic.consume(channel, queue_name) + {:ok, tag2} = AMQP.Basic.consume(channel, queue_name) + {:ok, tag3} = AMQP.Basic.consume(channel, queue_name) + :timer.sleep(100) + try do + consumers = List.first(run_command_to_list(@command, [["queue_name", "consumer_tag", "active", "activity_status"], context[:opts]])) + assert Keyword.equal?([{tag1, queue_name, true, :single_active}, + {tag2, queue_name, false, :waiting}, {tag3, queue_name, false, :waiting}], + for([queue_name: q, consumer_tag: t, active: a, activity_status: as] <- consumers, do: {t, q, a, as})) + AMQP.Basic.cancel(channel, tag1) + :timer.sleep(100) + consumers = List.first(run_command_to_list(@command, [["queue_name", "consumer_tag", "active", "activity_status"], context[:opts]])) + assert Keyword.equal?([{tag2, queue_name, true, :single_active}, {tag3, queue_name, false, :waiting}], + for([queue_name: q, consumer_tag: t, active: a, activity_status: as] <- consumers, do: {t, q, a, as})) + after + AMQP.Basic.cancel(channel, tag2) + AMQP.Basic.cancel(channel, tag3) + :timer.sleep(100) + delete_queue(queue_name, @vhost) + end + end) + end + end + + test "fill_consumer_active_fields: add missing fields if necessary" do + consumer38 = [ + queue_name: {:resource, "/", :queue, "queue1"}, + channel_pid: "", + consumer_tag: "ctag1", + ack_required: false, + prefetch_count: 0, + active: true, + activity_status: :up, + arguments: [] + ] + assert @command.fill_consumer_active_fields({[ + consumer38 + ], {1, :continue}}) == {[consumer38], {1, :continue}} + + assert @command.fill_consumer_active_fields({[ + [ + queue_name: {:resource, "/", :queue, "queue2"}, + channel_pid: "", + consumer_tag: "ctag2", + ack_required: false, + prefetch_count: 0, + arguments: [] + ] + ], {1, :continue}}) == {[ + [ + queue_name: {:resource, "/", :queue, "queue2"}, + channel_pid: "", + consumer_tag: "ctag2", + ack_required: false, + prefetch_count: 0, + active: true, + activity_status: :up, + arguments: [] + ] + ], {1, :continue}} + + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/list_exchanges_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_exchanges_command_test.exs new file mode 100644 index 0000000000..fd89cfd066 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_exchanges_command_test.exs @@ -0,0 +1,160 @@ +defmodule ListExchangesCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListExchangesCommand + + @vhost "test1" + @user "guest" + @default_timeout :infinity + @default_exchanges [{"amq.direct", :direct}, + {"amq.fanout", :fanout}, + {"amq.match", :headers}, + {"amq.rabbitmq.trace", :topic}, + {"amq.headers", :headers}, + {"amq.topic", :topic}, + {"", :direct}] + @default_options %{vhost: "/", table_headers: true} + + defp default_exchange_names() do + {names, _types} = Enum.unzip(@default_exchanges) + names + end + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(fn -> + delete_vhost @vhost + end) + { + :ok, + opts: %{ + quiet: true, + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || @default_timeout, + vhost: @vhost + } + } + end + + test "merge_defaults: should include name and type when no arguments provided and add default vhost to opts" do + assert @command.merge_defaults([], %{}) + == {["name", "type"], @default_options} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {["name", "type"], @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {["name", "type"], %{vhost: "non_default", + table_headers: true}} + end + + test "validate: returns bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: returns multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "type"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["name", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["name", "oink", "type"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + @tag test_timeout: 0 + test "run: zero timeout causes command to return badrpc", context do + assert run_command_to_list(@command, [["name"], context[:opts]]) == + [{:badrpc, {:timeout, 0.0}}] + end + + test "run: show default exchanges by default", context do + assert MapSet.new(run_command_to_list(@command, [["name"], context[:opts]])) == + MapSet.new(for {ex_name, _ex_type} <- @default_exchanges, do: [name: ex_name]) + end + + test "run: default options test", context do + exchange_name = "test_exchange" + declare_exchange(exchange_name, @vhost) + assert MapSet.new(run_command_to_list(@command, [["name", "type"], context[:opts]])) == + MapSet.new( + for({ex_name, ex_type} <- @default_exchanges, do: [name: ex_name, type: ex_type]) ++ + [[name: exchange_name, type: :direct]]) + end + + test "run: list multiple exchanges", context do + declare_exchange("test_exchange_1", @vhost, :direct) + declare_exchange("test_exchange_2", @vhost, :fanout) + non_default_exchanges = run_command_to_list(@command, [["name", "type"], context[:opts]]) + |> without_default_exchanges + assert_set_equal( + non_default_exchanges, + [[name: "test_exchange_1", type: :direct], + [name: "test_exchange_2", type: :fanout]]) + end + + def assert_set_equal(one, two) do + assert MapSet.new(one) == MapSet.new(two) + end + + test "run: info keys filter single key", context do + declare_exchange("test_exchange_1", @vhost) + declare_exchange("test_exchange_2", @vhost) + non_default_exchanges = run_command_to_list(@command, [["name"], context[:opts]]) + |> without_default_exchanges + assert_set_equal( + non_default_exchanges, + [[name: "test_exchange_1"], + [name: "test_exchange_2"]]) + end + + + test "run: info keys add additional keys", context do + declare_exchange("durable_exchange", @vhost, :direct, true) + declare_exchange("auto_delete_exchange", @vhost, :fanout, false, true) + non_default_exchanges = run_command_to_list(@command, [["name", "type", "durable", "auto_delete"], context[:opts]]) + |> without_default_exchanges + assert_set_equal( + non_default_exchanges, + [[name: "auto_delete_exchange", type: :fanout, durable: false, auto_delete: true], + [name: "durable_exchange", type: :direct, durable: true, auto_delete: false]]) + end + + test "run: specifying a vhost returns the targeted vhost exchanges", context do + other_vhost = "other_vhost" + add_vhost other_vhost + on_exit(fn -> + delete_vhost other_vhost + end) + declare_exchange("test_exchange_1", @vhost) + declare_exchange("test_exchange_2", other_vhost) + non_default_exchanges1 = run_command_to_list(@command, [["name"], context[:opts]]) + |> without_default_exchanges + + non_default_exchanges2 = run_command_to_list(@command, [["name"], %{context[:opts] | :vhost => other_vhost}]) + |> without_default_exchanges + + assert non_default_exchanges1 == [[name: "test_exchange_1"]] + assert non_default_exchanges2 == [[name: "test_exchange_2"]] + end + + defp without_default_exchanges(xs) do + Enum.filter(xs, + fn(x) -> + not Enum.member?(default_exchange_names(), x[:name]) + end) + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/list_feature_flags_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_feature_flags_command_test.exs new file mode 100644 index 0000000000..b2cf1ad52a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_feature_flags_command_test.exs @@ -0,0 +1,122 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2018-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ListFeatureFlagsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListFeatureFlagsCommand + + @flag1 :ff1_from_list_ff_testsuite + @flag2 :ff2_from_list_ff_testsuite + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + # Define an arbitrary feature flag for the test. + node = get_rabbit_hostname() + new_feature_flags = %{ + @flag1 => + %{desc: "My feature flag #1", + provided_by: :ListFeatureFlagsCommandTest, + stability: :stable}, + @flag2 => + %{desc: "My feature flag #2", + provided_by: :ListFeatureFlagsCommandTest, + stability: :stable}} + :ok = :rabbit_misc.rpc_call( + node, :rabbit_feature_flags, :initialize_registry, [new_feature_flags]) + :ok = :rabbit_misc.rpc_call( + node, :rabbit_feature_flags, :enable_all, []) + + name_result = [ + [{:name, @flag1}], + [{:name, @flag2}] + ] + + full_result = [ + [{:name, @flag1}, {:state, :enabled}], + [{:name, @flag2}, {:state, :enabled}] + ] + + { + :ok, + name_result: name_result, + full_result: full_result + } + end + + setup context do + { + :ok, + opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]} + } + end + + test "merge_defaults with no command, print just use the names" do + assert match?({["name", "state"], %{}}, @command.merge_defaults([], %{})) + end + + test "validate: return bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "name"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["name", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["name", "oink", "state"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + test "run: on a bad RabbitMQ node, return a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run(["name"], opts)) + end + + @tag test_timeout: :infinity + test "run: with the name tag, print just the names", context do + matches_found = @command.run(["name"], context[:opts]) + assert Enum.all?(context[:name_result], fn(feature_name) -> + Enum.find(matches_found, fn(found) -> found == feature_name end) + end) + end + + @tag test_timeout: :infinity + test "run: duplicate args do not produce duplicate entries", context do + # checks to ensure that all expected feature flags are in the results + matches_found = @command.run(["name", "name"], context[:opts]) + assert Enum.all?(context[:name_result], fn(feature_name) -> + Enum.find(matches_found, fn(found) -> found == feature_name end) + end) + end + + @tag test_timeout: 30000 + test "run: sufficiently long timeouts don't interfere with results", context do + matches_found = @command.run(["name", "state"], context[:opts]) + assert Enum.all?(context[:full_result], fn(feature_name) -> + Enum.find(matches_found, fn(found) -> found == feature_name end) + end) + end + + @tag test_timeout: 0, username: "guest" + test "run: timeout causes command to return a bad RPC", context do + assert @command.run(["name", "state"], context[:opts]) == + {:badrpc, :timeout} + end + + @tag test_timeout: :infinity + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Listing feature flags \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_global_parameters_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_global_parameters_command_test.exs new file mode 100644 index 0000000000..eabd6a3628 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_global_parameters_command_test.exs @@ -0,0 +1,86 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListGlobalParametersCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListGlobalParametersCommand + + @key :mqtt_default_vhosts + @value "{\"O=client,CN=dummy\":\"somevhost\"}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + :ok + end + + setup context do + on_exit(fn -> + clear_global_parameter context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: (context[:timeout] || :infinity), + } + } + end + + test "validate: wrong number of arguments leads to an arg count error" do + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag key: @key, value: @value + test "run: a well-formed command returns list of global parameters", context do + set_global_parameter(context[:key], @value) + @command.run([], context[:opts]) + |> assert_parameter_list(context) + end + + @tag key: @key, value: @value + test "run: zero timeout return badrpc", context do + set_global_parameter(context[:key], @value) + assert @command.run([], Map.put(context[:opts], :timeout, 0)) == {:badrpc, :timeout} + end + + test "run: multiple parameters returned in list", context do + initial = for param <- @command.run([], context[:opts]), do: Map.new(param) + parameters = [ + %{name: :global_param_1, value: "{\"key1\":\"value1\"}"}, + %{name: :global_param_2, value: "{\"key2\":\"value2\"}"} + ] + + + Enum.each(parameters, fn(%{name: name, value: value}) -> + set_global_parameter(name, value) + on_exit(fn -> + clear_global_parameter(name) + end) + end) + + parameters = initial ++ parameters + params = for param <- @command.run([], context[:opts]), do: Map.new(param) + + assert MapSet.new(params) == MapSet.new(parameters) + end + + @tag key: @key, value: @value + test "banner", context do + assert @command.banner([], context[:opts]) + =~ ~r/Listing global runtime parameters \.\.\./ + end + + # Checks each element of the first parameter against the expected context values + defp assert_parameter_list(params, context) do + [param | _] = params + assert MapSet.new(param) == MapSet.new([name: context[:key], + value: context[:value]]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_hashes_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_hashes_command_test.exs new file mode 100644 index 0000000000..2869479a8a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_hashes_command_test.exs @@ -0,0 +1,29 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ListHashesCommandTest do + use ExUnit.Case + @command RabbitMQ.CLI.Ctl.Commands.ListHashesCommand + + test "merge_defaults: nothing to do" do + assert @command.merge_defaults([], %{}) == {[], %{}} + end + + test "validate: treats positional arguments as a failure" do + assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: treats empty positional arguments and default switches as a success" do + assert @command.validate([], %{}) == :ok + end + + test "run: lists hashes", _context do + assert match?( + {:ok, _}, + @command.run([], %{}) + ) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_operator_policies_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_operator_policies_command_test.exs new file mode 100644 index 0000000000..6c86fe8441 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_operator_policies_command_test.exs @@ -0,0 +1,142 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListOperatorPoliciesCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListOperatorPoliciesCommand + + @vhost "test1" + @root "/" + @key "message-expiry" + @pattern "^queue\." + @value "{\"message-ttl\":10}" + @apply_to "all" + @default_options %{vhost: "/", table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit(fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + on_exit(fn -> + clear_operator_policy context[:vhost], context[:key] + end) + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: (context[:timeout] || :infinity), + vhost: context[:vhost], + apply_to: @apply_to, + priority: 0 + } + } + end + + test "merge_defaults: default vhost is '/'" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default", + table_headers: true}} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "run: a well-formed, host-specific command returns list of policies", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + set_operator_policy(context[:vhost], context[:key], context[:pattern], @value) + @command.run([], vhost_opts) + |> assert_operator_policy_list(context) + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @root + test "run: a well-formed command with no vhost runs against the default one", context do + + set_operator_policy("/", context[:key], context[:pattern], @value) + on_exit(fn -> + clear_operator_policy("/", context[:key]) + end) + + @command.run([], context[:opts]) + |> assert_operator_policy_list(context) + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "run: providing a timeout of 0 returns a badrpc", context do + set_operator_policy(context[:vhost], context[:key], context[:pattern], @value) + assert @command.run([], Map.put(context[:opts], :timeout, 0)) == {:badrpc, :timeout} + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: "bad-vhost" + test "run: providing a non-existent vhost returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag vhost: @vhost + test "run: when multiple policies exist in the vhost, returns them all", context do + policies = [ + %{vhost: @vhost, name: "some-policy", pattern: "foo", definition: "{\"message-ttl\":10}", 'apply-to': "all", priority: 0}, + %{vhost: @vhost, name: "other-policy", pattern: "bar", definition: "{\"expires\":20}", 'apply-to': "all", priority: 0} + ] + policies + |> Enum.map( + fn(%{name: name, pattern: pattern, definition: value}) -> + set_operator_policy(context[:vhost], name, pattern, value) + on_exit(fn -> + clear_operator_policy(context[:vhost], name) + end) + end) + + pols = for policy <- @command.run([], context[:opts]), do: Map.new(policy) + + assert MapSet.new(pols) == MapSet.new(policies) + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([], vhost_opts) + =~ ~r/Listing operator policy overrides for vhost \"#{context[:vhost]}\" \.\.\./ + end + + # Checks each element of the first policy against the expected context values + defp assert_operator_policy_list(policies, context) do + [policy] = policies + assert MapSet.new(policy) == MapSet.new([name: context[:key], + pattern: context[:pattern], + definition: context[:value], + vhost: context[:vhost], + priority: context[:opts][:priority], + "apply-to": context[:opts][:apply_to]]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs new file mode 100644 index 0000000000..f42e55353a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs @@ -0,0 +1,154 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListParametersCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListParametersCommand + + @vhost "test1" + @root "/" + @component_name "federation-upstream" + @key "reconnect-delay" + @value "{\"uri\":\"amqp://\"}" + @default_options %{vhost: "/", table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + node = get_rabbit_hostname() + + {:ok, plugins_file} = :rabbit_misc.rpc_call(node, + :application, :get_env, + [:rabbit, :enabled_plugins_file]) + {:ok, plugins_dir} = :rabbit_misc.rpc_call(node, + :application, :get_env, + [:rabbit, :plugins_dir]) + rabbitmq_home = :rabbit_misc.rpc_call(node, :code, :lib_dir, [:rabbit]) + + {:ok, [enabled_plugins]} = :file.consult(plugins_file) + + opts = %{enabled_plugins_file: plugins_file, + plugins_dir: plugins_dir, + rabbitmq_home: rabbitmq_home} + + set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation], :online, node, opts) + + add_vhost @vhost + + enable_federation_plugin() + + on_exit(fn -> + set_enabled_plugins(enabled_plugins, :online, get_rabbit_hostname(), opts) + delete_vhost @vhost + end) + + :ok + end + + setup context do + on_exit(fn -> + clear_parameter context[:vhost], context[:component_name], context[:key] + end) + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: (context[:timeout] || :infinity), + vhost: context[:vhost] + } + } + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default", + table_headers: true}} + end + + test "validate: wrong number of arguments leads to an arg count error" do + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @vhost + test "run: a well-formed, host-specific command returns list of parameters", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + set_parameter(context[:vhost], context[:component_name], context[:key], @value) + @command.run([], vhost_opts) + |> assert_parameter_list(context) + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @root + test "run: a well-formed command with no vhost runs against the default", context do + + set_parameter("/", context[:component_name], context[:key], @value) + on_exit(fn -> + clear_parameter("/", context[:component_name], context[:key]) + end) + + @command.run([], context[:opts]) + |> assert_parameter_list(context) + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @vhost + test "run: zero timeout return badrpc", context do + set_parameter(context[:vhost], context[:component_name], context[:key], @value) + assert @command.run([], Map.put(context[:opts], :timeout, 0)) == {:badrpc, :timeout} + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: "bad-vhost" + test "run: an invalid vhost returns a no-such-vhost error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag vhost: @vhost + test "run: multiple parameters returned in list", context do + parameters = [ + %{component: "federation-upstream", name: "my-upstream", value: "{\"uri\":\"amqp://\"}"}, + %{component: "exchange-delete-in-progress", name: "my-key", value: "{\"foo\":\"bar\"}"} + ] + parameters + |> Enum.map( + fn(%{component: component, name: name, value: value}) -> + set_parameter(context[:vhost], component, name, value) + on_exit(fn -> + clear_parameter(context[:vhost], component, name) + end) + end) + + params = for param <- @command.run([], context[:opts]), do: Map.new(param) + + assert MapSet.new(params) == MapSet.new(parameters) + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([], vhost_opts) + =~ ~r/Listing runtime parameters for vhost \"#{context[:vhost]}\" \.\.\./ + end + + # Checks each element of the first parameter against the expected context values + defp assert_parameter_list(params, context) do + [param] = params + assert MapSet.new(param) == MapSet.new([component: context[:component_name], + name: context[:key], + value: context[:value]]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_permissions_command_test.exs new file mode 100644 index 0000000000..eda8f001af --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_permissions_command_test.exs @@ -0,0 +1,92 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListPermissionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListPermissionsCommand + + @vhost "test1" + @user "guest" + @root "/" + @default_timeout :infinity + @default_options %{vhost: "/", table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + set_permissions @user, @vhost, ["^guest-.*", ".*", ".*"] + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout], + vhost: "/" + } + } + end + + test "merge_defaults adds default options" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default", + table_headers: true}} + end + + test "validate: invalid parameters yield an arg count error" do + assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: on a bad RabbitMQ node, return a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag test_timeout: @default_timeout, vhost: @vhost + test "run: specifying a vhost returns the targeted vhost permissions", context do + assert @command.run( + [], + Map.merge(context[:opts], %{vhost: @vhost}) + ) == [[user: "guest", configure: "^guest-.*", write: ".*", read: ".*"]] + end + + @tag test_timeout: 30000 + test "run: sufficiently long timeouts don't interfere with results", context do + results = @command.run([], context[:opts]) + Enum.all?([[user: "guest", configure: ".*", write: ".*", read: ".*"]], fn(perm) -> + Enum.find(results, fn(found) -> found == perm end) + end) + end + + @tag test_timeout: 0 + test "run: timeout causes command to return a bad RPC", context do + assert @command.run([], context[:opts]) == + {:badrpc, :timeout} + end + + @tag vhost: @root + test "banner", context do + ctx = Map.merge(context[:opts], %{vhost: @vhost}) + assert @command.banner([], ctx ) + =~ ~r/Listing permissions for vhost \"#{Regex.escape(ctx[:vhost])}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_policies_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_policies_command_test.exs new file mode 100644 index 0000000000..49ef6ee856 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_policies_command_test.exs @@ -0,0 +1,144 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListPoliciesCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListPoliciesCommand + + @vhost "test1" + @default_vhost "/" + @key "federate" + @pattern "^fed\." + @value "{\"federation-upstream-set\":\"all\"}" + @apply_to "all" + @default_options %{vhost: "/", table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + enable_federation_plugin() + + on_exit(fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + + on_exit(fn -> + clear_policy context[:vhost], context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: (context[:timeout] || :infinity), + vhost: context[:vhost], + apply_to: @apply_to, + priority: 0 + } + } + end + + test "merge_defaults: default vhost is '/'" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default", + table_headers: true}} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "run: a well-formed, host-specific command returns list of policies", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + set_policy(context[:vhost], context[:key], context[:pattern], @value) + @command.run([], vhost_opts) + |> assert_policy_list(context) + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @default_vhost + test "run: a well-formed command with no vhost runs against the default one", context do + set_policy("/", context[:key], context[:pattern], @value) + on_exit(fn -> + clear_policy("/", context[:key]) + end) + + @command.run([], context[:opts]) + |> assert_policy_list(context) + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "run: providing a timeout of 0 returns a badrpc", context do + set_policy(context[:vhost], context[:key], context[:pattern], @value) + assert @command.run([], Map.put(context[:opts], :timeout, 0)) == {:badrpc, :timeout} + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: "bad-vhost" + test "run: providing a non-existent vhost returns an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag vhost: @vhost + test "run: when multiple policies exist in the vhost, returns them all", context do + policies = [ + %{vhost: @vhost, name: "some-policy", pattern: "foo", definition: "{\"federation-upstream-set\":\"all\"}", 'apply-to': "all", priority: 0}, + %{vhost: @vhost, name: "other-policy", pattern: "bar", definition: "{\"ha-mode\":\"all\"}", 'apply-to': "all", priority: 0} + ] + policies + |> Enum.map( + fn(%{name: name, pattern: pattern, definition: value}) -> + set_policy(context[:vhost], name, pattern, value) + on_exit(fn -> + clear_policy(context[:vhost], name) + end) + end) + + pols = for policy <- @command.run([], context[:opts]), do: Map.new(policy) + + assert MapSet.new(pols) == MapSet.new(policies) + end + + @tag key: @key, pattern: @pattern, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([], vhost_opts) + =~ ~r/Listing policies for vhost \"#{context[:vhost]}\" \.\.\./ + end + + # Checks each element of the first policy against the expected context values + defp assert_policy_list(policies, context) do + [policy | _] = policies + assert MapSet.new(policy) == MapSet.new([name: context[:key], + pattern: context[:pattern], + definition: context[:value], + vhost: context[:vhost], + priority: context[:opts][:priority], + "apply-to": context[:opts][:apply_to]]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_queues_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_queues_command_test.exs new file mode 100644 index 0000000000..a6635c7933 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_queues_command_test.exs @@ -0,0 +1,145 @@ +defmodule ListQueuesCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListQueuesCommand + + @vhost "test1" + @user "guest" + @default_timeout 15000 + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + reset_vm_memory_high_watermark() + delete_all_queues() + close_all_connections(get_rabbit_hostname()) + + on_exit([], fn -> + delete_all_queues() + close_all_connections(get_rabbit_hostname()) + end) + + :ok + end + + setup context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(fn -> + delete_vhost @vhost + end) + { + :ok, + opts: %{ + quiet: true, + node: get_rabbit_hostname(), + timeout: context[:test_timeout] || @default_timeout, + vhost: @vhost, + offline: false, + online: false, + local: false + } + } + end + + test "merge_defaults: no info keys returns names and message count" do + assert match?({["name", "messages"], _}, @command.merge_defaults([], %{})) + end + + test "validate: returns bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "messages"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["name", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["name", "oink", "messages"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + @tag test_timeout: 0 + test "run: timeout causes command to return badrpc", context do + assert run_command_to_list(@command, [["name"], context[:opts]]) == + [{:badrpc, {:timeout, 0.0, "Some queue(s) are unresponsive, use list_unresponsive_queues command."}}] + end + + @tag test_timeout: 1 + test "run: command timeout (several thousands queues in 1ms) return badrpc with timeout value in seconds", context do + # we assume it will take longer than 1 ms to list thousands of queues + n = 5000 + for i <- 1..n do + declare_queue("test_queue_" <> Integer.to_string(i), @vhost) + end + assert run_command_to_list(@command, [["name"], context[:opts]]) == + [{:badrpc, {:timeout, 0.001, "Some queue(s) are unresponsive, use list_unresponsive_queues command."}}] + for i <- 1..n do + delete_queue("test_queue_" <> Integer.to_string(i), @vhost) + end + end + + @tag test_timeout: 5000 + test "run: return multiple queues", context do + declare_queue("test_queue_1", @vhost) + publish_messages(@vhost, "test_queue_1", 3) + declare_queue("test_queue_2", @vhost) + publish_messages(@vhost, "test_queue_2", 1) + assert Keyword.equal?(run_command_to_list(@command, [["name", "messages"], context[:opts]]), + [[name: "test_queue_1", messages: 3], + [name: "test_queue_2", messages: 1]]) + end + + @tag test_timeout: 5000 + test "run: info keys filter single key", context do + declare_queue("test_queue_1", @vhost) + declare_queue("test_queue_2", @vhost) + assert Keyword.equal?(run_command_to_list(@command, [["name"], context[:opts]]), + [[name: "test_queue_1"], + [name: "test_queue_2"]]) + end + + @tag test_timeout: 5000 + test "run: info keys add additional keys", context do + declare_queue("durable_queue", @vhost, true) + publish_messages(@vhost, "durable_queue", 3) + declare_queue("auto_delete_queue", @vhost, false, true) + publish_messages(@vhost, "auto_delete_queue", 1) + assert Keyword.equal?( + run_command_to_list(@command, [["name", "messages", "durable", "auto_delete"], context[:opts]]), + [[name: "durable_queue", messages: 3, durable: true, auto_delete: false], + [name: "auto_delete_queue", messages: 1, durable: false, auto_delete: true]]) + end + + @tag test_timeout: 5000 + test "run: info keys order is preserved", context do + declare_queue("durable_queue", @vhost, true) + publish_messages(@vhost, "durable_queue", 3) + declare_queue("auto_delete_queue", @vhost, false, true) + publish_messages(@vhost, "auto_delete_queue", 1) + assert Keyword.equal?( + run_command_to_list(@command, [["messages", "durable", "name", "auto_delete"], context[:opts]]), + [[messages: 3, durable: true, name: "durable_queue", auto_delete: false], + [messages: 1, durable: false, name: "auto_delete_queue", auto_delete: true]]) + end + + @tag test_timeout: 5000 + test "run: specifying a vhost returns the targeted vhost queues", context do + other_vhost = "other_vhost" + add_vhost other_vhost + on_exit(fn -> + delete_vhost other_vhost + end) + declare_queue("test_queue_1", @vhost) + declare_queue("test_queue_2", other_vhost) + assert run_command_to_list(@command, [["name"], context[:opts]]) == [[name: "test_queue_1"]] + assert run_command_to_list(@command, [["name"], %{context[:opts] | :vhost => other_vhost}]) == [[name: "test_queue_2"]] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_topic_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_topic_permissions_command_test.exs new file mode 100644 index 0000000000..8de1f2536a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_topic_permissions_command_test.exs @@ -0,0 +1,85 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListTopicPermissionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListTopicPermissionsCommand + + @vhost "test1" + @user "user1" + @password "password" + @root "/" + @default_timeout :infinity + @default_options %{vhost: "/", table_headers: true} + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost(@vhost) + add_user(@user, @password) + set_topic_permissions(@user, @vhost, "amq.topic", "^a", "^b") + set_topic_permissions(@user, @vhost, "topic1", "^a", "^b") + + on_exit([], fn -> + clear_topic_permissions(@user, @vhost) + delete_user(@user) + delete_vhost @vhost + end) + + :ok + end + + setup context do + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + timeout: context[:test_timeout], + vhost: "/" + } + } + end + + test "merge_defaults adds default vhost" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], @default_options} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default", + table_headers: true}} + end + + test "validate: does not expect any parameter" do + assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag test_timeout: @default_timeout, vhost: @vhost + test "run: specifying a vhost returns the topic permissions for the targeted vhost", context do + permissions = @command.run([], Map.merge(context[:opts], %{vhost: @vhost})) + assert Enum.count(permissions) == 2 + assert Enum.sort(permissions) == [ + [user: @user, exchange: "amq.topic", write: "^a", read: "^b"], + [user: @user, exchange: "topic1", write: "^a", read: "^b"] + ] + end + + @tag vhost: @root + test "banner", context do + ctx = Map.merge(context[:opts], %{vhost: @vhost}) + assert @command.banner([], ctx ) + =~ ~r/Listing topic permissions for vhost \"#{Regex.escape(ctx[:vhost])}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_user_limits_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_user_limits_command_test.exs new file mode 100644 index 0000000000..7b0370f940 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_user_limits_command_test.exs @@ -0,0 +1,103 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ListUserLimitsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListUserLimitsCommand + + @user "guest" + @user1 "test_user1" + @password1 "password1" + @connection_limit_defn "{\"max-connections\":100}" + @channel_limit_defn "{\"max-channels\":1000}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + user = context[:user] || @user + + clear_user_limits(user) + + on_exit(context, fn -> + clear_user_limits(user) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + global: true + }, + user: user + } + end + + test "merge_defaults: does not change defined user" do + assert match?({[], %{user: "test_user"}}, @command.merge_defaults([], %{user: "test_user"})) + end + + test "validate: providing arguments fails validation" do + assert @command.validate(["many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: a well-formed command returns an empty list if there are no limits", context do + assert @command.run([], context[:opts]) == [] + end + + test "run: a well-formed user specific command returns an empty json object if there are no limits" do + assert @command.run([], %{node: get_rabbit_hostname(), + user: @user}) == "{}" + end + + test "run: list limits for all users", context do + add_user(@user1, @password1) + on_exit(fn() -> + delete_user(@user1) + end) + set_user_limits(@user, @connection_limit_defn) + set_user_limits(@user1, @channel_limit_defn) + + assert Enum.sort(@command.run([], context[:opts])) == + Enum.sort([[user: @user, limits: @connection_limit_defn], + [user: @user1, limits: @channel_limit_defn]]) + end + + test "run: list limits for a single user", context do + user_opts = Map.put(context[:opts], :user, @user) + set_user_limits(@user, @connection_limit_defn) + + assert @command.run([], user_opts) == + [[user: @user, limits: @connection_limit_defn]] + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, user: "guest", timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag user: "user" + test "run: providing a non-existent user reports an error", _context do + s = "non-existent-user" + + assert @command.run([], %{node: get_rabbit_hostname(), + user: s}) == {:error, {:no_such_user, s}} + end + + test "banner", context do + assert @command.banner([], %{user: context[:user]}) + == "Listing limits for user \"#{context[:user]}\" ..." + assert @command.banner([], %{global: true}) + == "Listing limits for all users ..." + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_user_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_user_permissions_command_test.exs new file mode 100644 index 0000000000..ddd44c0e01 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_user_permissions_command_test.exs @@ -0,0 +1,91 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListUserPermissionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListUserPermissionsCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + default_result = [ + [ + {:vhost,<<"/">>}, + {:configure,<<".*">>}, + {:write,<<".*">>}, + {:read,<<".*">>} + ] + ] + + no_such_user_result = {:error, {:no_such_user, context[:username]}} + + { + :ok, + opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}, + result: default_result, + no_such_user: no_such_user_result, + timeout: {:badrpc, :timeout} + } + end + +## -------------------------------- Usage ------------------------------------- + + test "validate: wrong number of arguments results in an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["guest", "extra"], %{}) == {:validation_failure, :too_many_args} + end + +## ------------------------------- Username ----------------------------------- + + @tag test_timeout: :infinity, username: "guest" + test "run: valid user returns a list of permissions", context do + results = @command.run([context[:username]], context[:opts]) + assert Enum.all?(context[:result], fn(perm) -> + Enum.find(results, fn(found) -> found == perm end) + end) + end + + @tag test_timeout: :infinity, username: "interloper" + test "run: invalid user returns a no-such-user error", context do + assert @command.run( + [context[:username]], context[:opts]) == context[:no_such_user] + end + +## --------------------------------- Flags ------------------------------------ + + test "run: unreachable RabbitMQ node returns a badrpc" do + assert match?({:badrpc, _}, @command.run(["guest"], %{node: :jake@thedog, timeout: 200})) + end + + @tag test_timeout: 30000, username: "guest" + test "run: long user-defined timeout doesn't interfere with operation", context do + results = @command.run([context[:username]], context[:opts]) + Enum.all?(context[:result], fn(perm) -> + Enum.find(results, fn(found) -> found == perm end) + end) + end + + @tag test_timeout: 0, username: "guest" + test "run: timeout causes command to return a bad RPC", context do + assert @command.run( + [context[:username]], + context[:opts] + ) == context[:timeout] + end + + @tag test_timeout: :infinity + test "banner", context do + assert @command.banner( [context[:username]], context[:opts]) + =~ ~r/Listing permissions for user \"#{context[:username]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_user_topic_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_user_topic_permissions_command_test.exs new file mode 100644 index 0000000000..edf935de77 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_user_topic_permissions_command_test.exs @@ -0,0 +1,72 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListUserTopicPermissionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListUserTopicPermissionsCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + set_topic_permissions("guest", "/", "amq.topic", "^a", "^b") + set_topic_permissions("guest", "/", "topic1", "^a", "^b") + + on_exit([], fn -> + clear_topic_permissions("guest", "/") + end) + + :ok + end + + setup context do + no_such_user_result = {:error, {:no_such_user, context[:username]}} + + { + :ok, + opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}, + no_such_user: no_such_user_result, + timeout: {:badrpc, :timeout} + } + end + +## -------------------------------- Usage ------------------------------------- + + test "validate: expect username argument" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["guest", "extra"], %{}) == {:validation_failure, :too_many_args} + end + +## ------------------------------- Username ----------------------------------- + + @tag test_timeout: :infinity, username: "guest" + test "run: valid user returns a list of topic permissions", context do + results = @command.run([context[:username]], context[:opts]) + assert Enum.count(results) == 2 + end + + @tag test_timeout: :infinity, username: "interloper" + test "run: invalid user returns a no-such-user error", context do + assert @command.run( + [context[:username]], context[:opts]) == context[:no_such_user] + end + +## --------------------------------- Flags ------------------------------------ + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(["guest"], opts)) + end + + @tag test_timeout: :infinity + test "banner", context do + assert @command.banner( [context[:username]], context[:opts]) + =~ ~r/Listing topic permissions for user \"#{context[:username]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_users_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_users_command_test.exs new file mode 100644 index 0000000000..bcfdb84b2b --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_users_command_test.exs @@ -0,0 +1,74 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListUsersCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListUsersCommand + + @user "user1" + @password "password" + @guest "guest" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + std_result = [ + [{:user,@guest},{:tags,[:administrator]}], + [{:user,@user},{:tags,[]}] + ] + + {:ok, std_result: std_result} + end + + setup context do + add_user @user, @password + on_exit([], fn -> delete_user @user end) + + {:ok, opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}} + end + + test "validate: On incorrect number of commands, return an arg count error" do + assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args} + end + + @tag test_timeout: 15000 + test "run: On a successful query, return an array of lists of tuples", context do + matches_found = @command.run([], context[:opts]) + + assert Enum.all?(context[:std_result], fn(user) -> + Enum.find(matches_found, fn(found) -> found == user end) + end) + end + + test "run: On an invalid rabbitmq node, return a bad rpc" do + assert match?({:badrpc, _}, @command.run([], %{node: :jake@thedog, timeout: 200})) + end + + @tag test_timeout: 30000 + test "run: sufficiently long timeouts don't interfere with results", context do + # checks to ensure that all expected users are in the results + matches_found = @command.run([], context[:opts]) + + assert Enum.all?(context[:std_result], fn(user) -> + Enum.find(matches_found, fn(found) -> found == user end) + end) + end + + @tag test_timeout: 0 + test "run: timeout causes command to return a bad RPC", context do + assert @command.run([], context[:opts]) == + {:badrpc, :timeout} + end + + @tag test_timeout: :infinity + test "banner", context do + assert @command.banner([], context[:opts]) + =~ ~r/Listing users \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_vhost_limits_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_vhost_limits_command_test.exs new file mode 100644 index 0000000000..f07d40672a --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_vhost_limits_command_test.exs @@ -0,0 +1,111 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListVhostLimitsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListVhostLimitsCommand + + @vhost "test_vhost" + @vhost1 "test_vhost1" + @connection_limit_defn "{\"max-connections\":100}" + @queue_limit_defn "{\"max-queues\":1000}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + vhost = context[:vhost] || @vhost + + clear_vhost_limits(vhost) + + on_exit(context, fn -> + clear_vhost_limits(vhost) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + global: true + }, + vhost: vhost + } + end + + test "merge_defaults: does not change defined vhost" do + assert match?({[], %{vhost: "test_vhost"}}, @command.merge_defaults([], %{vhost: "test_vhost"})) + end + + test "validate: providing arguments fails validation" do + assert @command.validate(["many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: a well-formed command returns an empty list if there are no limits", context do + assert @command.run([], context[:opts]) == [] + end + + test "run: a well-formed vhost specific command returns an empty list if there are no limits", context do + vhost_opts = Map.put(context[:opts], :vhost, @vhost) + assert @command.run([], vhost_opts) == [] + end + + test "run: list limits for all vhosts", context do + add_vhost(@vhost1) + on_exit(fn() -> + delete_vhost(@vhost1) + end) + set_vhost_limits(@vhost, @connection_limit_defn) + set_vhost_limits(@vhost1, @queue_limit_defn) + + assert Enum.sort(@command.run([], context[:opts])) == + Enum.sort([[vhost: @vhost, limits: @connection_limit_defn], + [vhost: @vhost1, limits: @queue_limit_defn]]) + end + + test "run: list limits for a single vhost", context do + vhost_opts = Map.put(context[:opts], :vhost, @vhost) + set_vhost_limits(@vhost, @connection_limit_defn) + + assert @command.run([], vhost_opts) == + [[vhost: @vhost, limits: @connection_limit_defn]] + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag vhost: "bad-vhost" + test "run: providing a non-existent vhost reports an error", _context do + s = "non-existent-vhost-a9sd89" + + assert @command.run([], %{node: get_rabbit_hostname(), + vhost: s}) == {:error, {:no_such_vhost, s}} + end + + test "banner", context do + assert @command.banner([], %{vhost: context[:vhost]}) + == "Listing limits for vhost \"#{context[:vhost]}\" ..." + assert @command.banner([], %{global: true}) + == "Listing limits for all vhosts ..." + end +end diff --git a/deps/rabbitmq_cli/test/ctl/list_vhosts_command_test.exs b/deps/rabbitmq_cli/test/ctl/list_vhosts_command_test.exs new file mode 100644 index 0000000000..76f46af422 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/list_vhosts_command_test.exs @@ -0,0 +1,160 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ListVhostsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ListVhostsCommand + + @vhost1 "test1" + @vhost2 "test2" + @root "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost1 + add_vhost @vhost2 + trace_off @root + + on_exit([], fn -> + delete_vhost @vhost1 + delete_vhost @vhost2 + end) + + name_result = [ + [{:name, @vhost1}], + [{:name, @vhost2}], + [{:name, @root}] + ] + + tracing_result = [ + [{:tracing, false}], + [{:tracing, false}], + [{:tracing, false}] + ] + + full_result = [ + [{:name, @vhost1}, {:tracing, false}], + [{:name, @vhost2}, {:tracing, false}], + [{:name, @root}, {:tracing, false}] + ] + + transposed_result = [ + [{:tracing, false}, {:name, @vhost1}], + [{:tracing, false}, {:name, @vhost2}], + [{:tracing, false}, {:name, @root}] + ] + + { + :ok, + name_result: name_result, + tracing_result: tracing_result, + full_result: full_result, + transposed_result: transposed_result + } + end + + setup context do + { + :ok, + opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]} + } + end + + test "merge_defaults with no command, print just use the names" do + assert match?({["name"], %{}}, @command.merge_defaults([], %{})) + end + + test "validate: return bad_info_key on a single bad arg", context do + assert @command.validate(["quack"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + end + + test "validate: multiple bad args return a list of bad info key values", context do + assert @command.validate(["quack", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink, :quack]}} + end + + test "validate: return bad_info_key on mix of good and bad args", context do + assert @command.validate(["quack", "tracing"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:quack]}} + assert @command.validate(["name", "oink"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + assert @command.validate(["name", "oink", "tracing"], context[:opts]) == + {:validation_failure, {:bad_info_key, [:oink]}} + end + + test "run: on a bad RabbitMQ node, return a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(["name"], opts)) + end + + @tag test_timeout: :infinity + test "run: with the name tag, print just the names", context do + # checks to ensure that all expected vhosts are in the results + matches_found = @command.run(["name"], context[:opts]) + assert Enum.all?(context[:name_result], fn(vhost) -> + Enum.find(matches_found, fn(found) -> found == vhost end) + end) + end + + @tag test_timeout: :infinity + test "run: with the tracing tag, print just say if tracing is on", context do + # checks to ensure that all expected vhosts are in the results + matches_found = @command.run(["tracing"], context[:opts]) + assert Enum.all?(context[:tracing_result], fn(vhost) -> + Enum.find(matches_found, fn(found) -> found == vhost end) + end) + end + + @tag test_timeout: :infinity + test "run: with name and tracing keys, print both", context do + # checks to ensure that all expected vhosts are in the results + matches_found = @command.run(["name", "tracing"], context[:opts]) + assert Enum.all?(context[:full_result], fn(vhost) -> + Enum.find(matches_found, fn(found) -> found == vhost end) + end) + + # checks to ensure that all expected vhosts are in the results + matches_found = @command.run(["tracing", "name"], context[:opts]) + assert Enum.all?(context[:transposed_result], fn(vhost) -> + Enum.find(matches_found, fn(found) -> found == vhost end) + end) + end + + @tag test_timeout: :infinity + test "run: duplicate args do not produce duplicate entries", context do + # checks to ensure that all expected vhosts are in the results + matches_found = @command.run(["name", "name"], context[:opts]) + assert Enum.all?(context[:name_result], fn(vhost) -> + Enum.find(matches_found, fn(found) -> found == vhost end) + end) + end + + @tag test_timeout: 30000 + test "run: sufficiently long timeouts don't interfere with results", context do + # checks to ensure that all expected vhosts are in the results + matches_found = @command.run(["name", "tracing"], context[:opts]) + assert Enum.all?(context[:full_result], fn(vhost) -> + Enum.find(matches_found, fn(found) -> found == vhost end) + end) + end + + @tag test_timeout: 0, username: "guest" + test "run: timeout causes command to return a bad RPC", context do + assert @command.run(["name", "tracing"], context[:opts]) == + {:badrpc, :timeout} + end + + @tag test_timeout: :infinity + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Listing vhosts \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/node_health_check_command_test.exs b/deps/rabbitmq_cli/test/ctl/node_health_check_command_test.exs new file mode 100644 index 0000000000..12ff786bfb --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/node_health_check_command_test.exs @@ -0,0 +1,65 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule NodeHealthCheckCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.NodeHealthCheckCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + reset_vm_memory_high_watermark() + + on_exit([], fn -> + reset_vm_memory_high_watermark() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 20000}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "validate: with no arguments succeeds", _context do + assert @command.validate([], []) == :ok + end + + test "validate: with a named, active node argument succeeds", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "run: request to a named, active node succeeds", context do + assert @command.run([], context[:opts]) + end + + test "run: request to a named, active node with an alarm in effect fails", context do + set_vm_memory_high_watermark(0.0000000000001) + # give VM memory monitor check some time to kick in + :timer.sleep(1500) + {:healthcheck_failed, _message} = @command.run([], context[:opts]) + + reset_vm_memory_high_watermark() + :timer.sleep(1500) + assert @command.run([], context[:opts]) == :ok + end + + test "run: request to a non-existent node returns a badrpc" do + assert match?({:badrpc, _}, @command.run([], %{node: :jake@thedog, timeout: 200})) + end + + test "banner", context do + assert @command.banner([], context[:opts]) |> Enum.join("\n") =~ ~r/Checking health/ + assert @command.banner([], context[:opts]) |> Enum.join("\n") =~ ~r/#{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/ping_command_test.exs b/deps/rabbitmq_cli/test/ctl/ping_command_test.exs new file mode 100644 index 0000000000..347013a4a8 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/ping_command_test.exs @@ -0,0 +1,56 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule PingCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.PingCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + reset_vm_memory_high_watermark() + + on_exit([], fn -> + reset_vm_memory_high_watermark() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 200}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "validate: with no arguments succeeds", _context do + assert @command.validate([], []) == :ok + end + + test "validate: with a named, active node argument succeeds", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "run: request to a named, active node succeeds", context do + assert @command.run([], context[:opts]) + end + + test "run: request to a non-existent node returns a badrpc" do + assert match?({:error, _}, @command.run([], %{node: :jake@thedog, timeout: 200})) + end + + test "banner", context do + banner = @command.banner([], context[:opts]) + + assert banner =~ ~r/Will ping/ + assert banner =~ ~r/#{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/purge_queue_command_test.exs b/deps/rabbitmq_cli/test/ctl/purge_queue_command_test.exs new file mode 100644 index 0000000000..9891175f15 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/purge_queue_command_test.exs @@ -0,0 +1,88 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule PurgeQueueCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.PurgeQueueCommand + @user "guest" + @vhost "purge-queue-vhost" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + {:ok, opts: %{ + node: get_rabbit_hostname(), + vhost: @vhost, + timeout: context[:test_timeout] + }} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + @tag test_timeout: 30000 + test "request to an existent queue on active node succeeds", context do + add_vhost @vhost + set_permissions @user, @vhost, [".*", ".*", ".*"] + on_exit(context, fn -> delete_vhost(@vhost) end) + + q = "foo" + n = 20 + + declare_queue(q, @vhost) + assert message_count(@vhost, q) == 0 + + publish_messages(@vhost, q, n) + assert message_count(@vhost, q) == n + + assert @command.run([q], context[:opts]) == :ok + assert message_count(@vhost, q) == 0 + end + + @tag test_timeout: 30000 + test "request to a non-existent queue on active node returns not found", context do + assert @command.run(["non-existent"], context[:opts]) == {:error, :not_found} + end + + @tag test_timeout: 0 + test "run: timeout causes command to return a bad RPC", context do + assert @command.run(["foo"], context[:opts]) == {:badrpc, :timeout} + end + + test "shows up in help" do + s = @command.usage() + assert s =~ ~r/purge_queue/ + end + + test "defaults to vhost /" do + assert @command.merge_defaults(["foo"], %{bar: "baz"}) == {["foo"], %{bar: "baz", vhost: "/"}} + end + + test "validate: with extra arguments returns an arg count error" do + assert @command.validate(["queue-name", "extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: with no arguments returns an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: with correct args returns ok" do + assert @command.validate(["q"], %{}) == :ok + end + + test "banner informs that vhost's queue is purged" do + assert @command.banner(["my-q"], %{vhost: "/foo"}) == "Purging queue 'my-q' in vhost '/foo' ..." + end +end diff --git a/deps/rabbitmq_cli/test/ctl/rename_cluster_node_command_test.exs b/deps/rabbitmq_cli/test/ctl/rename_cluster_node_command_test.exs new file mode 100644 index 0000000000..02bf2ad795 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/rename_cluster_node_command_test.exs @@ -0,0 +1,102 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule RenameClusterNodeCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.RenameClusterNodeCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + node = get_rabbit_hostname() + + start_rabbitmq_app() + + {:ok, plugins_dir} = + :rabbit_misc.rpc_call(node, :application, :get_env, [:rabbit, :plugins_dir]) + + rabbitmq_home = :rabbit_misc.rpc_call(node, :code, :lib_dir, [:rabbit]) + mnesia_dir = :rabbit_misc.rpc_call(node, :rabbit_mnesia, :dir, []) + + on_exit([], fn -> + start_rabbitmq_app() + end) + + {:ok, opts: %{rabbitmq_home: rabbitmq_home, plugins_dir: plugins_dir, mnesia_dir: mnesia_dir}} + end + + setup context do + {:ok, + opts: + Map.merge( + context[:opts], + %{node: :not_running@localhost} + )} + end + + test "validate: specifying no nodes fails validation", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate: specifying one node only fails validation", context do + assert @command.validate(["a"], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate_execution_environment: specifying an uneven number of arguments fails validation", + context do + assert match?( + {:validation_failure, {:bad_argument, _}}, + @command.validate_execution_environment(["a", "b", "c"], context[:opts]) + ) + end + + test "validate_execution_environment: request to a running node fails", _context do + node = get_rabbit_hostname() + + assert match?( + {:validation_failure, :node_running}, + @command.validate_execution_environment([to_string(node), "other_node@localhost"], %{ + node: node + }) + ) + end + + test "validate_execution_environment: not providing node mnesia dir fails validation", + context do + opts_without_mnesia = Map.delete(context[:opts], :mnesia_dir) + Application.put_env(:mnesia, :dir, "/tmp") + on_exit(fn -> Application.delete_env(:mnesia, :dir) end) + + assert :ok == + @command.validate( + ["some_node@localhost", "other_node@localhost"], + opts_without_mnesia + ) + + Application.delete_env(:mnesia, :dir) + System.put_env("RABBITMQ_MNESIA_DIR", "/tmp") + on_exit(fn -> System.delete_env("RABBITMQ_MNESIA_DIR") end) + + assert :ok == + @command.validate( + ["some_node@localhost", "other_node@localhost"], + opts_without_mnesia + ) + + System.delete_env("RABBITMQ_MNESIA_DIR") + + assert :ok == + @command.validate(["some_node@localhost", "other_node@localhost"], context[:opts]) + end + + test "banner", context do + assert @command.banner(["a", "b"], context[:opts]) =~ + ~r/Renaming cluster nodes: \n a -> b/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/report_command_test.exs b/deps/rabbitmq_cli/test/ctl/report_command_test.exs new file mode 100644 index 0000000000..f207ab8c2b --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/report_command_test.exs @@ -0,0 +1,44 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ReportTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ReportCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: :infinity}} + end + + test "validate: with extra arguments, status returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "run: report request to a reachable node succeeds", context do + output = @command.run([], context[:opts]) |> Enum.to_list + + assert_stream_without_errors(output) + end + + test "run: report request on nonexistent RabbitMQ node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) + =~ ~r/Reporting server status of node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/reset_command_test.exs b/deps/rabbitmq_cli/test/ctl/reset_command_test.exs new file mode 100644 index 0000000000..8bded47377 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/reset_command_test.exs @@ -0,0 +1,68 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ResetCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ResetCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: reset request to an active node with a stopped rabbit app succeeds", context do + add_vhost "some_vhost" + #ensure the vhost really does exist + assert vhost_exists? "some_vhost" + stop_rabbitmq_app() + assert :ok == @command.run([], context[:opts]) + start_rabbitmq_app() + #check that the created vhost no longer exists + assert match?([_], list_vhosts()) + end + + test "run: reset request to an active node with a running rabbit app fails", context do + add_vhost "some_vhost" + assert vhost_exists? "some_vhost" + assert match?({:error, :mnesia_unexpectedly_running}, @command.run([], context[:opts])) + assert vhost_exists? "some_vhost" + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Resetting node #{get_rabbit_hostname()}/ + end + + test "output mnesia is running error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, + "Mnesia is still running on node " <> _}, + @command.output({:error, :mnesia_unexpectedly_running}, context[:opts])) + + end +end diff --git a/deps/rabbitmq_cli/test/ctl/restart_vhost_command_test.exs b/deps/rabbitmq_cli/test/ctl/restart_vhost_command_test.exs new file mode 100644 index 0000000000..c8d2fe7c48 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/restart_vhost_command_test.exs @@ -0,0 +1,95 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule RestartVhostCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.RestartVhostCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + :ok + end + + @vhost "vhost_to_restart" + @timeout 10000 + + setup do + {:ok, opts: %{ + node: get_rabbit_hostname(), + vhost: @vhost, + timeout: @timeout + }} + end + + test "validate: specifying arguments is reported as an error", context do + assert @command.validate(["a"], context[:opts]) == + {:validation_failure, :too_many_args} + assert @command.validate(["a", "b"], context[:opts]) == + {:validation_failure, :too_many_args} + assert @command.validate(["a", "b", "c"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "run: request to a non-existent node returns a badrpc", _context do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: @timeout} + assert match?( + {:badrpc, _}, + @command.run([], opts)) + end + + test "banner", context do + expected = "Trying to restart vhost '#{@vhost}' on node '#{get_rabbit_hostname()}' ..." + ^expected = @command.banner([], context[:opts]) + end + + test "run: restarting an existing vhost returns already_started", context do + setup_vhosts() + {:error, {:already_started, _}} = @command.run([], context[:opts]) + end + + test "run: restarting an failed vhost returns ok", context do + setup_vhosts() + vhost = context[:opts][:vhost] + node_name = context[:opts][:node] + force_vhost_failure(node_name, vhost) + {:ok, _} = @command.run([], context[:opts]) + {:ok, _} = :rpc.call(node_name, :rabbit_vhost_sup_sup, :get_vhost_sup, [vhost]) + end + + # + # Implementation + # + + defp setup_vhosts do + add_vhost @vhost + # give the vhost a chance to fully start and initialise + :timer.sleep(1000) + on_exit(fn -> + delete_vhost @vhost + end) + end + + defp force_vhost_failure(node_name, vhost) do + case :rpc.call(node_name, :rabbit_vhost_sup_sup, :get_vhost_sup, [vhost]) do + {:ok, sup} -> + case :lists.keyfind(:msg_store_persistent, 1, :supervisor.which_children(sup)) do + {_, pid, _, _} -> + Process.exit(pid, :foo) + :timer.sleep(5000) + force_vhost_failure(node_name, vhost); + false -> + Process.exit(sup, :foo) + :timer.sleep(5000) + force_vhost_failure(node_name, vhost) + end; + {:error, {:vhost_supervisor_not_running, _}} -> + :ok + end + end +end diff --git a/deps/rabbitmq_cli/test/ctl/resume_listeners_command_test.exs b/deps/rabbitmq_cli/test/ctl/resume_listeners_command_test.exs new file mode 100644 index 0000000000..3aad0b355b --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/resume_listeners_command_test.exs @@ -0,0 +1,67 @@ +## The contents of this file are subject to the Mozilla Public License +## Version 1.1 (the "License"); you may not use this file except in +## compliance with the License. You may obtain a copy of the License +## at https://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule ResumeListenersCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ResumeListenersCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + resume_all_client_listeners() + + node_name = get_rabbit_hostname() + on_exit(fn -> + resume_all_client_listeners() + close_all_connections(node_name) + end) + + {:ok, opts: %{node: node_name, timeout: 30_000}} + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "merge_defaults: merges no defaults" do + assert @command.merge_defaults([], %{}) == {[], %{}} + end + + test "validate: accepts no arguments", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "run: resumes all client TCP listeners so new client connects are accepted", context do + suspend_all_client_listeners() + expect_client_connection_failure() + + assert @command.run([], Map.merge(context[:opts], %{timeout: 5_000})) == :ok + + # implies a successful connection + with_channel("/", fn _ -> :ok end) + close_all_connections(get_rabbit_hostname()) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/rotate_logs_command_test.exs b/deps/rabbitmq_cli/test/ctl/rotate_logs_command_test.exs new file mode 100644 index 0000000000..13eed87d43 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/rotate_logs_command_test.exs @@ -0,0 +1,40 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule RotateLogsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.RotateLogsCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: request to a named, active node succeeds", context do + assert @command.run([], context[:opts]) == :ok + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Rotating logs for node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_cluster_name_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_cluster_name_command_test.exs new file mode 100644 index 0000000000..a0852522e4 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_cluster_name_command_test.exs @@ -0,0 +1,63 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetClusterNameCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetClusterNameCommand + + setup_all do + :net_kernel.start([:rabbitmqctl, :shortnames]) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "shows up in help" do + s = @command.usage() + assert s =~ ~r/set_cluster_name/ + end + + test "has no defaults" do + assert @command.merge_defaults(["foo"], %{bar: "baz"}) == {["foo"], %{bar: "baz"}} + end + + test "validate: with insufficient number of arguments, return arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: with too many arguments, return arg count error" do + assert @command.validate(["foo", "bar"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: with correct number of arguments, return ok" do + assert @command.validate(["mynewname"], %{}) == :ok + end + + test "run: valid name returns ok", context do + s = get_cluster_name() + assert @command.run(["agoodname"], context[:opts]) == :ok + # restore original name + @command.run([s], context[:opts]) + end + + test "run: An invalid Rabbit node returns a bad rpc message" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(["clustername"], opts)) + end + + test "banner shows that the name is being set" do + s = @command.banner(["annoyyou"], %{}) + assert s == "Setting cluster name to annoyyou ..." + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/set_disk_free_limit_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_disk_free_limit_command_test.exs new file mode 100644 index 0000000000..80f0e1511f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_disk_free_limit_command_test.exs @@ -0,0 +1,173 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetDiskFreeLimitCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetDiskFreeLimitCommand + + @default_limit 1048576 + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + set_disk_free_limit(@default_limit) + + on_exit([], fn -> + set_disk_free_limit(@default_limit) + end) + + end + + setup context do + context[:tag] # silences warnings + on_exit([], fn -> set_disk_free_limit(@default_limit) end) + + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: an invalid number of arguments results in arg count errors" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag limit: "2097152bytes" + test "run: an invalid string input returns a bad arg and does not change the limit", context do + assert @command.validate([context[:limit]], context[:opts]) == + {:validation_failure, :bad_argument} + end + + test "validate: valid fractional inputs return an ok", context do + assert @command.validate( + ["mem_relative", "0.0"], + context[:opts] + ) == :ok + + assert @command.validate( + ["mem_relative", "0.5"], + context[:opts] + ) == :ok + + assert @command.validate( + ["mem_relative", "1.8"], + context[:opts] + ) == :ok + end + + test "validate: a value outside the accepted range returns an error", context do + assert @command.validate( + ["mem_relative", "-1.0"], + context[:opts] + ) == {:validation_failure, :bad_argument} + end + + @tag fraction: "1.3" + test "validate: a valid float string input returns ok", context do + assert @command.validate( + ["mem_relative", context[:fraction]], + context[:opts] + ) == :ok + end + + @tag fraction: "1.3salt" + test "validate: an invalid string input returns a bad argument", context do + assert @command.validate( + ["mem_relative", context[:fraction]], + context[:opts] + ) == {:validation_failure, :bad_argument} + end + +## ------------------------ validate mem_relative command ------------------------------------------- + + test "validate: an invalid number of mem_relative arguments results in an arg count error" do + assert @command.validate(["mem_relative"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["mem_relative", 1.3, "extra"], %{}) == {:validation_failure, :too_many_args} + end + + +## ------------------------ run absolute command ------------------------------------------- + + @tag test_timeout: 3000 + test "run: an invalid node returns a bad rpc" do + args = [@default_limit] + opts = %{node: :jake@thedog} + + assert match?({:badrpc, _}, @command.run(args, opts)) + end + + @tag limit: 2097152 + test "run: a valid integer input returns an ok and sets the disk free limit", context do + assert @command.run([context[:limit]], context[:opts]) == :ok + assert status()[:disk_free_limit] === context[:limit] + end + + @tag limit: 2097152.0 + test "run: a valid non-fractional float input returns an ok and sets the disk free limit", context do + assert @command.run([context[:limit]], context[:opts]) == :ok + assert status()[:disk_free_limit] === round(context[:limit]) + end + + @tag limit: 2097152.9 + test "run: a valid fractional float input returns an ok and sets the disk free limit", context do + assert @command.run([context[:limit]], context[:opts]) == :ok + assert status()[:disk_free_limit] === context[:limit] |> Float.floor |> round + end + + @tag limit: "2097152" + test "run: an integer string input returns an ok and sets the disk free limit", context do + assert @command.run([context[:limit]], context[:opts]) == :ok + assert status()[:disk_free_limit] === String.to_integer(context[:limit]) + end + + @tag limit: "2MB" + test "run: an valid unit string input returns an ok and changes the limit", context do + assert @command.run([context[:limit]], context[:opts]) == :ok + assert status()[:disk_free_limit] === 2000000 + end + +## ------------------------ run relative command ------------------------------------------- + + @tag fraction: 1 + test "run: an integer input returns ok", context do + assert @command.run( + ["mem_relative", context[:fraction]], + context[:opts] + ) == :ok + end + + @tag fraction: 1.1 + test "run: a factional input returns ok", context do + assert @command.run( + ["mem_relative", context[:fraction]], + context[:opts] + ) == :ok + end + + + test "banner: returns absolute message", context do + assert @command.banner(["10"], context[:opts]) + =~ ~r/Setting disk free limit on #{get_rabbit_hostname()} to 10 bytes .../ + + assert @command.banner(["-10"], context[:opts]) + =~ ~r/Setting disk free limit on #{get_rabbit_hostname()} to -10 bytes .../ + + assert @command.banner(["sandwich"], context[:opts]) + =~ ~r/Setting disk free limit on #{get_rabbit_hostname()} to sandwich bytes .../ + end + + test "banner: returns memory-relative message", context do + assert @command.banner(["mem_relative", "1.3"], context[:opts]) + =~ ~r/Setting disk free limit on #{get_rabbit_hostname()} to 1\.3 times the total RAM \.\.\./ + + assert @command.banner(["mem_relative", "-1.3"], context[:opts]) + =~ ~r/Setting disk free limit on #{get_rabbit_hostname()} to -1\.3 times the total RAM \.\.\./ + + assert @command.banner(["mem_relative", "sandwich"], context[:opts]) + =~ ~r/Setting disk free limit on #{get_rabbit_hostname()} to sandwich times the total RAM \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_global_parameter_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_global_parameter_command_test.exs new file mode 100644 index 0000000000..848f29a0b8 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_global_parameter_command_test.exs @@ -0,0 +1,82 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetGlobalParameterCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetGlobalParameterCommand + + @key :mqtt_default_vhosts + @value "{\"O=client,CN=dummy\":\"somevhost\"}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup context do + on_exit(context, fn -> + clear_global_parameter context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + } + } + end + + test "validate: expects a key and a value" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag key: @key, value: @value + test "run: expects a key and a value", context do + assert @command.run( + [context[:key], context[:value]], + context[:opts] + ) == :ok + + assert_parameter_fields(context) + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([@key, @value], opts)) + end + + @tag key: @key, value: "bad-value" + test "run: a value that fails to parse as JSON returns a decoding error", context do + initial = list_global_parameters() + assert match?({:error_string, _}, + @command.run([context[:key], context[:value]], + context[:opts])) + + assert list_global_parameters() == initial + end + + @tag key: @key, value: @value + test "banner", context do + assert @command.banner([context[:key], context[:value]], context[:opts]) + =~ ~r/Setting global runtime parameter \"#{context[:key]}\" to \"#{context[:value]}\" \.\.\./ + end + + # Checks each element of the first parameter against the expected context values + defp assert_parameter_fields(context) do + result_param = list_global_parameters() |> List.first + + assert result_param[:value] == context[:value] + assert result_param[:name] == context[:key] + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/set_log_level_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_log_level_command_test.exs new file mode 100644 index 0000000000..b4108219ba --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_log_level_command_test.exs @@ -0,0 +1,44 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule SetLogLevelCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetLogLevelCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + {:ok, + log_level: "debug", + opts: %{node: get_rabbit_hostname()}} + end + + test "validate: with a single known level succeeds", context do + assert @command.validate([context[:log_level]], context[:opts]) == :ok + end + + test "validate: with a single unsupported level fails", context do + assert match?({:error, _}, @command.validate(["lolwut"], context[:opts])) + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate([context[:log_level], "whoops"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: request to a named, active node succeeds", context do + assert @command.run([context[:log_level]], context[:opts]) == :ok + end + + test "run: request to a non-existent node returns a badrpc", context do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([context[:log_level]], opts)) + end + + test "banner", context do + assert @command.banner([context[:log_level]], context[:opts]) == "Setting log level to \"debug\" ..." + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_operator_policy_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_operator_policy_command_test.exs new file mode 100644 index 0000000000..5911132a32 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_operator_policy_command_test.exs @@ -0,0 +1,153 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetOperatorPolicyCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetOperatorPolicyCommand + + @vhost "test1" + @root "/" + @key "message-expiry" + @pattern "^queue\." + @value "{\"message-ttl\":10}" + @apply_to "all" + @priority 0 + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + + on_exit(context, fn -> + clear_operator_policy(context[:vhost], context[:key]) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + vhost: "/", + apply_to: @apply_to, + priority: @priority + } + } + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: @root + test "merge_defaults: a well-formed command with no vhost runs against the default" do + assert match?({_, %{vhost: "/"}}, @command.merge_defaults([], %{})) + end + + test "merge_defaults: does not change defined vhost" do + assert match?({[], %{vhost: "test_vhost"}}, @command.merge_defaults([], %{vhost: "test_vhost"})) + end + + test "merge_defaults: default apply_to is \"all\"" do + assert match?({_, %{apply_to: "all"}}, @command.merge_defaults([], %{})) + assert match?({_, %{apply_to: "custom"}}, @command.merge_defaults([], %{apply_to: "custom"})) + end + + test "merge_defaults: default priority is 0" do + assert match?({_, %{priority: 0}}, @command.merge_defaults([], %{})) + assert match?({_, %{priority: 3}}, @command.merge_defaults([], %{priority: 3})) + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not", "enough"], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: @vhost + test "run: a well-formed, host-specific command returns okay", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key], context[:pattern], context[:value]], + vhost_opts + ) == :ok + + assert_operator_policy_fields(context) + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", priority: 0, apply_to: "all", timeout: 200} + + assert match?({:badrpc, _}, @command.run([@key, @pattern, @value], opts)) + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: "bad-vhost" + test "run: providing a non-existent vhost reports an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key], context[:pattern], context[:value]], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag pattern: @pattern, key: @key, value: "bad-value", vhost: @root + test "run: an invalid value returns a JSON decoding error", context do + assert match?({:error_string, _}, + @command.run([context[:key], context[:pattern], context[:value]], + context[:opts])) + + assert list_operator_policies(context[:vhost]) == [] + end + + @tag pattern: @pattern, key: @key, value: "{\"foo\":\"bar\"}", vhost: @root + test "run: invalid policy returns an error", context do + assert @command.run( + [context[:key], context[:pattern], context[:value]], + context[:opts] + ) == {:error_string, 'Validation failed\n\n[{<<"foo">>,<<"bar">>}] are not recognised policy settings\n'} + + assert list_operator_policies(context[:vhost]) == [] + end + + @tag pattern: @pattern, key: @key, value: "{}", vhost: @root + test "run: an empty JSON object value returns an error", context do + assert @command.run( + [context[:key], context[:pattern], context[:value]], + context[:opts] + ) == {:error_string, 'Validation failed\n\nno policy provided\n'} + + assert list_operator_policies(context[:vhost]) == [] + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:key], context[:pattern], context[:value]], vhost_opts) + == "Setting operator policy override \"#{context[:key]}\" for pattern \"#{context[:pattern]}\" to \"#{context[:value]}\" with priority \"#{context[:opts][:priority]}\" for vhost \"#{context[:vhost]}\" \.\.\." + end + + # Checks each element of the first policy against the expected context values + defp assert_operator_policy_fields(context) do + result_policy = context[:vhost] |> list_operator_policies |> List.first + assert result_policy[:definition] == context[:value] + assert result_policy[:vhost] == context[:vhost] + assert result_policy[:pattern] == context[:pattern] + assert result_policy[:name] == context[:key] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_parameter_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_parameter_command_test.exs new file mode 100644 index 0000000000..50a2543dee --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_parameter_command_test.exs @@ -0,0 +1,136 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetParameterCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetParameterCommand + + @vhost "test1" + @root "/" + @component_name "federation-upstream" + @key "reconnect-delay" + @value "{\"uri\":\"amqp://127.0.0.1:5672\"}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + enable_federation_plugin() + + on_exit([], fn -> + delete_vhost @vhost + end) + + # featured in a definitions file imported by other tests + clear_parameter("/", "federation-upstream", "up-1") + + :ok + end + + setup context do + on_exit(context, fn -> + clear_parameter context[:vhost], context[:component_name], context[:key] + end) + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + vhost: context[:vhost] + } + } + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @root + test "merge_defaults: a well-formed command with no vhost runs against the default" do + assert match?({_, %{vhost: "/"}}, @command.merge_defaults([], %{})) + assert match?({_, %{vhost: "non_default"}}, @command.merge_defaults([], %{vhost: "non_default"})) + end + + test "validate: wrong number of arguments leads to an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not", "enough"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @vhost + test "run: a well-formed, host-specific command returns okay", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:component_name], context[:key], context[:value]], + vhost_opts + ) == :ok + + assert_parameter_fields(context) + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run([@component_name, @key, @value], opts)) + end + + @tag component_name: "bad-component-name", key: @key, value: @value, vhost: @root + test "run: an invalid component_name returns a validation failed error", context do + assert @command.run( + [context[:component_name], context[:key], context[:value]], + context[:opts] + ) == {:error_string, 'Validation failed\n\ncomponent #{context[:component_name]} not found\n'} + + assert list_parameters(context[:vhost]) == [] + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: "bad-vhost" + test "run: an invalid vhost returns a no-such-vhost error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:component_name], context[:key], context[:value]], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag component_name: @component_name, key: @key, value: "bad-value", vhost: @root + test "run: an invalid value returns a JSON decoding error", context do + assert match?({:error_string, _}, + @command.run([context[:component_name], context[:key], context[:value]], + context[:opts])) + + assert list_parameters(context[:vhost]) == [] + end + + @tag component_name: @component_name, key: @key, value: "{}", vhost: @root + test "run: an empty JSON object value returns a key \"uri\" not found error", context do + assert @command.run( + [context[:component_name], context[:key], context[:value]], + context[:opts] + ) == {:error_string, 'Validation failed\n\nKey "uri" not found in reconnect-delay\n'} + + assert list_parameters(context[:vhost]) == [] + end + + @tag component_name: @component_name, key: @key, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:component_name], context[:key], context[:value]], vhost_opts) + =~ ~r/Setting runtime parameter \"#{context[:key]}\" for component \"#{context[:component_name]}\" to \"#{context[:value]}\" in vhost \"#{context[:vhost]}\" \.\.\./ + end + + # Checks each element of the first parameter against the expected context values + defp assert_parameter_fields(context) do + result_param = context[:vhost] |> list_parameters |> List.first + + assert result_param[:value] == context[:value] + assert result_param[:component] == context[:component_name] + assert result_param[:name] == context[:key] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_permissions_command_test.exs new file mode 100644 index 0000000000..c2628f2728 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_permissions_command_test.exs @@ -0,0 +1,114 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetPermissionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetPermissionsCommand + + @vhost "test1" + @user "guest" + @root "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + + on_exit(context, fn -> + set_permissions context[:user], context[:vhost], [".*", ".*", ".*"] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + vhost: context[:vhost] + } + } + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: wrong number of arguments leads to an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not", "enough"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not", "quite", "enough"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this", "is", "way", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag user: @user, vhost: @vhost + test "run: a well-formed, host-specific command returns okay", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:user], "^#{context[:user]}-.*", ".*", ".*"], + vhost_opts + ) == :ok + + u = Enum.find(list_permissions(context[:vhost]), fn(x) -> x[:user] == context[:user] end) + assert u[:configure] == "^#{context[:user]}-.*" + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + + assert match?({:badrpc, _}, @command.run([@user, ".*", ".*", ".*"], opts)) + end + + @tag user: "interloper", vhost: @root + test "run: an invalid user returns a no-such-user error", context do + assert @command.run( + [context[:user], "^#{context[:user]}-.*", ".*", ".*"], + context[:opts] + ) == {:error, {:no_such_user, context[:user]}} + end + + @tag user: @user, vhost: "wintermute" + test "run: an invalid vhost returns a no-such-vhost error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:user], "^#{context[:user]}-.*", ".*", ".*"], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag user: @user, vhost: @root + test "run: invalid regex patterns returns an error", context do + assert @command.run( + [context[:user], "^#{context[:user]}-.*", ".*", "*"], + context[:opts] + ) == {:error, {:invalid_regexp, '*', {'nothing to repeat', 0}}} + + # asserts that the failed command didn't change anything + u = Enum.find(list_permissions(context[:vhost]), fn(x) -> x[:user] == context[:user] end) + assert u == [user: context[:user], configure: ".*", write: ".*", read: ".*"] + end + + @tag user: @user, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:user], "^#{context[:user]}-.*", ".*", ".*"], vhost_opts) + =~ ~r/Setting permissions for user \"#{context[:user]}\" in vhost \"#{context[:vhost]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_policy_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_policy_command_test.exs new file mode 100644 index 0000000000..0422933ecb --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_policy_command_test.exs @@ -0,0 +1,217 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetPolicyCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetPolicyCommand + + @vhost "test1" + @root "/" + @key "federate" + @pattern "^fed\." + @value "{\"federation-upstream-set\":\"all\"}" + @apply_to "all" + @priority 0 + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + enable_federation_plugin() + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + + on_exit(context, fn -> + clear_policy context[:vhost], context[:key] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + vhost: "/", + apply_to: @apply_to, + priority: @priority + } + } + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: @root + test "merge_defaults: a well-formed command with no vhost runs against the default" do + assert match?({_, %{vhost: "/"}}, @command.merge_defaults([], %{})) + end + + test "merge_defaults: does not change defined vhost" do + assert match?({[], %{vhost: "test_vhost"}}, @command.merge_defaults([], %{vhost: "test_vhost"})) + end + + test "merge_defaults: default apply_to is \"all\"" do + assert match?({_, %{apply_to: "all"}}, @command.merge_defaults([], %{})) + assert match?({_, %{apply_to: "custom"}}, @command.merge_defaults([], %{apply_to: "custom"})) + end + + test "merge_defaults: default priority is 0" do + assert match?({_, %{priority: 0}}, @command.merge_defaults([], %{})) + assert match?({_, %{priority: 3}}, @command.merge_defaults([], %{priority: 3})) + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not", "enough"], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: @vhost + test "run: a well-formed, host-specific command returns okay", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key], context[:pattern], context[:value]], + vhost_opts + ) == :ok + + assert_policy_fields(context) + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", priority: 0, apply_to: "all", timeout: 200} + + assert match?({:badrpc, _}, @command.run([@key, @pattern, @value], opts)) + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: "bad-vhost" + test "run: providing a non-existent vhost reports an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:key], context[:pattern], context[:value]], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + @tag pattern: @pattern, key: @key, value: "bad-value", vhost: @root + test "run: an invalid value returns a JSON decoding error", context do + assert match?({:error_string, _}, + @command.run([context[:key], context[:pattern], context[:value]], + context[:opts])) + + assert list_policies(context[:vhost]) == [] + end + + @tag pattern: @pattern, key: @key, value: "{\"foo\":\"bar\"}", vhost: @root + test "run: invalid policy returns an error", context do + assert @command.run( + [context[:key], context[:pattern], context[:value]], + context[:opts] + ) == {:error_string, 'Validation failed\n\n[{<<"foo">>,<<"bar">>}] are not recognised policy settings\n'} + + assert list_policies(context[:vhost]) == [] + end + + @tag pattern: @pattern, key: @key, value: "{}", vhost: @root + test "run: an empty JSON object value returns an error", context do + assert @command.run( + [context[:key], context[:pattern], context[:value]], + context[:opts] + ) == {:error_string, 'Validation failed\n\nno policy provided\n'} + + assert list_policies(context[:vhost]) == [] + end + + @tag pattern: @pattern, key: @key, value: @value, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:key], context[:pattern], context[:value]], vhost_opts) + == "Setting policy \"#{context[:key]}\" for pattern \"#{context[:pattern]}\" to \"#{context[:value]}\" with priority \"#{context[:opts][:priority]}\" for vhost \"#{context[:vhost]}\" \.\.\." + end + + @tag pattern: "ha_", key: "ha_policy_test", vhost: @vhost + test "ha policy validation", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + context = Map.put(context, :opts, vhost_opts) + pass_validation(context, "{\"ha-mode\":\"all\"}") + fail_validation(context, "{\"ha-mode\":\"made_up\"}") + + fail_validation(context, "{\"ha-mode\":\"nodes\"}") + fail_validation(context, "{\"ha-mode\":\"nodes\",\"ha-params\":2}") + fail_validation(context, "{\"ha-mode\":\"nodes\",\"ha-params\":[\"a\",2]}") + pass_validation(context, "{\"ha-mode\":\"nodes\",\"ha-params\":[\"a\",\"b\"]}") + fail_validation(context, "{\"ha-params\":[\"a\",\"b\"]}") + + fail_validation(context, "{\"ha-mode\":\"exactly\"}") + fail_validation(context, "{\"ha-mode\":\"exactly\",\"ha-params\":[\"a\",\"b\"]}") + pass_validation(context, "{\"ha-mode\":\"exactly\",\"ha-params\":2}") + fail_validation(context, "{\"ha-params\":2}") + + pass_validation(context, "{\"ha-mode\":\"all\",\"ha-sync-mode\":\"manual\"}") + pass_validation(context, "{\"ha-mode\":\"all\",\"ha-sync-mode\":\"automatic\"}") + fail_validation(context, "{\"ha-mode\":\"all\",\"ha-sync-mode\":\"made_up\"}") + fail_validation(context, "{\"ha-sync-mode\":\"manual\"}") + fail_validation(context, "{\"ha-sync-mode\":\"automatic\"}") + end + + @tag pattern: "ha_", key: "ha_policy_test", vhost: @vhost + test "queue master locator policy validation", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + context = Map.put(context, :opts, vhost_opts) + pass_validation(context, "{\"queue-master-locator\":\"min-masters\"}") + pass_validation(context, "{\"queue-master-locator\":\"client-local\"}") + pass_validation(context, "{\"queue-master-locator\":\"random\"}") + fail_validation(context, "{\"queue-master-locator\":\"made_up\"}") + end + + @tag pattern: "ha_", key: "ha_policy_test", vhost: @vhost + test "queue modes policy validation", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + context = Map.put(context, :opts, vhost_opts) + pass_validation(context, "{\"queue-mode\":\"lazy\"}") + pass_validation(context, "{\"queue-mode\":\"default\"}") + fail_validation(context, "{\"queue-mode\":\"wrong\"}") + end + + def pass_validation(context, value) do + assert @command.run( + [context[:key], context[:pattern], value], + context[:opts] + ) == :ok + assert_policy_fields(Map.merge(context, %{value: value})) + end + + def fail_validation(context, value) do + result = @command.run( + [context[:key], context[:pattern], value], + context[:opts] + ) + assert {:error_string, _} = result + {:error_string, msg} = result + assert "Validation failed"<>_ = to_string(msg) + end + + # Checks each element of the first policy against the expected context values + defp assert_policy_fields(context) do + result_policy = context[:vhost] |> list_policies |> List.first + assert result_policy[:definition] == context[:value] + assert result_policy[:vhost] == context[:vhost] + assert result_policy[:pattern] == context[:pattern] + assert result_policy[:name] == context[:key] + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_topic_permissions_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_topic_permissions_command_test.exs new file mode 100644 index 0000000000..f117f5a789 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_topic_permissions_command_test.exs @@ -0,0 +1,114 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetTopicPermissionsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetTopicPermissionsCommand + + @vhost "test1" + @user "guest" + @root "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + + on_exit(context, fn -> + clear_topic_permissions context[:user], context[:vhost] + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + vhost: context[:vhost] + } + } + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: expects username, exchange, and pattern arguments" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["insufficient"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not", "enough"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["still", "not", "enough"], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["this", "is", "way", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + @tag user: @user, vhost: @vhost + test "run: a well-formed, host-specific command returns okay", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:user], "amq.topic", "^a", "^b"], + vhost_opts + ) == :ok + + assert List.first(list_user_topic_permissions(context[:user]))[:write] == "^a" + assert List.first(list_user_topic_permissions(context[:user]))[:read] == "^b" + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + + assert match?({:badrpc, _}, @command.run([@user, "amq.topic", "^a", "^b"], opts)) + end + + @tag user: "interloper", vhost: @root + test "run: an invalid user returns a no-such-user error", context do + assert @command.run( + [context[:user], "amq.topic", "^a", "^b"], + context[:opts] + ) == {:error, {:no_such_user, context[:user]}} + end + + @tag user: @user, vhost: "wintermute" + test "run: an invalid vhost returns a no-such-vhost error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:user], "amq.topic", "^a", "^b"], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + + assert Enum.count(list_user_topic_permissions(context[:user])) == 0 + end + + @tag user: @user, vhost: @root + test "run: invalid regex patterns return error", context do + n = Enum.count(list_user_topic_permissions(context[:user])) + {:error, {:invalid_regexp, _, _}} = @command.run( + [context[:user], "amq.topic", "[", "^b"], + context[:opts] + ) + assert Enum.count(list_user_topic_permissions(context[:user])) == n + end + + @tag user: @user, vhost: @vhost + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:user], "amq.topic", "^a", "^b"], vhost_opts) + =~ ~r/Setting topic permissions on \"amq.topic\" for user \"#{context[:user]}\" in vhost \"#{context[:vhost]}\" \.\.\./ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_user_limits_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_user_limits_command_test.exs new file mode 100644 index 0000000000..6179267396 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_user_limits_command_test.exs @@ -0,0 +1,137 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule SetUserLimitsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetUserLimitsCommand + + @user "someone" + @password "password" + @conn_definition "{\"max-connections\":100}" + @channel_definition "{\"max-channels\":200}" + @definition "{\"max-connections\":50, \"max-channels\":500}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_user @user, @password + + on_exit([], fn -> + delete_user @user + end) + + :ok + end + + setup context do + user = context[:user] || @user + + clear_user_limits(user) + + on_exit(context, fn -> + clear_user_limits(user) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname() + }, + user: user + } + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["not-enough"], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: a well-formed, host-specific command returns okay", context do + assert @command.run( + [context[:user], + @conn_definition], + context[:opts] + ) == :ok + + assert_limits(context, @conn_definition) + clear_user_limits(context[:user]) + + assert @command.run( + [context[:user], + @channel_definition], + context[:opts] + ) == :ok + + assert_limits(context, @channel_definition) + end + + test "run: a well-formed command to set both max-connections and max-channels returns okay", context do + assert @command.run( + [context[:user], + @definition], + context[:opts] + ) == :ok + + assert_limits(context, @definition) + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([@user, @conn_definition], opts)) + end + + @tag user: "non-existent-user" + test "run: providing a non-existent user reports an error", context do + + assert @command.run( + [context[:user], + @conn_definition], + context[:opts] + ) == {:error, {:no_such_user, context[:user]}} + end + + test "run: an invalid definition returns a JSON decoding error", context do + assert match?({:error_string, _}, + @command.run( + [context[:user], + ["this_is_not_json"]], + context[:opts])) + + assert get_user_limits(context[:user]) == %{} + end + + test "run: invalid limit returns an error", context do + assert @command.run( + [context[:user], + "{\"foo\":\"bar\"}"], + context[:opts] + ) == {:error_string, 'Unrecognised terms [{<<"foo">>,<<"bar">>}] in user-limits'} + + assert get_user_limits(context[:user]) == %{} + end + + test "banner", context do + assert @command.banner([context[:user], context[:conn_definition]], context[:opts]) + == "Setting user limits to \"#{context[:conn_definition]}\" for user \"#{context[:user]}\" ..." + end + + # + # Implementation + # + + defp assert_limits(context, definition) do + limits = get_user_limits(context[:user]) + assert {:ok, limits} == JSON.decode(definition) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_user_tags_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_user_tags_command_test.exs new file mode 100644 index 0000000000..cdc51e673f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_user_tags_command_test.exs @@ -0,0 +1,144 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetUserTagsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetUserTagsCommand + + @user "user1" + @password "password" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_user @user, @password + + on_exit([], fn -> + delete_user(@user) + end) + + :ok + end + + setup context do + context[:user] # silences warnings + on_exit([], fn -> set_user_tags(context[:user], []) end) + + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: on an incorrect number of arguments, return an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "run: throws a badrpc when instructed to contact an unreachable RabbitMQ node" do + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run([@user, :imperator], opts)) + end + + @tag user: @user, tags: [:imperator] + test "run: on a single optional argument, add a flag to the user", context do + @command.run( + [context[:user] | context[:tags]], + context[:opts] + ) + + result = Enum.find( + list_users(), + fn(record) -> record[:user] == context[:user] end + ) + + assert result[:tags] == context[:tags] + end + + @tag user: "interloper", tags: [:imperator] + test "run: on an invalid user, get a no such user error", context do + assert @command.run( + [context[:user] | context[:tags]], + context[:opts] + ) == {:error, {:no_such_user, context[:user]}} + end + + @tag user: @user, tags: [:imperator, :generalissimo] + test "run: on multiple optional arguments, add all flags to the user", context do + @command.run( + [context[:user] | context[:tags]], + context[:opts] + ) + + result = Enum.find( + list_users(), + fn(record) -> record[:user] == context[:user] end + ) + + assert result[:tags] == context[:tags] + end + + @tag user: @user, tags: [:imperator] + test "run: with no optional arguments, clear user tags", context do + + set_user_tags(context[:user], context[:tags]) + + @command.run([context[:user]], context[:opts]) + + result = Enum.find( + list_users(), + fn(record) -> record[:user] == context[:user] end + ) + + assert result[:tags] == [] + end + + @tag user: @user, tags: [:imperator] + test "run: identical calls are idempotent", context do + + set_user_tags(context[:user], context[:tags]) + + assert @command.run( + [context[:user] | context[:tags]], + context[:opts] + ) == :ok + + result = Enum.find( + list_users(), + fn(record) -> record[:user] == context[:user] end + ) + + assert result[:tags] == context[:tags] + end + + @tag user: @user, old_tags: [:imperator], new_tags: [:generalissimo] + test "run: if different tags exist, overwrite them", context do + + set_user_tags(context[:user], context[:old_tags]) + + assert @command.run( + [context[:user] | context[:new_tags]], + context[:opts] + ) == :ok + + result = Enum.find( + list_users(), + fn(record) -> record[:user] == context[:user] end + ) + + assert result[:tags] == context[:new_tags] + end + + @tag user: @user, tags: ["imperator"] + test "banner", context do + assert @command.banner( + [context[:user] | context[:tags]], + context[:opts] + ) + =~ ~r/Setting tags for user \"#{context[:user]}\" to \[#{context[:tags]}\] \.\.\./ + end + +end diff --git a/deps/rabbitmq_cli/test/ctl/set_vhost_limits_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_vhost_limits_command_test.exs new file mode 100644 index 0000000000..b5c679b02f --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_vhost_limits_command_test.exs @@ -0,0 +1,137 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule SetVhostLimitsCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SetVhostLimitsCommand + + @vhost "test1" + @definition "{\"max-connections\":100}" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost @vhost + + on_exit([], fn -> + delete_vhost @vhost + end) + + :ok + end + + setup context do + + vhost = context[:vhost] || @vhost + + clear_vhost_limits(vhost) + + on_exit(context, fn -> + clear_vhost_limits(vhost) + end) + + { + :ok, + opts: %{ + node: get_rabbit_hostname(), + vhost: vhost + }, + definition: context[:definition] || @definition, + vhost: vhost + } + end + + test "merge_defaults: a well-formed command with no vhost runs against the default" do + assert match?({_, %{vhost: "/"}}, @command.merge_defaults([], %{})) + end + + test "merge_defaults: does not change defined vhost" do + assert match?({[], %{vhost: "test_vhost"}}, @command.merge_defaults([], %{vhost: "test_vhost"})) + end + + test "validate: providing too few arguments fails validation" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation" do + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + assert @command.validate(["this", "is", "too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: a well-formed, host-specific command returns okay", context do + assert @command.run( + [context[:definition]], + context[:opts] + ) == :ok + + assert_limits(context) + end + + test "run: an unreachable node throws a badrpc" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run([@definition], opts)) + end + + @tag vhost: "bad-vhost" + test "run: providing a non-existent vhost reports an error", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.run( + [context[:definition]], + vhost_opts + ) == {:error, {:no_such_vhost, context[:vhost]}} + end + + test "run: an invalid definition returns a JSON decoding error", context do + assert match?({:error_string, _}, + @command.run(["bad_value"], context[:opts])) + + assert get_vhost_limits(context[:vhost]) == %{} + end + + test "run: invalid limit returns an error", context do + assert @command.run( + ["{\"foo\":\"bar\"}"], + context[:opts] + ) == {:error_string, 'Validation failed\n\nUnrecognised terms [{<<"foo">>,<<"bar">>}] in limits\n'} + + assert get_vhost_limits(context[:vhost]) == %{} + end + + test "run: an empty JSON object definition unsets all limits for vhost", context do + + assert @command.run( + [@definition], + context[:opts] + ) == :ok + + assert_limits(context) + + assert @command.run( + ["{}"], + context[:opts] + ) == :ok + + assert get_vhost_limits(context[:vhost]) == %{} + end + + test "banner", context do + vhost_opts = Map.merge(context[:opts], %{vhost: context[:vhost]}) + + assert @command.banner([context[:definition]], vhost_opts) + == "Setting vhost limits to \"#{context[:definition]}\" for vhost \"#{context[:vhost]}\" ..." + end + + defp assert_limits(context) do + limits = get_vhost_limits(context[:vhost]) + assert {:ok, limits} == JSON.decode(context[:definition]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/set_vm_memory_high_watermark_command_test.exs b/deps/rabbitmq_cli/test/ctl/set_vm_memory_high_watermark_command_test.exs new file mode 100644 index 0000000000..bd9719ab40 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/set_vm_memory_high_watermark_command_test.exs @@ -0,0 +1,162 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule SetVmMemoryHighWatermarkCommandTest do + use ExUnit.Case, async: false + import TestHelper + import RabbitMQ.CLI.Core.{Alarms, Memory} + + @command RabbitMQ.CLI.Ctl.Commands.SetVmMemoryHighWatermarkCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + start_rabbitmq_app() + reset_vm_memory_high_watermark() + + on_exit([], fn -> + start_rabbitmq_app() + reset_vm_memory_high_watermark() + end) + + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: a string returns an error", context do + assert @command.validate(["sandwich"], context[:opts]) == {:validation_failure, :bad_argument} + assert @command.validate(["0.4sandwich"], context[:opts]) == {:validation_failure, :bad_argument} + end + + test "validate: valid numerical value returns valid", context do + assert @command.validate(["0.7"], context[:opts]) == :ok + assert @command.validate(["1"], context[:opts]) == :ok + end + + test "run: valid numerical value returns valid", context do + assert @command.run([0.7], context[:opts]) == :ok + assert status()[:vm_memory_high_watermark] == 0.7 + + assert @command.run([1], context[:opts]) == :ok + assert status()[:vm_memory_high_watermark] == 1 + end + + test "validate: validate a valid numerical string value returns valid", context do + assert @command.validate(["0.7"], context[:opts]) == :ok + assert @command.validate(["1"], context[:opts]) == :ok + end + + test "validate: the wrong number of arguments returns an arg count error" do + assert @command.validate([], %{}) == {:validation_failure, :not_enough_args} + assert @command.validate(["too", "many"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: a negative number returns a bad argument", context do + assert @command.validate(["-0.1"], context[:opts]) == {:validation_failure, {:bad_argument, "The threshold should be a fraction between 0.0 and 1.0"}} + end + + test "validate: a percentage returns a bad argument", context do + assert @command.validate(["40"], context[:opts]) == {:validation_failure, {:bad_argument, "The threshold should be a fraction between 0.0 and 1.0"}} + end + + test "validate: a value greater than 1.0 returns a bad argument", context do + assert @command.validate(["1.1"], context[:opts]) == {:validation_failure, {:bad_argument, "The threshold should be a fraction between 0.0 and 1.0"}} + end + + @tag test_timeout: 3000 + test "run: on an invalid node, return a bad rpc" do + args = [0.7] + opts = %{node: :jake@thedog, timeout: 200} + + assert match?({:badrpc, _}, @command.run(args, opts)) + end + +## ---------------------------- Absolute tests -------------------------------- + + test "validate: an absolute call without an argument returns not enough args" do + assert @command.validate(["absolute"], %{}) == {:validation_failure, :not_enough_args} + end + + test "validate: an absolute call with too many arguments returns too many args" do + assert @command.validate(["absolute", "too", "many"], %{}) == + {:validation_failure, :too_many_args} + end + + test "validate: a single absolute integer return valid", context do + assert @command.validate(["absolute","10"], context[:opts]) == :ok + end + test "run: a single absolute integer return ok", context do + assert @command.run(["absolute","10"], context[:opts]) == :ok + assert status()[:vm_memory_high_watermark] == {:absolute, memory_unit_absolute(10, "")} + end + + test "validate: a single absolute integer with an invalid memory unit fails ", context do + assert @command.validate(["absolute","10bytes"], context[:opts]) == {:validation_failure, {:bad_argument, "Invalid units."}} + end + + test "validate: a single absolute float with a valid memory unit fails ", context do + assert @command.validate(["absolute","10.0MB"], context[:opts]) == {:validation_failure, {:bad_argument, "The threshold should be an integer."}} + end + + test "validate: a single absolute float with an invalid memory unit fails ", context do + assert @command.validate(["absolute","10.0bytes"], context[:opts]) == {:validation_failure, {:bad_argument, "The threshold should be an integer."}} + end + + test "validate: a single absolute string fails ", context do + assert @command.validate(["absolute","large"], context[:opts]) == {:validation_failure, :bad_argument} + end + + test "validate: a single absolute string with a valid unit fails ", context do + assert @command.validate(["absolute","manyGB"], context[:opts]) == {:validation_failure, :bad_argument} + end + + test "run: a single absolute integer with memory units return ok", context do + memory_units() + |> Enum.each(fn mu -> + arg = "10#{mu}" + assert @command.run(["absolute",arg], context[:opts]) == :ok + assert status()[:vm_memory_high_watermark] == {:absolute, memory_unit_absolute(10, mu)} + end) + end + + test "run: low watermark sets alarm", context do + old_watermark = status()[:vm_memory_high_watermark] + on_exit(fn() -> + args = case old_watermark do + {:absolute, val} -> ["absolute", to_string(val)]; + other -> [to_string(other)] + end + @command.run(args, context[:opts]) + end) + ## this will trigger an alarm + @command.run(["absolute", "2000"], context[:opts]) + + assert [:memory] == alarm_types(status()[:alarms]) + end + + test "banner: absolute memory request prints info message", context do + assert @command.banner(["absolute", "10"], context[:opts]) + =~ ~r/Setting memory threshold on #{get_rabbit_hostname()} to 10 bytes .../ + + assert @command.banner(["absolute", "-10"], context[:opts]) + =~ ~r/Setting memory threshold on #{get_rabbit_hostname()} to -10 bytes .../ + + assert @command.banner(["absolute", "sandwich"], context[:opts]) + =~ ~r/Setting memory threshold on #{get_rabbit_hostname()} to sandwich bytes .../ + end + + test "banner, relative memory", context do + assert @command.banner(["0.7"], context[:opts]) + =~ ~r/Setting memory threshold on #{get_rabbit_hostname()} to 0.7 .../ + + assert @command.banner(["-0.7"], context[:opts]) + =~ ~r/Setting memory threshold on #{get_rabbit_hostname()} to -0.7 .../ + + assert @command.banner(["sandwich"], context[:opts]) + =~ ~r/Setting memory threshold on #{get_rabbit_hostname()} to sandwich .../ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs b/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs new file mode 100644 index 0000000000..153c136c4b --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs @@ -0,0 +1,53 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule ShutdownCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.ShutdownCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 15}} + end + + test "validate: accepts no arguments", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "validate: in wait mode, checks if local and target node hostnames match" do + assert match?({:validation_failure, {:unsupported_target, _}}, + @command.validate([], %{wait: true, node: :'rabbit@some.remote.hostname'})) + end + + test "validate: in wait mode, always assumes @localhost nodes are local" do + assert @command.validate([], %{wait: true, node: :rabbit@localhost}) == :ok + end + + test "validate: in no wait mode, passes unconditionally", context do + assert @command.validate([], Map.merge(%{wait: false}, context[:opts])) == :ok + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, wait: false, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "empty banner", context do + nil = @command.banner([], context[:opts]) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/start_app_command_test.exs b/deps/rabbitmq_cli/test/ctl/start_app_command_test.exs new file mode 100644 index 0000000000..bdd8632842 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/start_app_command_test.exs @@ -0,0 +1,50 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule StartAppCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.StartAppCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: request to an active node succeeds", context do + node = RabbitMQ.CLI.Core.Helpers.normalise_node(context[:node], :shortnames) + stop_rabbitmq_app() + refute :rabbit_misc.rpc_call(node, :rabbit, :is_running, []) + assert @command.run([], context[:opts]) + assert :rabbit_misc.rpc_call(node, :rabbit, :is_running, []) + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Starting node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/status_command_test.exs b/deps/rabbitmq_cli/test/ctl/status_command_test.exs new file mode 100644 index 0000000000..03ab6cb8fc --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/status_command_test.exs @@ -0,0 +1,40 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule StatusCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.StatusCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), timeout: 60_000}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: request to a named, active node succeeds", context do + assert @command.run([], context[:opts])[:pid] != nil + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Status of node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/stop_app_command_test.exs b/deps/rabbitmq_cli/test/ctl/stop_app_command_test.exs new file mode 100644 index 0000000000..60551b2189 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/stop_app_command_test.exs @@ -0,0 +1,49 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule StopAppCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.StopAppCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: request to an active node succeeds", context do + node = RabbitMQ.CLI.Core.Helpers.normalise_node(context[:node], :shortnames) + assert :rabbit_misc.rpc_call(node, :rabbit, :is_running, []) + assert @command.run([], context[:opts]) + refute :rabbit_misc.rpc_call(node, :rabbit, :is_running, []) + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Stopping rabbit application on node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/stop_command_test.exs b/deps/rabbitmq_cli/test/ctl/stop_command_test.exs new file mode 100644 index 0000000000..2f1dca2eae --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/stop_command_test.exs @@ -0,0 +1,52 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule StopCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.StopCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + :ok + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname(), + idempotent: false}} + end + + test "validate accepts no arguments", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "validate accepts a PID file path", context do + assert @command.validate(["/path/to/pidfile.pid"], context[:opts]) == :ok + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["/path/to/pidfile.pid", "extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + # NB: as this commands shuts down the Erlang vm it isn't really practical to test it here + + test "run: request to a non-existent node with --idempotent=false returns a badrpc" do + opts = %{node: :jake@thedog, idempotent: false, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "run: request to a non-existent node with --idempotent returns ok" do + opts = %{node: :jake@thedog, idempotent: true, timeout: 200} + assert match?({:ok, _}, @command.run([], opts)) + end + + test "banner", context do + assert @command.banner([], context[:opts]) =~ ~r/Stopping and halting node #{get_rabbit_hostname()}/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/suspend_listeners_command_test.exs b/deps/rabbitmq_cli/test/ctl/suspend_listeners_command_test.exs new file mode 100644 index 0000000000..602cdf9f8b --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/suspend_listeners_command_test.exs @@ -0,0 +1,67 @@ +## The contents of this file are subject to the Mozilla Public License +## Version 1.1 (the "License"); you may not use this file except in +## compliance with the License. You may obtain a copy of the License +## at https://www.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +## the License for the specific language governing rights and +## limitations under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developer of the Original Code is GoPivotal, Inc. +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule SuspendListenersCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SuspendListenersCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + resume_all_client_listeners() + + node_name = get_rabbit_hostname() + on_exit(fn -> + resume_all_client_listeners() + close_all_connections(node_name) + end) + + {:ok, opts: %{node: node_name, timeout: 30_000}} + end + + setup do + {:ok, opts: %{node: get_rabbit_hostname()}} + end + + test "merge_defaults: merges no defaults" do + assert @command.merge_defaults([], %{}) == {[], %{}} + end + + test "validate: accepts no arguments", context do + assert @command.validate([], context[:opts]) == :ok + end + + test "validate: with extra arguments returns an arg count error", context do + assert @command.validate(["extra"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "run: request to a non-existent node returns a badrpc" do + opts = %{node: :jake@thedog, timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + test "run: suspends all client TCP listeners so no new client connects are accepted", context do + assert @command.run([], Map.merge(context[:opts], %{timeout: 5_000})) == :ok + + expect_client_connection_failure() + resume_all_client_listeners() + + # implies a successful connection + with_channel("/", fn _ -> :ok end) + close_all_connections(get_rabbit_hostname()) + end +end diff --git a/deps/rabbitmq_cli/test/ctl/sync_queue_command_test.exs b/deps/rabbitmq_cli/test/ctl/sync_queue_command_test.exs new file mode 100644 index 0000000000..3d3f866dd0 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/sync_queue_command_test.exs @@ -0,0 +1,64 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule SyncQueueCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.SyncQueueCommand + + @vhost "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{ + node: get_rabbit_hostname(), + vhost: @vhost + }} + end + + test "validate: specifying no queue name is reported as an error", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate: specifying two queue names is reported as an error", context do + assert @command.validate(["q1", "q2"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: specifying three queue names is reported as an error", context do + assert @command.validate(["q1", "q2", "q3"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "validate: specifying one queue name succeeds", context do + assert @command.validate(["q1"], context[:opts]) == :ok + end + + test "run: request to a non-existent RabbitMQ node returns a nodedown" do + opts = %{node: :jake@thedog, vhost: @vhost, timeout: 200} + assert match?({:badrpc, _}, @command.run(["q1"], opts)) + end + + test "banner", context do + s = @command.banner(["q1"], context[:opts]) + + assert s =~ ~r/Synchronising queue/ + assert s =~ ~r/q1/ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/trace_off_command_test.exs b/deps/rabbitmq_cli/test/ctl/trace_off_command_test.exs new file mode 100644 index 0000000000..0ea53774cb --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/trace_off_command_test.exs @@ -0,0 +1,78 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule TraceOffCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.TraceOffCommand + + @test_vhost "test" + @default_vhost "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost(@test_vhost) + + on_exit([], fn -> + delete_vhost(@test_vhost) + end) + + :ok + end + + setup context do + trace_on(context[:vhost]) + on_exit(context, fn -> trace_off(context[:vhost]) end) + {:ok, opts: %{node: get_rabbit_hostname(), vhost: context[:vhost]}} + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: wrong number of arguments triggers arg count error" do + assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: on an active node, trace_off command works on default" do + opts = %{node: get_rabbit_hostname()} + opts_with_vhost = %{node: get_rabbit_hostname(), vhost: "/"} + trace_on(@default_vhost) + + assert @command.merge_defaults([], opts) == {[], opts_with_vhost} + end + + test "run: on an invalid RabbitMQ node, return a nodedown" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag target: get_rabbit_hostname(), vhost: @default_vhost + test "run: calls to trace_off are idempotent", context do + @command.run([], context[:opts]) + assert @command.run([], context[:opts]) == {:ok, "Trace disabled for vhost #{@default_vhost}"} + end + + @tag vhost: @test_vhost + test "run: on an active node, trace_off command works on named vhost", context do + assert @command.run([], context[:opts]) == {:ok, "Trace disabled for vhost #{@test_vhost}"} + end + + @tag vhost: "toast" + test "run: Turning tracing off on invalid host returns successfully", context do + assert @command.run([], context[:opts]) == {:ok, "Trace disabled for vhost toast"} + end + + @tag vhost: @default_vhost + test "banner", context do + assert @command.banner([], context[:opts]) + =~ ~r/Stopping tracing for vhost "#{context[:vhost]}" .../ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/trace_on_command_test.exs b/deps/rabbitmq_cli/test/ctl/trace_on_command_test.exs new file mode 100644 index 0000000000..4db58772a1 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/trace_on_command_test.exs @@ -0,0 +1,79 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule TraceOnCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.TraceOnCommand + + @test_vhost "test" + @default_vhost "/" + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + add_vhost(@test_vhost) + + on_exit([], fn -> + delete_vhost(@test_vhost) + end) + + :ok + end + + setup context do + on_exit(context, fn -> trace_off(context[:vhost]) end) + {:ok, opts: %{node: get_rabbit_hostname(), vhost: context[:vhost]}} + end + + test "merge_defaults: on an active node, trace_on command works on default" do + opts = %{node: get_rabbit_hostname()} + opts_with_vhost = %{node: get_rabbit_hostname(), vhost: "/"} + + assert @command.merge_defaults([], opts) == {[], opts_with_vhost} + + trace_off(@default_vhost) + end + + test "merge_defaults: defaults can be overridden" do + assert @command.merge_defaults([], %{}) == {[], %{vhost: "/"}} + assert @command.merge_defaults([], %{vhost: "non_default"}) == {[], %{vhost: "non_default"}} + end + + test "validate: wrong number of arguments triggers arg count error" do + assert @command.validate(["extra"], %{}) == {:validation_failure, :too_many_args} + end + + test "run: on an invalid RabbitMQ node, return a nodedown" do + opts = %{node: :jake@thedog, vhost: "/", timeout: 200} + + assert match?({:badrpc, _}, @command.run([], opts)) + end + + @tag vhost: @default_vhost + test "run: calls to trace_on are idempotent", context do + @command.run([], context[:opts]) + assert @command.run([], context[:opts]) == {:ok, "Trace enabled for vhost #{@default_vhost}"} + end + + @tag vhost: @test_vhost + test "run: on an active node, trace_on command works on named vhost", context do + assert @command.run([], context[:opts]) == {:ok, "Trace enabled for vhost #{@test_vhost}"} + end + + @tag vhost: "toast" + test "run: Turning tracing on on invalid host returns successfully", context do + assert @command.run([], context[:opts]) == {:ok, "Trace enabled for vhost toast"} + end + + @tag vhost: @default_vhost + test "banner", context do + assert @command.banner([], context[:opts]) + =~ ~r/Starting tracing for vhost "#{context[:vhost]}" .../ + end +end diff --git a/deps/rabbitmq_cli/test/ctl/update_cluster_nodes_command_test.exs b/deps/rabbitmq_cli/test/ctl/update_cluster_nodes_command_test.exs new file mode 100644 index 0000000000..b94c21f1be --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/update_cluster_nodes_command_test.exs @@ -0,0 +1,80 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2016-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule UpdateClusterNodesCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.UpdateClusterNodesCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + :ok + end + + setup do + {:ok, opts: %{ + node: get_rabbit_hostname() + }} + end + + test "validate: providing too few arguments fails validation", context do + assert @command.validate([], context[:opts]) == + {:validation_failure, :not_enough_args} + end + + test "validate: providing too many arguments fails validation", context do + assert @command.validate(["a", "b", "c"], context[:opts]) == + {:validation_failure, :too_many_args} + end + + test "run: specifying self as seed node fails validation", context do + stop_rabbitmq_app() + assert match?( + {:error, :cannot_cluster_node_with_itself}, + @command.run([context[:opts][:node]], context[:opts])) + start_rabbitmq_app() + end + + test "run: request to an unreachable node returns a badrpc", context do + opts = %{ + node: :jake@thedog, + timeout: 200 + } + assert match?( + {:badrpc, :nodedown}, + @command.run([context[:opts][:node]], opts)) + end + + test "run: specifying an unreachable node as seed returns a badrpc", context do + stop_rabbitmq_app() + assert match?( + {:badrpc_multi, _, [_]}, + @command.run([:jake@thedog], context[:opts])) + start_rabbitmq_app() + end + + test "banner", context do + assert @command.banner(["a"], context[:opts]) =~ + ~r/Will seed #{get_rabbit_hostname()} from a on next start/ + end + + test "output mnesia is running error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, + "Mnesia is still running on node " <> _}, + @command.output({:error, :mnesia_unexpectedly_running}, context[:opts])) + + end +end diff --git a/deps/rabbitmq_cli/test/ctl/version_command_test.exs b/deps/rabbitmq_cli/test/ctl/version_command_test.exs new file mode 100644 index 0000000000..76216b6cf0 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/version_command_test.exs @@ -0,0 +1,24 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule VersionCommandTest do + use ExUnit.Case + + @command RabbitMQ.CLI.Ctl.Commands.VersionCommand + + test "merge_defaults: merges no defaults" do + assert @command.merge_defaults([], %{}) == {[], %{}} + end + + test "validate: treats positional arguments as a failure" do + assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args} + end + + test "validate: treats empty positional arguments and default switches as a success" do + assert @command.validate([], %{}) == :ok + end +end diff --git a/deps/rabbitmq_cli/test/ctl/wait_command_test.exs b/deps/rabbitmq_cli/test/ctl/wait_command_test.exs new file mode 100644 index 0000000000..c1fd604245 --- /dev/null +++ b/deps/rabbitmq_cli/test/ctl/wait_command_test.exs @@ -0,0 +1,114 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + + +defmodule WaitCommandTest do + use ExUnit.Case, async: false + import TestHelper + + @command RabbitMQ.CLI.Ctl.Commands.WaitCommand + + setup_all do + RabbitMQ.CLI.Core.Distribution.start() + start_rabbitmq_app() + + on_exit([], fn -> + start_rabbitmq_app() + end) + + RabbitMQ.CLI.Core.Distribution.start() + rabbitmq_home = :rabbit_misc.rpc_call(get_rabbit_hostname(), :code, :lib_dir, [:rabbit]) + + {:ok, opts: %{node: get_rabbit_hostname(), + rabbitmq_home: rabbitmq_home, + timeout: 500}} + end + + + test "validate: cannot have both pid and pidfile", context do + {:validation_failure, "Cannot specify both pid and pidfile"} = + @command.validate(["pid_file"], Map.merge(context[:opts], %{pid: 123})) + end + + test "validate: should have either pid or pidfile", context do + {:validation_failure, "No pid or pidfile specified"} = + @command.validate([], context[:opts]) + end + + test "validate: with more than one argument returns an arg count error", context do + assert @command.validate(["pid_file", "extra"], context[:opts]) == {:validation_failure, :too_many_args} + end + + test "run: times out waiting for non-existent pid file", context do + {:error, {:timeout, _}} = @command.run(["pid_file"], context[:opts]) |> Enum.to_list |> List.last + end + + test "run: fails if pid process does not exist", context do + non_existent_pid = get_non_existent_os_pid() + {:error, :process_not_running} = + @command.run([], Map.merge(context[:opts], %{pid: non_existent_pid})) + |> Enum.to_list + |> List.last + end + + test "run: times out if unable to communicate with the node", context do + pid = String.to_integer(System.get_pid()) + {:error, {:timeout, _}} = + @command.run([], Map.merge(context[:opts], %{pid: pid, node: :nonode@nohost})) + |> Enum.to_list + |> List.last + end + + test "run: happy path", context do + pid = :erlang.list_to_integer(:rpc.call(context[:opts][:node], :os, :getpid, [])) + output = @command.run([], Map.merge(context[:opts], %{pid: pid})) + assert_stream_without_errors(output) + end + + test "run: happy path in quiet mode", context do + pid = :erlang.list_to_integer(:rpc.call(context[:opts][:node], :os, :getpid, [])) + output = @command.run([], Map.merge(context[:opts], %{pid: pid, quiet: true})) + [] = Enum.to_list(output) + end + + test "no banner", context do + nil = @command.banner([], context[:opts]) + end + + test "output: process not running error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, "Error: process is not running."}, + @command.output({:error, :process_not_running}, context[:opts])) + end + + test "output: garbage in pid file error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, "Error: garbage in pid file."}, + @command.output({:error, {:garbage_in_pid_file, "somefile"}}, context[:opts])) + end + + test "output: could not read pid error", context do + exit_code = RabbitMQ.CLI.Core.ExitCodes.exit_software + assert match?({:error, ^exit_code, "Error: could not read pid. Detail: something wrong"}, + @command.output({:error, {:could_not_read_pid, "something wrong"}}, context[:opts])) + end + + test "output: default output is fine", context do + assert match?({:error, "message"}, @command.output({:error, "message"}, context[:opts])) + assert match?({:error, :message}, @command.output({:error, :message}, context[:opts])) + assert match?({:error, :message}, @command.output(:message, context[:opts])) + assert match?({:ok, "ok"}, @command.output({:ok, "ok"}, context[:opts])) + assert match?(:ok, @command.output(:ok, context[:opts])) + assert match?({:ok, "ok"}, @command.output("ok", context[:opts])) + end + + def get_non_existent_os_pid(pid \\ 2) do + case :rabbit_misc.is_os_process_alive(to_charlist(pid)) do + true -> get_non_existent_os_pid(pid + 1) + false -> pid + end + end +end |