summaryrefslogtreecommitdiff
path: root/deps/rabbitmq_cli/test
diff options
context:
space:
mode:
Diffstat (limited to 'deps/rabbitmq_cli/test')
-rw-r--r--deps/rabbitmq_cli/test/core/args_processing_test.exs90
-rw-r--r--deps/rabbitmq_cli/test/core/auto_complete_test.exs85
-rw-r--r--deps/rabbitmq_cli/test/core/command_modules_test.exs202
-rw-r--r--deps/rabbitmq_cli/test/core/default_output_test.exs114
-rw-r--r--deps/rabbitmq_cli/test/core/distribution_test.exs48
-rw-r--r--deps/rabbitmq_cli/test/core/helpers_test.exs140
-rw-r--r--deps/rabbitmq_cli/test/core/information_unit_test.exs44
-rw-r--r--deps/rabbitmq_cli/test/core/json_stream_test.exs24
-rw-r--r--deps/rabbitmq_cli/test/core/listeners_test.exs64
-rw-r--r--deps/rabbitmq_cli/test/core/node_name_test.exs73
-rw-r--r--deps/rabbitmq_cli/test/core/os_pid_test.exs54
-rw-r--r--deps/rabbitmq_cli/test/core/parser_test.exs369
-rw-r--r--deps/rabbitmq_cli/test/core/rpc_stream_test.exs94
-rw-r--r--deps/rabbitmq_cli/test/core/table_formatter_test.exs46
-rw-r--r--deps/rabbitmq_cli/test/ctl/add_user_command_test.exs86
-rw-r--r--deps/rabbitmq_cli/test/ctl/add_vhost_command_test.exs68
-rw-r--r--deps/rabbitmq_cli/test/ctl/authenticate_user_command_test.exs81
-rw-r--r--deps/rabbitmq_cli/test/ctl/autocomplete_command_test.exs52
-rw-r--r--deps/rabbitmq_cli/test/ctl/await_online_nodes_command_test.exs44
-rw-r--r--deps/rabbitmq_cli/test/ctl/await_startup_command_test.exs49
-rw-r--r--deps/rabbitmq_cli/test/ctl/cancel_sync_command_test.exs64
-rw-r--r--deps/rabbitmq_cli/test/ctl/change_cluster_node_type_command_test.exs84
-rw-r--r--deps/rabbitmq_cli/test/ctl/change_password_command_test.exs80
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_global_parameter_command_test.exs86
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_operator_policy_command_test.exs127
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_parameter_command_test.exs138
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_password_command_test.exs64
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_permissions_command_test.exs100
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_policy_command_test.exs129
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_topic_permissions_command_test.exs107
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_user_limits_command_test.exs115
-rw-r--r--deps/rabbitmq_cli/test/ctl/clear_vhost_limits_command_test.exs103
-rw-r--r--deps/rabbitmq_cli/test/ctl/close_all_connections_command_test.exs147
-rw-r--r--deps/rabbitmq_cli/test/ctl/close_connection_command_test.exs96
-rw-r--r--deps/rabbitmq_cli/test/ctl/cluster_status_command_test.exs50
-rw-r--r--deps/rabbitmq_cli/test/ctl/decode_command_test.exs95
-rw-r--r--deps/rabbitmq_cli/test/ctl/delete_queue_command_test.exs119
-rw-r--r--deps/rabbitmq_cli/test/ctl/delete_user_command_test.exs59
-rw-r--r--deps/rabbitmq_cli/test/ctl/delete_vhost_command_test.exs67
-rw-r--r--deps/rabbitmq_cli/test/ctl/enable_feature_flag_test.exs70
-rw-r--r--deps/rabbitmq_cli/test/ctl/encode_command_test.exs92
-rw-r--r--deps/rabbitmq_cli/test/ctl/environment_command_test.exs45
-rw-r--r--deps/rabbitmq_cli/test/ctl/eval_command_test.exs74
-rw-r--r--deps/rabbitmq_cli/test/ctl/eval_file_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/ctl/exec_command_test.exs47
-rw-r--r--deps/rabbitmq_cli/test/ctl/export_definitions_command_test.exs138
-rw-r--r--deps/rabbitmq_cli/test/ctl/force_boot_command_test.exs63
-rw-r--r--deps/rabbitmq_cli/test/ctl/force_gc_command_test.exs46
-rw-r--r--deps/rabbitmq_cli/test/ctl/force_reset_command_test.exs68
-rw-r--r--deps/rabbitmq_cli/test/ctl/forget_cluster_node_command_test.exs132
-rw-r--r--deps/rabbitmq_cli/test/ctl/help_command_test.exs76
-rw-r--r--deps/rabbitmq_cli/test/ctl/import_definitions_command_test.exs88
-rw-r--r--deps/rabbitmq_cli/test/ctl/join_cluster_command_test.exs104
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_bindings_command_test.exs85
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_channels_command_test.exs118
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_ciphers_command_test.exs29
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_connections_command_test.exs90
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_consumers_command_test.exs213
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_exchanges_command_test.exs160
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_feature_flags_command_test.exs122
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_global_parameters_command_test.exs86
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_hashes_command_test.exs29
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_operator_policies_command_test.exs142
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_parameters_command_test.exs154
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_permissions_command_test.exs92
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_policies_command_test.exs144
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_queues_command_test.exs145
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_topic_permissions_command_test.exs85
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_user_limits_command_test.exs103
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_user_permissions_command_test.exs91
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_user_topic_permissions_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_users_command_test.exs74
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_vhost_limits_command_test.exs111
-rw-r--r--deps/rabbitmq_cli/test/ctl/list_vhosts_command_test.exs160
-rw-r--r--deps/rabbitmq_cli/test/ctl/node_health_check_command_test.exs65
-rw-r--r--deps/rabbitmq_cli/test/ctl/ping_command_test.exs56
-rw-r--r--deps/rabbitmq_cli/test/ctl/purge_queue_command_test.exs88
-rw-r--r--deps/rabbitmq_cli/test/ctl/rename_cluster_node_command_test.exs102
-rw-r--r--deps/rabbitmq_cli/test/ctl/report_command_test.exs44
-rw-r--r--deps/rabbitmq_cli/test/ctl/reset_command_test.exs68
-rw-r--r--deps/rabbitmq_cli/test/ctl/restart_vhost_command_test.exs95
-rw-r--r--deps/rabbitmq_cli/test/ctl/resume_listeners_command_test.exs67
-rw-r--r--deps/rabbitmq_cli/test/ctl/rotate_logs_command_test.exs40
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_cluster_name_command_test.exs63
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_disk_free_limit_command_test.exs173
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_global_parameter_command_test.exs82
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_log_level_command_test.exs44
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_operator_policy_command_test.exs153
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_parameter_command_test.exs136
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_permissions_command_test.exs114
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_policy_command_test.exs217
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_topic_permissions_command_test.exs114
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_user_limits_command_test.exs137
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_user_tags_command_test.exs144
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_vhost_limits_command_test.exs137
-rw-r--r--deps/rabbitmq_cli/test/ctl/set_vm_memory_high_watermark_command_test.exs162
-rw-r--r--deps/rabbitmq_cli/test/ctl/shutdown_command_test.exs53
-rw-r--r--deps/rabbitmq_cli/test/ctl/start_app_command_test.exs50
-rw-r--r--deps/rabbitmq_cli/test/ctl/status_command_test.exs40
-rw-r--r--deps/rabbitmq_cli/test/ctl/stop_app_command_test.exs49
-rw-r--r--deps/rabbitmq_cli/test/ctl/stop_command_test.exs52
-rw-r--r--deps/rabbitmq_cli/test/ctl/suspend_listeners_command_test.exs67
-rw-r--r--deps/rabbitmq_cli/test/ctl/sync_queue_command_test.exs64
-rw-r--r--deps/rabbitmq_cli/test/ctl/trace_off_command_test.exs78
-rw-r--r--deps/rabbitmq_cli/test/ctl/trace_on_command_test.exs79
-rw-r--r--deps/rabbitmq_cli/test/ctl/update_cluster_nodes_command_test.exs80
-rw-r--r--deps/rabbitmq_cli/test/ctl/version_command_test.exs24
-rw-r--r--deps/rabbitmq_cli/test/ctl/wait_command_test.exs114
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/alarms_command_test.exs69
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_alarms_command_test.exs118
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_local_alarms_command_test.exs111
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_port_connectivity_command_test.exs59
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_port_listener_command_test.exs62
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_protocol_listener_command_test.exs68
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_running_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/check_virtual_hosts_command_test.exs50
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/cipher_suites_command_test.exs101
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/command_line_arguments_command_test.exs44
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/consume_event_stream_command_test.exs73
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/disable_auth_attempt_source_tracking_command_test.exs39
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/discover_peers_command_test.exs39
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/enable_auth_attempt_source_tracking_command_test.exs39
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/erlang_cookie_hash_command_test.exs50
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/erlang_cookie_sources_command_test.exs37
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/erlang_version_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/is_booting_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/is_running_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/list_network_interfaces_command_test.exs39
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/list_node_auth_attempt_stats_command_test.exs39
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/listeners_command_test.exs78
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/log_location_command_test.exs98
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/log_tail_command_test.exs115
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/log_tail_stream_command_test.exs107
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/maybe_stuck_command_test.exs48
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/memory_breakdown_command_test.exs72
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/observer_command_test.exs44
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/os_env_command_test.exs62
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/resolve_hostname_command_test.exs85
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/resolver_info_command_test.exs65
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/runtime_thread_stats_command_test.exs50
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/schema_info_command_test.exs69
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/server_version_command_test.exs48
-rw-r--r--deps/rabbitmq_cli/test/diagnostics/tls_versions_command_test.exs60
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/definitions.json40
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/empty_pidfile.pid0
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/invalid_erl_expression.escript1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/invalid_pidfile.pid1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/loaded_applications.escript1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/valid_erl_expression.escript1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/valid_pidfile.pid1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/files/valid_pidfile_with_spaces.pid1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/.gitignore1
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.1.0.ezbin0 -> 3281 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.2.0.ezbin0 -> 3281 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_02-0.1.0.ezbin0 -> 3288 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-02/mock_rabbitmq_plugins_02-0.2.0.ezbin0 -> 3288 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-03/mock_rabbitmq_plugins_03-0.1.0.ezbin0 -> 2518 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-04/mock_rabbitmq_plugins_04.ezbin0 -> 2460 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_7-0.1.0.ezbin0 -> 3357 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0.ezbin0 -> 3363 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugin_for_3_8.app10
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_app.beambin0 -> 1316 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_sup.beambin0 -> 1460 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_01-0.1.0.ezbin0 -> 3276 bytes
-rw-r--r--deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_02-0.1.0.ezbin0 -> 3283 bytes
-rw-r--r--deps/rabbitmq_cli/test/json_formatting.exs59
-rw-r--r--deps/rabbitmq_cli/test/plugins/directories_command_test.exs103
-rw-r--r--deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs187
-rw-r--r--deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs243
-rw-r--r--deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs103
-rw-r--r--deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs235
-rw-r--r--deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs45
-rw-r--r--deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs157
-rw-r--r--deps/rabbitmq_cli/test/queues/add_member_command_test.exs49
-rw-r--r--deps/rabbitmq_cli/test/queues/check_if_node_is_mirror_sync_critical_command_test.exs40
-rw-r--r--deps/rabbitmq_cli/test/queues/check_if_node_is_quorum_critical_command_test.exs40
-rw-r--r--deps/rabbitmq_cli/test/queues/delete_member_command_test.exs49
-rw-r--r--deps/rabbitmq_cli/test/queues/grow_command_test.exs67
-rw-r--r--deps/rabbitmq_cli/test/queues/peek_command_test.exs59
-rw-r--r--deps/rabbitmq_cli/test/queues/quorum_status_command_test.exs45
-rw-r--r--deps/rabbitmq_cli/test/queues/reclaim_quorum_memory_command_test.exs45
-rw-r--r--deps/rabbitmq_cli/test/queues/shrink_command_test.exs55
-rw-r--r--deps/rabbitmq_cli/test/rabbitmqctl_test.exs301
-rw-r--r--deps/rabbitmq_cli/test/streams/add_replica_command_test.exs57
-rw-r--r--deps/rabbitmq_cli/test/streams/delete_replica_command_test.exs57
-rw-r--r--deps/rabbitmq_cli/test/streams/set_stream_retention_policy_command_test.exs63
-rw-r--r--deps/rabbitmq_cli/test/test_helper.exs620
-rw-r--r--deps/rabbitmq_cli/test/upgrade/await_online_quorum_plus_one_command_test.exs45
-rw-r--r--deps/rabbitmq_cli/test/upgrade/await_online_synchronized_mirror_command_test.exs45
-rw-r--r--deps/rabbitmq_cli/test/upgrade/drain_command_test.exs57
-rw-r--r--deps/rabbitmq_cli/test/upgrade/post_upgrade_command_test.exs49
-rw-r--r--deps/rabbitmq_cli/test/upgrade/revive_command_test.exs57
192 files changed, 15443 insertions, 0 deletions
diff --git a/deps/rabbitmq_cli/test/core/args_processing_test.exs b/deps/rabbitmq_cli/test/core/args_processing_test.exs
new file mode 100644
index 0000000000..18c67d3a4a
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/args_processing_test.exs
@@ -0,0 +1,90 @@
+## 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 ArgsProcessingTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ defp list_commands() do
+ [
+ RabbitMQ.CLI.Ctl.Commands.ListBindingsCommand,
+ RabbitMQ.CLI.Ctl.Commands.ListChannelsCommand,
+ RabbitMQ.CLI.Ctl.Commands.ListConnectionsCommand,
+ RabbitMQ.CLI.Ctl.Commands.ListConsumersCommand,
+ RabbitMQ.CLI.Ctl.Commands.ListExchangesCommand,
+ RabbitMQ.CLI.Ctl.Commands.ListQueuesCommand,
+ RabbitMQ.CLI.Ctl.Commands.ListVhostsCommand
+ ]
+ end
+
+ defp all_commands() do
+ RabbitMQ.CLI.Core.CommandModules.load_commands(:all, %{})
+ |> Map.values
+ end
+
+ defp line_filter([_, description]) do
+ Regex.match?(~r/must be one of/, description)
+ end
+ defp line_filter(line) do
+ Regex.match?(~r/must be one of/, line)
+ end
+
+ 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(), timeout: 50_000, vhost: "/"}}
+ end
+
+ test "defaults are merged with positinal args", _context do
+ commands = all_commands()
+ Enum.each(commands,
+ fn(command) ->
+ command.merge_defaults([], %{})
+ command.merge_defaults(["arg"], %{})
+ command.merge_defaults(["two", "args"], %{})
+ command.merge_defaults(["even", "more", "args"], %{})
+
+ command.merge_defaults([], %{unknown: "option"})
+ command.merge_defaults(["arg"], %{unknown: "option"})
+ end)
+ end
+
+ # this test parses info keys mentioned in the usage_additional section
+ # and makes sure they pass validation, including when separated by a comma
+ # or a mix of commas and spaces
+ test "comma-separated info items are supported", context do
+ commands = list_commands()
+ Enum.each(commands, fn(command) ->
+ items_usage = case command.usage_additional() do
+ # find the line with info items, ignore the rest
+ list when is_list(list) ->
+ # list items can be strings or pairs
+ Enum.filter(list, &line_filter/1) |> List.first |> Enum.join(" ")
+ string ->
+ string
+ end
+ # info_item, info_item2, …
+ case Regex.run(~r/.*one of (.*)$/, items_usage, [capture: :all_but_first]) do
+ nil ->
+ throw "Command #{command} does not list info items in usage_additional or the format has changed. Output: #{items_usage}"
+ [info_items] ->
+ :ok = command.validate([info_items], context[:opts])
+ :ok = command.validate(String.split(info_items, " "), context[:opts])
+ run_command_ok(command, [info_items], context[:opts])
+ run_command_ok(command, String.split(info_items, " "), context[:opts])
+ end
+ end)
+ end
+
+ def run_command_ok(command, args_init, options_init) do
+ {args, options} = command.merge_defaults(args_init, options_init)
+ assert_stream_without_errors(command.run(args, options))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/auto_complete_test.exs b/deps/rabbitmq_cli/test/core/auto_complete_test.exs
new file mode 100644
index 0000000000..d410ec6640
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/auto_complete_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 AutoCompleteTest do
+ use ExUnit.Case, async: false
+
+ @subject RabbitMQ.CLI.AutoComplete
+
+
+ test "Auto-completes a command" do
+ ["canis_aureus", "canis_latrans", "canis_lupus"] = @subject.complete("rabbitmqctl", ["canis"])
+ ["canis_aureus", "canis_latrans", "canis_lupus"] = @subject.complete("rabbitmqctl", ["canis_"])
+ ["canis_latrans", "canis_lupus"] = @subject.complete("rabbitmqctl", ["canis_l"])
+ ["canis_latrans"] = @subject.complete("rabbitmqctl", ["canis_la"])
+ ["canis_aureus"] = @subject.complete("rabbitmqctl", ["canis_a"])
+ ["canis_aureus"] = @subject.complete("rabbitmqctl", ["--node", "foo", "--quet", "canis_a"])
+ end
+
+ test "Auto-completes default options if command is not specified" do
+ ["--vhost"] = @subject.complete("rabbitmqctl", ["--vh"])
+ ## Prints script_name as script-name
+ ["--script-name"] = @subject.complete("rabbitmqctl", ["--script"])
+ ["--script-name"] = @subject.complete("rabbitmqctl", ["--node", "foo", "--script"])
+ end
+
+ test "Auto-completes the command options if full command is specified" do
+ ["--colour", "--dingo", "--dog"] = @subject.complete("rabbitmqctl", ["canis_lupus", "-"])
+ ["--colour", "--dingo", "--dog"] = @subject.complete("rabbitmqctl", ["canis_lupus", "--"])
+ ["--dingo", "--dog"] = @subject.complete("rabbitmqctl", ["canis_lupus", "--d"])
+ end
+
+ test "Auto-completes scoped command" do
+ ["enable"] = @subject.complete("rabbitmq-plugins", ["enab"])
+ scopes = Application.get_env(:rabbitmqctl, :scopes)
+ scopes_with_wolf = Keyword.put(scopes, :rabbitmq_wolf, :wolf)
+ Application.put_env(:rabbitmqctl, :scopes, scopes_with_wolf)
+ on_exit(fn() ->
+ Application.put_env(:rabbitmqctl, :scopes, scopes)
+ end)
+
+ ["canis_aureus", "canis_latrans", "canis_lupus"] = @subject.complete("rabbitmq_wolf", ["canis"])
+ end
+
+ test "Auto-completes scoped command with --script-name flag" do
+ ["enable"] = @subject.complete("rabbitmqctl", ["--script-name", "rabbitmq-plugins", "enab"])
+ end
+end
+
+defmodule RabbitMQ.CLI.Wolf.Commands.CanisLupusCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["canis_lupus"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+ def switches(), do: [colour: :string, dingo: :boolean, dog: :boolean]
+ def scopes, do: [:ctl, :wolf]
+end
+
+defmodule RabbitMQ.CLI.Wolf.Commands.CanisLatransCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["canis_latrans"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+ def scopes, do: [:ctl, :wolf]
+end
+
+defmodule RabbitMQ.CLI.Wolf.Commands.CanisAureusCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["canis_aureus"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+ def scopes, do: [:ctl, :wolf]
+end
diff --git a/deps/rabbitmq_cli/test/core/command_modules_test.exs b/deps/rabbitmq_cli/test/core/command_modules_test.exs
new file mode 100644
index 0000000000..8617415a22
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/command_modules_test.exs
@@ -0,0 +1,202 @@
+## 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 CommandModulesTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @subject RabbitMQ.CLI.Core.CommandModules
+
+ setup_all do
+ on_exit(fn ->
+ set_scope(:none)
+ Application.put_env(:rabbitmqctl, :commands, nil)
+ end)
+ :ok
+ end
+
+ test "command modules has existing commands" do
+ assert @subject.load_commands(:all, %{})["duck"] ==
+ RabbitMQ.CLI.Ctl.Commands.DuckCommand
+ end
+
+ test "command with multiple underscores shows up in map" do
+ assert @subject.load_commands(:all, %{})["gray_goose"] ==
+ RabbitMQ.CLI.Ctl.Commands.GrayGooseCommand
+ end
+
+ test "command modules does not have non-existent commands" do
+ assert @subject.load_commands(:all, %{})["usurper"] == nil
+ end
+
+ test "non command modules do not show in command map" do
+ assert @subject.load_commands(:all, %{})["ugly_duckling"] == nil
+ end
+
+ test "loaded commands are saved in env variable" do
+ set_scope(:ctl)
+ commands = @subject.module_map
+ assert commands == @subject.module_map
+ assert commands == Application.get_env(:rabbitmqctl, :commands)
+ end
+
+ test "load commands for current scope" do
+ set_scope(:ctl)
+ commands = @subject.load(%{})
+ assert commands == @subject.load_commands(:ctl, %{})
+
+ assert commands["duck"] == RabbitMQ.CLI.Ctl.Commands.DuckCommand
+ assert commands["gray_goose"] == RabbitMQ.CLI.Ctl.Commands.GrayGooseCommand
+
+ assert commands["stork"] == nil
+ assert commands["heron"] == nil
+
+ assert commands["crow"] == nil
+ assert commands["raven"] == nil
+
+ set_scope(:plugins)
+ commands = @subject.load(%{})
+ assert commands == @subject.load_commands(:plugins, %{})
+ assert commands["duck"] == nil
+ assert commands["gray_goose"] == nil
+
+ assert commands["stork"] == RabbitMQ.CLI.Plugins.Commands.StorkCommand
+ assert commands["heron"] == RabbitMQ.CLI.Plugins.Commands.HeronCommand
+
+ assert commands["crow"] == nil
+ assert commands["raven"] == nil
+ end
+
+ test "can set scopes inside command" do
+ plugin_commands = @subject.load_commands(:plugins, %{})
+
+ assert plugin_commands["duck"] == nil
+ assert plugin_commands["gray_goose"] == nil
+
+ assert plugin_commands["stork"] == RabbitMQ.CLI.Plugins.Commands.StorkCommand
+ assert plugin_commands["heron"] == RabbitMQ.CLI.Plugins.Commands.HeronCommand
+
+ assert plugin_commands["crow"] == nil
+ assert plugin_commands["raven"] == nil
+
+ # SeagullCommand has scopes() defined as [:plugins, :custom]
+ assert plugin_commands["seagull"] == RabbitMQ.CLI.Seagull.Commands.SeagullCommand
+
+ custom_commands = @subject.load_commands(:custom, %{})
+
+ assert custom_commands["duck"] == nil
+ assert custom_commands["gray_goose"] == nil
+
+ assert custom_commands["stork"] == nil
+ assert custom_commands["heron"] == nil
+
+ assert custom_commands["crow"] == RabbitMQ.CLI.Custom.Commands.CrowCommand
+ assert custom_commands["raven"] == RabbitMQ.CLI.Custom.Commands.RavenCommand
+
+ # SeagullCommand has scopes() defined as [:plugins, :custom]
+ assert custom_commands["seagull"] == RabbitMQ.CLI.Seagull.Commands.SeagullCommand
+
+ end
+
+ ## ------------------- commands/0 tests --------------------
+
+ test "command_modules has existing commands" do
+ set_scope(:ctl)
+ @subject.load(%{})
+ assert @subject.module_map["status"] == RabbitMQ.CLI.Ctl.Commands.StatusCommand
+ assert @subject.module_map["environment"] == RabbitMQ.CLI.Ctl.Commands.EnvironmentCommand
+ end
+
+ test "command_modules does not have non-existent commands" do
+ set_scope(:ctl)
+ @subject.load(%{})
+ assert @subject.module_map[:p_equals_np_proof] == nil
+ end
+end
+
+# Mock command modules for Ctl
+
+defmodule RabbitMQ.CLI.Ctl.Commands.DuckCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["duck"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+defmodule RabbitMQ.CLI.Ctl.Commands.GrayGooseCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["gray_goose"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+defmodule RabbitMQ.CLI.Ctl.Commands.UglyDucklingCommand do
+end
+
+
+# Mock command modules for Plugins
+
+defmodule RabbitMQ.CLI.Plugins.Commands.StorkCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["stork"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+defmodule RabbitMQ.CLI.Plugins.Commands.HeronCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["heron"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+# Mock command modules for Custom
+
+defmodule RabbitMQ.CLI.Custom.Commands.CrowCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["crow"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+ def scopes(), do: [:custom, ]
+end
+
+defmodule RabbitMQ.CLI.Custom.Commands.RavenCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["raven"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+defmodule RabbitMQ.CLI.Seagull.Commands.SeagullCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["seagull"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+ def scopes(), do: [:plugins, :custom]
+end
+
+
diff --git a/deps/rabbitmq_cli/test/core/default_output_test.exs b/deps/rabbitmq_cli/test/core/default_output_test.exs
new file mode 100644
index 0000000000..f567c5cc96
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/default_output_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 DefaultOutputTest do
+ use ExUnit.Case, async: false
+
+ test "ok is passed as is" do
+ assert match?(:ok, ExampleCommand.output(:ok, %{}))
+ end
+
+ test "ok with message is passed as is" do
+ assert match?({:ok, :message}, ExampleCommand.output({:ok, :message}, %{}))
+ assert match?({:ok, {:complex, "message"}}, ExampleCommand.output({:ok, {:complex, "message"}}, %{}))
+ end
+
+ test "enumerable is passed as stream" do
+ assert match?({:stream, 'list'}, ExampleCommand.output({:ok, 'list'}, %{}))
+ assert match?({:stream, 'list'}, ExampleCommand.output('list', %{}))
+
+ assert match?({:stream, [1,2,3]}, ExampleCommand.output({:ok, [1,2,3]}, %{}))
+ assert match?({:stream, [1,2,3]}, ExampleCommand.output([1,2,3], %{}))
+
+ stream = Stream.timer(10000)
+ assert match?({:stream, ^stream}, ExampleCommand.output({:ok, stream}, %{}))
+ assert match?({:stream, ^stream}, ExampleCommand.output(stream, %{}))
+ end
+
+ test "badrpc is an error" do
+ {:error, {:badrpc, :nodedown}} =
+ ExampleCommand.output({:badrpc, :nodedown}, %{})
+
+ {:error, {:badrpc, :timeout}} =
+ ExampleCommand.output({:badrpc, :timeout}, %{})
+ end
+
+ test "unknown atom is error" do
+ {:error, :error_message} = ExampleCommand.output(:error_message, %{})
+ end
+
+ test "unknown tuple is error" do
+ {:error, {:left, :right}} = ExampleCommand.output({:left, :right}, %{})
+ end
+
+ test "error_string is error" do
+ assert {:error, "I am string"} == ExampleCommand.output({:error_string, "I am string"}, %{})
+ end
+
+ test "error_string is converted to string" do
+ assert match?({:error, "I am charlist"},
+ ExampleCommand.output({:error_string, 'I am charlist'}, %{}))
+ end
+
+ test "error is formatted" do
+ {:error, "I am formatted \"string\""} =
+ ExampleCommand.output({:error, 'I am formatted ~p', ['string']}, %{})
+ end
+
+ test "non atom value is ok" do
+ val = "foo"
+ assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
+ val = 125
+ assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
+ val = 100.2
+ assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
+ val = {:one, :two, :three}
+ assert match?({:ok, ^val}, ExampleCommand.output(val, %{}))
+ end
+
+ test "custom output function can be defined" do
+ assert {:error, 125, "Non standard"} == ExampleCommandWithCustomOutput.output(:non_standard_output, %{})
+ end
+
+ test "default output works even if custom output is defined" do
+ assert :ok == ExampleCommandWithCustomOutput.output(:ok, %{})
+ assert {:ok, {:complex, "message"}} == ExampleCommandWithCustomOutput.output({:ok, {:complex, "message"}}, %{})
+
+ assert {:stream, [1,2,3]} == ExampleCommandWithCustomOutput.output({:ok, [1,2,3]}, %{})
+ assert {:stream, [1,2,3]} == ExampleCommandWithCustomOutput.output([1,2,3], %{})
+
+ assert {:error, {:badrpc, :nodedown}} ==
+ ExampleCommandWithCustomOutput.output({:badrpc, :nodedown}, %{})
+ assert {:error, {:badrpc, :timeout}} ==
+ ExampleCommandWithCustomOutput.output({:badrpc, :timeout}, %{})
+
+ error = %{i: [am: "arbitrary", error: 1]}
+ {:error, ^error} = ExampleCommandWithCustomOutput.output({:error, error}, %{})
+
+ assert {:error, "I am string"} == ExampleCommandWithCustomOutput.output({:error_string, "I am string"}, %{})
+
+ val = "foo"
+ assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
+ val = 125
+ assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
+ val = 100.2
+ assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
+ val = {:one, :two, :three}
+ assert match?({:ok, ^val}, ExampleCommandWithCustomOutput.output(val, %{}))
+ end
+end
+
+defmodule ExampleCommand do
+ use RabbitMQ.CLI.DefaultOutput
+end
+
+defmodule ExampleCommandWithCustomOutput do
+ def output(:non_standard_output, _) do
+ {:error, 125, "Non standard"}
+ end
+ use RabbitMQ.CLI.DefaultOutput
+end
diff --git a/deps/rabbitmq_cli/test/core/distribution_test.exs b/deps/rabbitmq_cli/test/core/distribution_test.exs
new file mode 100644
index 0000000000..00dd872ab4
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/distribution_test.exs
@@ -0,0 +1,48 @@
+## 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.
+
+alias RabbitMQ.CLI.Core.Distribution
+
+defmodule DistributionTest do
+ use ExUnit.Case, async: false
+
+ setup_all do
+ :net_kernel.stop()
+ :ok
+ end
+
+ test "set cookie via environment variable" do
+ on_exit(fn ->
+ :net_kernel.stop()
+ System.delete_env("RABBITMQ_ERLANG_COOKIE")
+ end)
+ try do
+ :nocookie = Node.get_cookie()
+ catch
+ # one of net_kernel processes is not running ¯\_(ツ)_/¯
+ :exit, _ -> :ok
+ end
+ System.put_env("RABBITMQ_ERLANG_COOKIE", "mycookie")
+ opts = %{}
+ Distribution.start(opts)
+ :mycookie = Node.get_cookie()
+ end
+
+ test "set cookie via argument" do
+ on_exit(fn ->
+ :net_kernel.stop()
+ end)
+ try do
+ :nocookie = Node.get_cookie()
+ catch
+ # one of net_kernel processes is not running ¯\_(ツ)_/¯
+ :exit, _ -> :ok
+ end
+ opts = %{erlang_cookie: :mycookie}
+ Distribution.start(opts)
+ :mycookie = Node.get_cookie()
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/helpers_test.exs b/deps/rabbitmq_cli/test/core/helpers_test.exs
new file mode 100644
index 0000000000..71d107bef8
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/helpers_test.exs
@@ -0,0 +1,140 @@
+## 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 HelpersTest do
+ alias RabbitMQ.CLI.Core.{Config, Helpers}
+ import RabbitMQ.CLI.Core.{CodePath, Memory}
+
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ ## --------------------- get_rabbit_hostname()/0 tests -------------------------
+
+ test "RabbitMQ hostname is properly formed" do
+ assert Helpers.get_rabbit_hostname() |> Atom.to_string =~ ~r/rabbit@\w+/
+ end
+
+ ## ------------------- memory_unit* tests --------------------
+
+ test "an invalid memory unit fails " do
+ assert memory_unit_absolute(10, "gigantibytes") == {:bad_argument, ["gigantibytes"]}
+ end
+
+ test "an invalid number fails " do
+ assert memory_unit_absolute("lots", "gigantibytes") == {:bad_argument, ["lots", "gigantibytes"]}
+ assert memory_unit_absolute(-1, "gigantibytes") == {:bad_argument, [-1, "gigantibytes"]}
+ end
+
+ test "valid number and unit returns a valid result " do
+ assert memory_unit_absolute(10, "k") == 10240
+ assert memory_unit_absolute(10, "kiB") == 10240
+ assert memory_unit_absolute(10, "M") == 10485760
+ assert memory_unit_absolute(10, "MiB") == 10485760
+ assert memory_unit_absolute(10, "G") == 10737418240
+ assert memory_unit_absolute(10, "GiB")== 10737418240
+ assert memory_unit_absolute(10, "kB")== 10000
+ assert memory_unit_absolute(10, "MB")== 10000000
+ assert memory_unit_absolute(10, "GB")== 10000000000
+ assert memory_unit_absolute(10, "") == 10
+ end
+
+ ## ------------------- Helpers.normalise_node_option tests --------------------
+
+ test "longnames: 'rabbit' as node name, correct domain is used" do
+ default_name = Config.default(:node)
+ options = %{node: default_name, longnames: true}
+ {:ok, options} = Helpers.normalise_node_option(options)
+ assert options[:node] == :"rabbit@#{hostname()}.#{domain()}"
+ end
+
+ test "shortnames: 'rabbit' as node name, no domain is used" do
+ options = %{node: :rabbit, longnames: false}
+ {:ok, options} = Helpers.normalise_node_option(options)
+ assert options[:node] == :"rabbit@#{hostname()}"
+ end
+
+ ## ------------------- normalise_node tests (:shortnames) --------------------
+
+ test "shortnames: if nil input, retrieve standard rabbit hostname" do
+ assert Helpers.normalise_node(nil, :shortnames) == get_rabbit_hostname()
+ end
+
+ test "shortnames: if input is an atom short name, return the atom with hostname" do
+ want = String.to_atom("rabbit_test@#{hostname()}")
+ got = Helpers.normalise_node(:rabbit_test, :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: if input is a string fully qualified node name, return an atom" do
+ want = String.to_atom("rabbit_test@#{hostname()}")
+ got = Helpers.normalise_node("rabbit_test@#{hostname()}", :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: if input is a short node name, host name is added" do
+ want = String.to_atom("rabbit_test@#{hostname()}")
+ got = Helpers.normalise_node("rabbit_test", :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: if input is a hostname without a node name, default node name is added" do
+ default_name = Config.default(:node)
+ want = String.to_atom("#{default_name}@#{hostname()}")
+ got = Helpers.normalise_node("@#{hostname()}", :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: if input is a short node name with an @ and no hostname, local host name is added" do
+ want = String.to_atom("rabbit_test@#{hostname()}")
+ got = Helpers.normalise_node("rabbit_test@", :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: if input contains more than one @, return an atom" do
+ want = String.to_atom("rabbit@rabbit_test@#{hostname()}")
+ got = Helpers.normalise_node("rabbit@rabbit_test@#{hostname()}", :shortnames)
+ assert want == got
+ end
+
+ ## ------------------- normalise_node tests (:longnames) --------------------
+
+ test "longnames: if nil input, retrieve standard rabbit hostname" do
+ want = get_rabbit_hostname(:longnames)
+ got = Helpers.normalise_node(nil, :longnames)
+ assert want == got
+ end
+
+ test "longnames: if input is an atom short name, return the atom with full hostname" do
+ want = String.to_atom("rabbit_test@#{hostname()}.#{domain()}")
+ got = Helpers.normalise_node(:rabbit_test, :longnames)
+ assert want == got
+ end
+
+ ## ------------------- require_rabbit/1 tests --------------------
+
+ test "locate plugin with version number in filename" do
+ plugins_directory_03 = fixture_plugins_path("plugins-subdirectory-03")
+ rabbitmq_home = :rabbit_misc.rpc_call(node(), :code, :lib_dir, [:rabbit])
+ opts = %{plugins_dir: to_string(plugins_directory_03),
+ rabbitmq_home: rabbitmq_home}
+ assert Enum.member?(Application.loaded_applications(), {:mock_rabbitmq_plugins_03, 'New project', '0.1.0'}) == false
+ require_rabbit_and_plugins(opts)
+ Application.load(:mock_rabbitmq_plugins_03)
+ assert Enum.member?(Application.loaded_applications(), {:mock_rabbitmq_plugins_03, 'New project', '0.1.0'})
+ end
+
+ test "locate plugin without version number in filename" do
+ plugins_directory_04 = fixture_plugins_path("plugins-subdirectory-04")
+ rabbitmq_home = :rabbit_misc.rpc_call(node(), :code, :lib_dir, [:rabbit])
+ opts = %{plugins_dir: to_string(plugins_directory_04),
+ rabbitmq_home: rabbitmq_home}
+ assert Enum.member?(Application.loaded_applications(), {:mock_rabbitmq_plugins_04, 'New project', 'rolling'}) == false
+ require_rabbit_and_plugins(opts)
+ Application.load(:mock_rabbitmq_plugins_04)
+ assert Enum.member?(Application.loaded_applications(), {:mock_rabbitmq_plugins_04, 'New project', 'rolling'})
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/core/information_unit_test.exs b/deps/rabbitmq_cli/test/core/information_unit_test.exs
new file mode 100644
index 0000000000..568b687b2d
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/information_unit_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 InformationUnitTest do
+ use ExUnit.Case, async: true
+
+ alias RabbitMQ.CLI.InformationUnit, as: IU
+
+ test "bytes, MB, GB, TB are known units" do
+ Enum.each(["bytes", "mb", "MB", "gb", "GB", "tb", "TB"],
+ fn x -> assert IU.known_unit?(x) end)
+ end
+
+ test "glip-glops, millibars, gold pressed latinum bars and looney and are not known units" do
+ Enum.each(["glip-glops", "millibars", "gold pressed latinum bars", "looney"],
+ fn x -> assert not IU.known_unit?(x) end)
+ end
+
+ test "conversion to bytes" do
+ assert IU.convert(0, "bytes") == 0
+ assert IU.convert(100, "bytes") == 100
+ assert IU.convert(9988, "bytes") == 9988
+ end
+
+ test "conversion to MB" do
+ assert IU.convert(1000000, "mb") == 1.0
+ assert IU.convert(9500000, "mb") == 9.5
+ assert IU.convert(97893000, "mb") == 97.893
+ assert IU.convert(978930000, "mb") == 978.93
+ end
+
+ test "conversion to GB" do
+ assert IU.convert(978930000, "gb") == 0.9789
+
+ assert IU.convert(1000000000, "gb") == 1.0
+ assert IU.convert(9500000000, "gb") == 9.5
+ assert IU.convert(97893000000, "gb") == 97.893
+ assert IU.convert(978930000000, "gb") == 978.93
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/json_stream_test.exs b/deps/rabbitmq_cli/test/core/json_stream_test.exs
new file mode 100644
index 0000000000..ab3bebd62c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/json_stream_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) 2019-2020 VMware, Inc. or its affiliates. All rights reserved.
+
+defmodule JsonStreamTest do
+ use ExUnit.Case, async: false
+
+ @formatter RabbitMQ.CLI.Formatters.JsonStream
+
+ test "format_output map with atom keys is converted to JSON object" do
+ assert @formatter.format_output(%{a: :apple, b: :beer}, %{}) == "{\"a\":\"apple\",\"b\":\"beer\"}"
+ end
+
+ test "format_output map with binary keys is converted to JSON object" do
+ assert @formatter.format_output(%{"a" => :apple, "b" => :beer}, %{}) == "{\"a\":\"apple\",\"b\":\"beer\"}"
+ end
+
+ test "format_output empty binary is converted to empty JSON array" do
+ assert @formatter.format_output("", %{}) == ""
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/core/listeners_test.exs b/deps/rabbitmq_cli/test/core/listeners_test.exs
new file mode 100644
index 0000000000..266413c6fa
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/listeners_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) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
+
+defmodule CoreListenersTest do
+ use ExUnit.Case, async: true
+
+ import RabbitMQ.CLI.Core.Listeners
+ import RabbitCommon.Records
+
+ test "listener record translation to a map" do
+ assert listener_map(listener(node: :rabbit@mercurio,
+ protocol: :stomp,
+ ip_address: {0,0,0,0,0,0,0,0},
+ port: 61613)) ==
+ %{
+ interface: "[::]",
+ node: :rabbit@mercurio,
+ port: 61613,
+ protocol: :stomp,
+ purpose: "STOMP"
+ }
+ end
+
+ test "[human-readable] protocol labels" do
+ assert protocol_label(:amqp) == "AMQP 0-9-1 and AMQP 1.0"
+ assert protocol_label(:'amqp/ssl') == "AMQP 0-9-1 and AMQP 1.0 over TLS"
+ assert protocol_label(:mqtt) == "MQTT"
+ assert protocol_label(:'mqtt/ssl') == "MQTT over TLS"
+ assert protocol_label(:stomp) == "STOMP"
+ assert protocol_label(:'stomp/ssl') == "STOMP over TLS"
+ assert protocol_label(:http) == "HTTP API"
+ assert protocol_label(:https) == "HTTP API over TLS (HTTPS)"
+ assert protocol_label(:'https/web-stomp') == "STOMP over WebSockets and TLS (HTTPS)"
+ assert protocol_label(:'https/web-mqtt') == "MQTT over WebSockets and TLS (HTTPS)"
+
+ assert protocol_label(:'http/prometheus') == "Prometheus exporter API over HTTP"
+ assert protocol_label(:'https/prometheus') == "Prometheus exporter API over TLS (HTTPS)"
+ end
+
+ test "listener expiring within" do
+ validityInDays = 10
+ validity = X509.Certificate.Validity.days_from_now(validityInDays)
+ ca_key = X509.PrivateKey.new_ec(:secp256r1)
+ ca = X509.Certificate.self_signed(ca_key,
+ "/C=US/ST=CA/L=San Francisco/O=Megacorp/CN=Megacorp Intermediate CA",
+ template: :root_ca,
+ validity: validity
+ )
+ pem = X509.Certificate.to_pem(ca)
+
+ opts = [{:certfile, {:pem, pem}}, {:cacertfile, {:pem, pem}}]
+ listener = listener(node: :rabbit@mercurio,
+ protocol: :stomp,
+ ip_address: {0,0,0,0,0,0,0,0},
+ port: 61613,
+ opts: opts)
+
+ assert not listener_expiring_within(listener, 86400 * (validityInDays - 5))
+ assert listener_expiring_within(listener, 86400 * (validityInDays + 5))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/node_name_test.exs b/deps/rabbitmq_cli/test/core/node_name_test.exs
new file mode 100644
index 0000000000..89bc1484bc
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/node_name_test.exs
@@ -0,0 +1,73 @@
+## 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 NodeNameTest do
+ use ExUnit.Case, async: true
+
+ @subject RabbitMQ.CLI.Core.NodeName
+
+ test "shortnames: RabbitMQ nodename is properly formed from atom" do
+ want = String.to_atom("rabbit@#{:inet_db.gethostname()}")
+ {:ok, got} = @subject.create(:rabbit, :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: RabbitMQ nodename is properly formed from string" do
+ want = String.to_atom("rabbit@#{:inet_db.gethostname()}")
+ {:ok, got} = @subject.create("rabbit", :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: RabbitMQ nodename is properly formed with trailing @" do
+ want = String.to_atom("rabbit@#{:inet_db.gethostname()}")
+ {:ok, got} = @subject.create(:rabbit@, :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: RabbitMQ nodename is properly formed with host part" do
+ want = :rabbit@foofoo
+ {:ok, got} = @subject.create(want, :shortnames)
+ assert want == got
+ end
+
+ test "shortnames: nodename head only supports alphanumerics, underscores and hyphens in name head" do
+ {:error, {:node_name, :invalid_node_name_head}} = @subject.create("кириллица", :shortnames)
+ end
+
+ test "longnames: RabbitMQ nodename is properly formed from atom" do
+ {:ok, got} = @subject.create(:rabbit, :longnames)
+ assert Atom.to_string(got) =~ ~r/rabbit@[\w\-]+\.\w+/
+ end
+
+ test "longnames: RabbitMQ nodename is properly formed from string" do
+ {:ok, got} = @subject.create("rabbit", :longnames)
+ assert Atom.to_string(got) =~ ~r/rabbit@[\w\-]+\.\w+/
+ end
+
+ test "longnames: RabbitMQ nodename is properly formed from atom with domain" do
+ want = :"rabbit@localhost.localdomain"
+ {:ok, got} = @subject.create(want, :longnames)
+ assert want == got
+ end
+
+ test "longnames: RabbitMQ nodename is properly formed from string with domain" do
+ name_str = "rabbit@localhost.localdomain"
+ want = String.to_atom(name_str)
+ {:ok, got} = @subject.create(name_str, :longnames)
+ assert want == got
+ end
+
+ test "longnames: RabbitMQ nodename is properly formed from string with partial domain" do
+ name_str = "rabbit@localhost"
+ want = String.to_atom(name_str <> "." <> @subject.domain())
+ {:ok, got} = @subject.create(name_str, :longnames)
+ assert want == got
+ end
+
+ test "longnames: nodename head only supports alphanumerics, underscores and hyphens in name head" do
+ {:error, {:node_name, :invalid_node_name_head}} = @subject.create("кириллица", :longnames)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/os_pid_test.exs b/deps/rabbitmq_cli/test/core/os_pid_test.exs
new file mode 100644
index 0000000000..2d110f591f
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/os_pid_test.exs
@@ -0,0 +1,54 @@
+## 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 OsPidTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @subject RabbitMQ.CLI.Core.OsPid
+
+ #
+ # Tests
+ #
+
+ describe "#read_pid_from_file with should_wait = false" do
+ test "with a valid pid file returns an integer value" do
+ path = fixture_file_path("valid_pidfile.pid")
+
+ assert (File.exists?(path) and File.regular?(path))
+ assert @subject.read_pid_from_file(path, false) == 13566
+ end
+
+ test "with a valid pid file that includes spaces returns an integer value" do
+ path = fixture_file_path("valid_pidfile_with_spaces.pid")
+
+ assert (File.exists?(path) and File.regular?(path))
+ assert @subject.read_pid_from_file(path, false) == 83777
+ end
+
+ test "with an empty file" do
+ path = fixture_file_path("empty_pidfile.pid")
+
+ assert (File.exists?(path) and File.regular?(path))
+ assert match?({:error, :could_not_read_pid_from_file, _}, @subject.read_pid_from_file(path, false))
+ end
+
+ test "with a non-empty file full of garbage (that doesn't parse)" do
+ path = fixture_file_path("invalid_pidfile.pid")
+
+ assert (File.exists?(path) and File.regular?(path))
+ assert match?({:error, :could_not_read_pid_from_file, _}, @subject.read_pid_from_file(path, false))
+ end
+
+ test "with a file that does not exist" do
+ path = fixture_file_path("pidfile_that_does_not_exist_128787df8s7f8%4&^.pid")
+
+ assert !File.exists?(path)
+ assert match?({:error, :could_not_read_pid_from_file, _}, @subject.read_pid_from_file(path, false))
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/parser_test.exs b/deps/rabbitmq_cli/test/core/parser_test.exs
new file mode 100644
index 0000000000..b483db1fdd
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/parser_test.exs
@@ -0,0 +1,369 @@
+## 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.
+
+## Mock command for command specific parser
+defmodule RabbitMQ.CLI.Seagull.Commands.HerringGullCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["herring_gull"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+ def switches(), do: [herring: :string, garbage: :boolean]
+ def aliases(), do: [h: :herring, g: :garbage]
+end
+
+defmodule RabbitMQ.CLI.Seagull.Commands.PacificGullCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["pacific_gull"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+defmodule RabbitMQ.CLI.Seagull.Commands.HermannGullCommand do
+ @behaviour RabbitMQ.CLI.CommandBehaviour
+ use RabbitMQ.CLI.DefaultOutput
+ def usage(), do: ["hermann_gull"]
+ def validate(_,_), do: :ok
+ def merge_defaults(_,_), do: {[], %{}}
+ def banner(_,_), do: ""
+ def run(_,_), do: :ok
+end
+
+defmodule ParserTest do
+ use ExUnit.Case, async: true
+ import ExUnit.CaptureIO
+ import TestHelper
+
+ @subject RabbitMQ.CLI.Core.Parser
+
+ setup_all do
+ Code.ensure_loaded(RabbitMQ.CLI.Seagull.Commands.HerringGullCommand)
+ Code.ensure_loaded(RabbitMQ.CLI.Seagull.Commands.PacificGullCommand)
+ set_scope(:seagull)
+ on_exit(fn ->
+ set_scope(:none)
+ end)
+ :ok
+ end
+
+ test "one arity 0 command, no options" do
+ assert @subject.parse_global(["sandwich"]) == {["sandwich"], %{}, []}
+ end
+
+ test "one arity 1 command, no options" do
+ assert @subject.parse_global(["sandwich", "pastrami"]) == {["sandwich", "pastrami"], %{}, []}
+ end
+
+ test "no commands, no options (empty string)" do
+ assert @subject.parse_global([""]) == {[""], %{}, []}
+ end
+
+ test "no commands, no options (empty array)" do
+ assert @subject.parse_global([]) == {[],%{}, []}
+ end
+
+ test "one arity 1 command, one double-dash quiet flag" do
+ assert @subject.parse_global(["sandwich", "pastrami", "--quiet"]) ==
+ {["sandwich", "pastrami"], %{quiet: true}, []}
+ end
+
+ test "one arity 1 command, one single-dash quiet flag" do
+ assert @subject.parse_global(["sandwich", "pastrami", "-q"]) ==
+ {["sandwich", "pastrami"], %{quiet: true}, []}
+ end
+
+ test "one arity 1 command, one double-dash silent flag" do
+ assert @subject.parse_global(["sandwich", "pastrami", "--silent"]) ==
+ {["sandwich", "pastrami"], %{silent: true}, []}
+ end
+
+ test "one arity 1 command, one single-dash silent flag" do
+ assert @subject.parse_global(["sandwich", "pastrami", "-s"]) ==
+ {["sandwich", "pastrami"], %{silent: true}, []}
+ end
+
+ test "one arity 0 command, one single-dash node option" do
+ assert @subject.parse_global(["sandwich", "-n", "rabbitmq@localhost"]) ==
+ {["sandwich"], %{node: :rabbitmq@localhost}, []}
+ end
+
+ test "one arity 1 command, one single-dash node option" do
+ assert @subject.parse_global(["sandwich", "pastrami", "-n", "rabbitmq@localhost"]) ==
+ {["sandwich", "pastrami"], %{node: :rabbitmq@localhost}, []}
+ end
+
+ test "one arity 1 command, one single-dash node option and one quiet flag" do
+ assert @subject.parse_global(["sandwich", "pastrami", "-n", "rabbitmq@localhost", "--quiet"]) ==
+ {["sandwich", "pastrami"], %{node: :rabbitmq@localhost, quiet: true}, []}
+ end
+
+ test "single-dash node option before command" do
+ assert @subject.parse_global(["-n", "rabbitmq@localhost", "sandwich", "pastrami"]) ==
+ {["sandwich", "pastrami"], %{node: :rabbitmq@localhost}, []}
+ end
+
+ test "no commands, one double-dash node option" do
+ assert @subject.parse_global(["--node=rabbitmq@localhost"]) == {[], %{node: :rabbitmq@localhost}, []}
+ end
+
+ test "no commands, one single-dash -p option" do
+ assert @subject.parse_global(["-p", "sandwich"]) == {[], %{vhost: "sandwich"}, []}
+ end
+
+ test "global parse treats command-specific arguments as invalid (ignores them)" do
+ command_line = ["seagull", "--herring", "atlantic", "-g", "-p", "my_vhost"]
+ {args, options, invalid} = @subject.parse_global(command_line)
+ assert {args, options, invalid} ==
+ {["seagull", "atlantic"], %{vhost: "my_vhost"}, [{"--herring", nil}, {"-g", nil}]}
+ end
+
+ test "global parse treats command-specific arguments that are separated by an equals sign as invalid (ignores them)" do
+ command_line = ["seagull", "--herring=atlantic", "-g", "-p", "my_vhost"]
+ {args, options, invalid} = @subject.parse_global(command_line)
+ assert {args, options, invalid} ==
+ {["seagull"], %{vhost: "my_vhost"}, [{"--herring", nil}, {"-g", nil}]}
+ end
+
+ test "command-specific parse recognizes command switches" do
+ command_line = ["seagull", "--herring", "atlantic", "-g", "-p", "my_vhost"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse_command_specific(command_line, command) ==
+ {["seagull"], %{vhost: "my_vhost", herring: "atlantic", garbage: true}, []}
+ end
+
+ test "command-specific parse recognizes command switches that are separated by an equals sign" do
+ command_line = ["seagull", "--herring=atlantic", "-g", "-p", "my_vhost"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse_command_specific(command_line, command) ==
+ {["seagull"], %{vhost: "my_vhost", herring: "atlantic", garbage: true}, []}
+ end
+
+ test "command-specific switches and aliases are optional" do
+ command_line = ["seagull", "-p", "my_vhost"]
+ command = RabbitMQ.CLI.Seagull.Commands.PacificGullCommand
+ assert @subject.parse_command_specific(command_line, command) ==
+ {["seagull"], %{vhost: "my_vhost"}, []}
+ end
+
+ test "--timeout can be specified before command" do
+ # for backwards compatibility
+ assert @subject.parse_global(["-n", "rabbitmq@localhost", "--timeout", "5", "sandwich", "pastrami"]) ==
+ {["sandwich", "pastrami"], %{node: :rabbitmq@localhost, timeout: 5}, []}
+ end
+
+ test "-t can be specified before command" do
+ # for backwards compatibility
+ assert @subject.parse_global(["-n", "rabbitmq@localhost", "-t", "5", "sandwich", "pastrami"]) ==
+ {["sandwich", "pastrami"], %{node: :rabbitmq@localhost, timeout: 5}, []}
+ end
+
+ test "parse/1 returns command name" do
+ command_line = ["pacific_gull", "fly", "-p", "my_vhost"]
+ command = RabbitMQ.CLI.Seagull.Commands.PacificGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "pacific_gull", ["fly"], %{vhost: "my_vhost"}, []}
+ end
+
+ test "parse/1 returns command name when a global flag comes before the command" do
+ command_line = ["-p", "my_vhost", "pacific_gull", "fly"]
+ command = RabbitMQ.CLI.Seagull.Commands.PacificGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "pacific_gull", ["fly"], %{vhost: "my_vhost"}, []}
+ end
+
+ test "parse/1 returns command name when a global flag separated by an equals sign comes before the command" do
+ command_line = ["-p=my_vhost", "pacific_gull", "fly"]
+ command = RabbitMQ.CLI.Seagull.Commands.PacificGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "pacific_gull", ["fly"], %{vhost: "my_vhost"}, []}
+ end
+
+ test "parse/1 returns :no_command when given an empty argument list" do
+ command_line = ["-p", "my_vhost"]
+ assert @subject.parse(command_line) ==
+ {:no_command, "", [], %{vhost: "my_vhost"}, []}
+ end
+
+ test "parse/1 returns :no_command and command name when command isn't known" do
+ command_line = ["atlantic_gull", "-p", "my_vhost"]
+ assert @subject.parse(command_line) ==
+ {:no_command, "atlantic_gull", [], %{vhost: "my_vhost"}, []}
+ end
+
+ test "parse/1 returns :no command if command-specific options come before the command" do
+ command_line = ["--herring", "atlantic", "herring_gull", "-p", "my_vhost"]
+ assert @subject.parse(command_line) ==
+ {:no_command, "atlantic", ["herring_gull"],
+ %{vhost: "my_vhost"}, [{"--herring", nil}]}
+ end
+
+ test "parse/1 returns command name if a global option comes before the command" do
+ command_line = ["-p", "my_vhost", "herring_gull"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "herring_gull", [], %{vhost: "my_vhost"}, []}
+ end
+
+ test "parse/1 returns command name if multiple global options come before the command" do
+ command_line = ["-p", "my_vhost", "-q", "-n", "rabbit@test", "herring_gull"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "herring_gull", [], %{vhost: "my_vhost", node: :rabbit@test, quiet: true}, []}
+ end
+
+ test "parse/1 returns command name if multiple global options separated by an equals sign come before the command" do
+ command_line = ["-p=my_vhost", "-q", "--node=rabbit@test", "herring_gull"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "herring_gull", [], %{vhost: "my_vhost", node: :rabbit@test, quiet: true}, []}
+ end
+
+ test "parse/1 returns command with command specific options" do
+ command_line = ["herring_gull", "--herring", "atlantic",
+ "-g", "fly", "-p", "my_vhost"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "herring_gull", ["fly"],
+ %{vhost: "my_vhost", herring: "atlantic", garbage: true}, []}
+ end
+
+ test "parse/1 returns command with command specific options that are separated by an equals sign" do
+ command_line = ["herring_gull", "--herring=atlantic",
+ "-g", "fly", "-p=my_vhost"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "herring_gull", ["fly"],
+ %{vhost: "my_vhost", herring: "atlantic", garbage: true}, []}
+ end
+
+ test "parse/1 expands command-defined aliases" do
+ command_line = ["herring_gull", "fly", "-g"]
+ command = RabbitMQ.CLI.Seagull.Commands.HerringGullCommand
+ assert @subject.parse(command_line) ==
+ {command, "herring_gull", ["fly"], %{garbage: true}, []}
+ end
+
+ test "parse/1 returns invalid/extra options for command" do
+ command_line = ["pacific_gull", "fly",
+ "--herring", "atlantic",
+ "-p", "my_vhost"]
+ pacific_gull = RabbitMQ.CLI.Seagull.Commands.PacificGullCommand
+ assert @subject.parse(command_line) ==
+ {pacific_gull, "pacific_gull", ["fly", "atlantic"],
+ %{vhost: "my_vhost"},
+ [{"--herring", nil}]}
+ end
+
+ test "parse/1 suggests similar command" do
+ # One letter difference
+ assert @subject.parse(["pacific_gulf"]) ==
+ {{:suggest, "pacific_gull"}, "pacific_gulf", [], %{}, []}
+
+ # One letter missing
+ assert @subject.parse(["pacific_gul"]) ==
+ {{:suggest, "pacific_gull"}, "pacific_gul", [], %{}, []}
+
+ # One letter extra
+ assert @subject.parse(["pacific_gulll"]) ==
+ {{:suggest, "pacific_gull"}, "pacific_gulll", [], %{}, []}
+
+ # Five letter difference
+ assert @subject.parse(["pacifistcatl"]) ==
+ {{:suggest, "pacific_gull"}, "pacifistcatl", [], %{}, []}
+
+ # Five letters missing
+ assert @subject.parse(["pacific"]) ==
+ {{:suggest, "pacific_gull"}, "pacific", [], %{}, []}
+
+ # Closest to similar
+ assert @subject.parse(["herrdog_gull"]) ==
+ {{:suggest, "herring_gull"}, "herrdog_gull", [], %{}, []}
+
+ # Closest to similar
+ assert @subject.parse(["hermaug_gull"]) ==
+ {{:suggest, "hermann_gull"}, "hermaug_gull", [], %{}, []}
+ end
+
+ @tag cd: "fixtures"
+ test "parse/1 supports aliases" do
+ aliases = """
+ larus_pacificus = pacific_gull
+ gull_with_herring = herring_gull --herring atlantic
+ flying_gull = herring_gull fly
+ garbage_gull = herring_gull -g
+ complex_gull = herring_gull --herring pacific -g --formatter=erlang eat
+ invalid_gull = herring_gull --invalid
+ unknown_gull = mysterious_gull
+ """
+
+ aliases_file_name = "aliases.ini"
+ File.write(aliases_file_name, aliases)
+ on_exit(fn() ->
+ File.rm(aliases_file_name)
+ end)
+
+ assert @subject.parse(["larus_pacificus", "--aliases-file", aliases_file_name]) ==
+ {RabbitMQ.CLI.Seagull.Commands.PacificGullCommand,
+ "larus_pacificus",
+ [],
+ %{aliases_file: aliases_file_name},
+ []}
+
+ assert @subject.parse(["gull_with_herring", "--aliases-file", aliases_file_name]) ==
+ {RabbitMQ.CLI.Seagull.Commands.HerringGullCommand,
+ "gull_with_herring",
+ [],
+ %{aliases_file: aliases_file_name, herring: "atlantic"},
+ []}
+
+ assert @subject.parse(["flying_gull", "--aliases-file", aliases_file_name]) ==
+ {RabbitMQ.CLI.Seagull.Commands.HerringGullCommand,
+ "flying_gull",
+ ["fly"],
+ %{aliases_file: aliases_file_name},
+ []}
+
+ assert @subject.parse(["garbage_gull", "--aliases-file", aliases_file_name]) ==
+ {RabbitMQ.CLI.Seagull.Commands.HerringGullCommand,
+ "garbage_gull",
+ [],
+ %{aliases_file: aliases_file_name, garbage: true},
+ []}
+
+ assert @subject.parse(["complex_gull", "--aliases-file", aliases_file_name]) ==
+ {RabbitMQ.CLI.Seagull.Commands.HerringGullCommand,
+ "complex_gull",
+ ["eat"],
+ %{aliases_file: aliases_file_name, garbage: true, herring: "pacific", formatter: "erlang"},
+ []}
+
+ assert @subject.parse(["invalid_gull", "--aliases-file", aliases_file_name]) ==
+ {RabbitMQ.CLI.Seagull.Commands.HerringGullCommand,
+ "invalid_gull",
+ [],
+ %{aliases_file: aliases_file_name},
+ [{"--invalid", nil}]}
+
+ assert @subject.parse(["unknown_gull", "--aliases-file", aliases_file_name]) ==
+ {:no_command, "unknown_gull", [], %{aliases_file: aliases_file_name}, []}
+
+ File.rm(aliases_file_name)
+
+
+ assert capture_io(:stderr,
+ fn ->
+ assert @subject.parse(["larus_pacificus", "--aliases-file", aliases_file_name]) ==
+ {:no_command, "larus_pacificus", [], %{aliases_file: aliases_file_name}, []}
+ end) =~ "Error reading aliases file"
+
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/core/rpc_stream_test.exs b/deps/rabbitmq_cli/test/core/rpc_stream_test.exs
new file mode 100644
index 0000000000..cadd303f23
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/rpc_stream_test.exs
@@ -0,0 +1,94 @@
+defmodule RpcStreamTest do
+ use ExUnit.Case, async: false
+
+ require RabbitMQ.CLI.Ctl.RpcStream
+ alias RabbitMQ.CLI.Ctl.RpcStream
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+
+ :ok
+
+ end
+
+ test "emit empty list" do
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list, [[]], :infinity, []])
+
+ assert [] == items
+ end
+
+ test "emit list without filters" do
+ list = [:one, :two, :three]
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list, [list], :infinity, []])
+
+ assert list == items
+ end
+
+
+ test "emit list with filters" do
+ list = [[one: 1, two: 2, three: 3], [one: 11, two: 12, three: 13]]
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list, [list], :infinity, [:one, :two]])
+
+ assert [[one: 1, two: 2], [one: 11, two: 12]] == items
+ end
+
+ test "emit list of lists with filters" do
+ list = [[[one: 1, two: 2, three: 3], [one: 11, two: 12, three: 13]],
+ [[one: 21, two: 22, three: 23], [one: 31, two: 32, three: 33]]]
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list, [list], :infinity, [:one, :two]])
+
+ assert [[[one: 1, two: 2], [one: 11, two: 12]], [[one: 21, two: 22], [one: 31, two: 32]]] == items
+ end
+
+ test "emission timeout 0 return badrpc" do
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list, [[]], 0, []])
+
+ assert [{:badrpc, {:timeout, 0.0}}] == items
+ end
+
+ test "emission timeout return badrpc with timeout value in seconds" do
+ timeout_fun = fn(x) -> :timer.sleep(1000); x end
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list_map, [[1,2,3], timeout_fun], 100, []])
+ assert [{:badrpc, {:timeout, 0.1}}] == items
+ end
+
+ test "emission timeout in progress return badrpc with timeout value in seconds as last element" do
+ timeout_fun = fn(x) -> :timer.sleep(100); x end
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list_map, [[1,2,3], timeout_fun], 150, []])
+ assert [1, {:badrpc, {:timeout, 0.15}}] == items
+ end
+
+ test "parallel emission do not mix values" do
+ {:ok, agent} = Agent.start_link(fn() -> :init end)
+ list1 = [:one, :two, :three]
+ list2 = [:dog, :cat, :pig]
+ # Adding timeout to make sure emissions are executed in parallel
+ timeout_fun = fn(x) -> :timer.sleep(10); x end
+ Agent.update(agent,
+ fn(:init) ->
+ receive_list_items_to_list([Kernel.node, TestHelper, :emit_list_map, [list2, timeout_fun], :infinity, []])
+ end)
+ items1 = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list_map, [list1, timeout_fun], :infinity, []])
+ items2 = Agent.get(agent, fn(x) -> x end)
+
+ assert items1 == list1
+ assert items2 == list2
+ end
+
+ test "can receive from multiple emission sources in parallel" do
+ list1 = [:one, :two, :three]
+ list2 = [:dog, :cat, :pig]
+ items = receive_list_items_to_list([Kernel.node, TestHelper, :emit_list_multiple_sources, [list1, list2], :infinity, []], 2)
+ assert Kernel.length(list1 ++ list2) == Kernel.length(items)
+ assert MapSet.new(list1 ++ list2) == MapSet.new(items)
+ end
+
+ def receive_list_items_to_list(args, chunks \\ 1) do
+ res = Kernel.apply(RpcStream, :receive_list_items, args ++ [chunks])
+ case Enumerable.impl_for(res) do
+ nil -> res;
+ _ -> Enum.to_list(res)
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/core/table_formatter_test.exs b/deps/rabbitmq_cli/test/core/table_formatter_test.exs
new file mode 100644
index 0000000000..60bf2060f1
--- /dev/null
+++ b/deps/rabbitmq_cli/test/core/table_formatter_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 TableFormatterTest do
+ use ExUnit.Case, async: false
+
+ @formatter RabbitMQ.CLI.Formatters.Table
+
+ test "format_output tab-separates map values" do
+ assert @formatter.format_output(%{a: :apple, b: :beer}, %{}) == ["a\tb", "apple\tbeer"]
+ assert @formatter.format_output(%{a: :apple, b: :beer, c: 1}, %{}) == ["a\tb\tc", "apple\tbeer\t1"]
+ assert @formatter.format_output(%{a: "apple", b: 'beer', c: 1}, %{}) == ["a\tb\tc", "apple\t\"beer\"\t1"]
+ end
+
+ test "format_output tab-separates keyword values" do
+ assert @formatter.format_output([a: :apple, b: :beer], %{}) == ["a\tb", "apple\tbeer"]
+ assert @formatter.format_output([a: :apple, b: :beer, c: 1], %{}) == ["a\tb\tc", "apple\tbeer\t1"]
+ assert @formatter.format_output([a: "apple", b: 'beer', c: 1], %{}) == ["a\tb\tc", "apple\t\"beer\"\t1"]
+ end
+
+ test "format_stream tab-separates map values" do
+ assert @formatter.format_stream([%{a: :apple, b: :beer, c: 1},
+ %{a: "aadvark", b: 'bee', c: 2}], %{})
+ |> Enum.to_list ==
+ ["a\tb\tc", "apple\tbeer\t1", "aadvark\t\"bee\"\t2"]
+ end
+
+ test "format_stream tab-separates keyword values" do
+ assert @formatter.format_stream([[a: :apple, b: :beer, c: 1],
+ [a: "aadvark", b: 'bee', c: 2]], %{})
+ |> Enum.to_list ==
+ ["a\tb\tc", "apple\tbeer\t1", "aadvark\t\"bee\"\t2"]
+ end
+
+ test "format_output formats non-string values with inspect recursively" do
+ assert @formatter.format_output(%{a: :apple, b: "beer", c: {:carp, "fish"}, d: [door: :way], e: %{elk: "horn", for: :you}}, %{}) ==
+ ["a\tb\tc\td\te", "apple\tbeer\t{carp, fish}\t[{door, way}]\t\#{elk => horn, for => you}"]
+
+ assert @formatter.format_output(%{a: :apple, b: "beer", c: {:carp, {:small, :fish}}, d: [door: {:way, "big"}], e: %{elk: [horn: :big]}}, %{}) ==
+ ["a\tb\tc\td\te", "apple\tbeer\t{carp, {small, fish}}\t[{door, {way, big}}]\t\#{elk => [{horn, big}]}"]
+ end
+end
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
diff --git a/deps/rabbitmq_cli/test/diagnostics/alarms_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/alarms_command_test.exs
new file mode 100644
index 0000000000..70a2bfda64
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/alarms_command_test.exs
@@ -0,0 +1,69 @@
+## 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 AlarmsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+ import RabbitMQ.CLI.Core.Alarms, only: [alarm_types: 1]
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.AlarmsCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: when target node has no alarms in effect, returns an empty list", context do
+ assert [] == status()[:alarms]
+
+ assert @command.run([], context[:opts]) == []
+ end
+
+ test "run: when target node has an alarm in effect, returns it", context do
+ old_watermark = status()[:vm_memory_high_watermark]
+ on_exit(fn() ->
+ set_vm_memory_high_watermark(old_watermark)
+ end)
+ # 2000 bytes will trigger an alarm
+ set_vm_memory_high_watermark({:absolute, 2000})
+
+ assert [:memory] == alarm_types(status()[:alarms])
+ assert length(@command.run([], context[:opts])) == 1
+
+ set_vm_memory_high_watermark(old_watermark)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_alarms_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_alarms_command_test.exs
new file mode 100644
index 0000000000..f5b64282e3
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_alarms_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 CheckAlarmsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+ import RabbitMQ.CLI.Core.Alarms, only: [alarm_types: 1]
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckAlarmsCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when target node has no alarms in effect, returns an empty list", context do
+ assert [] == status()[:alarms]
+
+ assert @command.run([], context[:opts]) == []
+ end
+
+ test "run: when target node has an alarm in effect, returns it", context do
+ old_watermark = status()[:vm_memory_high_watermark]
+ on_exit(fn() ->
+ set_vm_memory_high_watermark(old_watermark)
+ end)
+ # 2000 bytes will trigger an alarm
+ set_vm_memory_high_watermark({:absolute, 2000})
+
+ assert [:memory] == alarm_types(status()[:alarms])
+ assert length(@command.run([], context[:opts])) == 1
+
+ set_vm_memory_high_watermark(old_watermark)
+ end
+
+
+ test "output: when target node has no alarms in effect, returns a success", context do
+ assert [] == status()[:alarms]
+
+ assert match?({:ok, _}, @command.output([], context[:opts]))
+ end
+
+ test "output: when target node has an alarm in effect, returns a failure", context do
+ for input <- [
+ [
+ :file_descriptor_limit
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, :hare@warp10}, []}
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, :hare@warp10}, []},
+ {{:resource_limit, :memory, :hare@warp10}, []},
+ {{:resource_limit, :disk, :rabbit@warp10}, []},
+ {{:resource_limit, :memory, :rabbit@warp10}, []}
+ ]
+ ] do
+ assert match?({:error, _, _}, @command.output(input, context[:opts]))
+ end
+ end
+
+ test "output: when target node has an alarm in effect and --silent is passed, returns a silent failure", _context do
+ for input <- [
+ [
+ :file_descriptor_limit
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, :hare@warp10}, []}
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, :hare@warp10}, []},
+ {{:resource_limit, :memory, :hare@warp10}, []},
+ {{:resource_limit, :disk, :rabbit@warp10}, []},
+ {{:resource_limit, :memory, :rabbit@warp10}, []}
+ ]
+ ] do
+ assert {:error, :check_failed} == @command.output(input, %{silent: true})
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_local_alarms_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_local_alarms_command_test.exs
new file mode 100644
index 0000000000..0aaf66c707
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_local_alarms_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 CheckLocalAlarmsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+ import RabbitMQ.CLI.Core.Alarms, only: [alarm_types: 1]
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckLocalAlarmsCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when target node has no alarms in effect, returns an empty list", context do
+ assert [] == status()[:alarms]
+
+ assert @command.run([], context[:opts]) == []
+ end
+
+ test "run: when target node has a local alarm in effect, returns it", context do
+ old_watermark = status()[:vm_memory_high_watermark]
+ on_exit(fn() ->
+ set_vm_memory_high_watermark(old_watermark)
+ end)
+ # 2000 bytes will trigger an alarm
+ set_vm_memory_high_watermark({:absolute, 2000})
+
+ assert [:memory] == alarm_types(status()[:alarms])
+ assert length(@command.run([], context[:opts])) == 1
+
+ set_vm_memory_high_watermark(old_watermark)
+ end
+
+ test "output: when target node has no local alarms in effect, returns a success", context do
+ assert [] == status()[:alarms]
+
+ assert match?({:ok, _}, @command.output([], context[:opts]))
+ end
+
+ # note: it's run/2 that filters out non-local alarms
+ test "output: when target node has a local alarm in effect, returns a failure", context do
+ for input <- [
+ [
+ :file_descriptor_limit
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, get_rabbit_hostname()}, []},
+ {{:resource_limit, :memory, get_rabbit_hostname()}, []}
+ ]
+ ] do
+ assert match?({:error, _}, @command.output(input, context[:opts]))
+ end
+ end
+
+ # note: it's run/2 that filters out non-local alarms
+ test "output: when target node has an alarm in effect and --silent is passed, returns a silent failure", _context do
+ for input <- [
+ [
+ :file_descriptor_limit
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, :hare@warp10}, []}
+ ],
+ [
+ :file_descriptor_limit,
+ {{:resource_limit, :disk, get_rabbit_hostname()}, []},
+ {{:resource_limit, :memory, get_rabbit_hostname()}, []}
+ ]
+ ] do
+ assert {:error, :check_failed} == @command.output(input, %{silent: true})
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_port_connectivity_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_port_connectivity_command_test.exs
new file mode 100644
index 0000000000..845a7b6f1d
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_port_connectivity_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 CheckPortConnectivityCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckPortConnectivityCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "merge_defaults: provides a default timeout" do
+ assert @command.merge_defaults([], %{}) == {[], %{timeout: 30000}}
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: tries to connect to every inferred active listener", context do
+ assert match?({true, _}, @command.run([], context[:opts]))
+ end
+
+
+ test "output: when all connections succeeded, returns a success", context do
+ assert match?({:ok, _}, @command.output({true, []}, context[:opts]))
+ end
+
+ # note: it's run/2 that filters out non-local alarms
+ test "output: when target node has a local alarm in effect, returns a failure", context do
+ failure = {:listener, :rabbit@mercurio, :lolz, 'mercurio',
+ {0, 0, 0, 0, 0, 0, 0, 0}, 7761613,
+ [backlog: 128, nodelay: true]}
+ assert match?({:error, _}, @command.output({false, [failure]}, context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_port_listener_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_port_listener_command_test.exs
new file mode 100644
index 0000000000..7c0428c190
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_port_listener_command_test.exs
@@ -0,0 +1,62 @@
+## 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 CheckPortListenerCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckPortListenerCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "merge_defaults: nothing to do" do
+ assert @command.merge_defaults([], %{}) == {[], %{}}
+ end
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when two or more arguments are provided, returns a failure" do
+ assert @command.validate([5672, 61613], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats a single positional argument and default switches as a success" do
+ assert @command.validate([1883], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([61613], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when a listener for the protocol is active, returns a success", context do
+ assert match?({true, _}, @command.run([5672], context[:opts]))
+ end
+
+ test "run: when a listener on the port is not active or unknown, returns an error", context do
+ assert match?({false, _, _}, @command.run([47777], context[:opts]))
+ end
+
+ test "output: when a listener for the port is active, returns a success", context do
+ assert match?({:ok, _}, @command.output({true, 5672}, context[:opts]))
+ end
+
+ test "output: when a listener for the port is not active, returns an error", context do
+ assert match?({:error, _, _}, @command.output({false, 15672, []}, context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_protocol_listener_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_protocol_listener_command_test.exs
new file mode 100644
index 0000000000..a6aef88bc1
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_protocol_listener_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 CheckProtocolListenerCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckProtocolListenerCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "merge_defaults: nothing to do" do
+ assert @command.merge_defaults([], %{}) == {[], %{}}
+ end
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when two or more arguments are provided, returns a failure" do
+ assert @command.validate(["amqp", "stomp"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats a single positional argument and default switches as a success" do
+ assert @command.validate(["mqtt"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run(["stomp"], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when a listener for the protocol is active, returns a success", context do
+ assert match?({true, _}, @command.run(["amqp"], context[:opts]))
+ end
+
+ test "run: accepts a number of alternative protocol names/spellings", context do
+ for p <- ["amqp", "amqp1.0", "amqp10", "amqp091", "stomp1.2", "distribution"] do
+ assert match?({true, _}, @command.run([p], context[:opts]))
+ end
+ end
+
+ test "run: when a listener for the protocol is not active or unknown, returns an error", context do
+ assert match?({false, _, _}, @command.run(["non-existent-proto"], context[:opts]))
+ end
+
+ test "output: when a listener for the protocol is active, returns a success", context do
+ assert match?({:ok, _}, @command.output({true, "amqp"}, context[:opts]))
+ end
+
+ test "output: when a listener for the protocol is not active, returns an error", context do
+ assert match?({:error, _}, @command.output({false, "http", []}, context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_running_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_running_command_test.exs
new file mode 100644
index 0000000000..ab89d1e89e
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_running_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 CheckRunningCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckRunningCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when the RabbitMQ app is booted and started, returns true", context do
+ await_rabbitmq_startup()
+
+ assert @command.run([], context[:opts])
+ end
+
+ test "run: when the RabbitMQ app is stopped, returns false", context do
+ stop_rabbitmq_app()
+
+ refute is_rabbitmq_app_running()
+ refute @command.run([], context[:opts])
+
+ start_rabbitmq_app()
+ end
+
+ test "output: when the result is true, returns successfully", context do
+ assert match?({:ok, _}, @command.output(true, context[:opts]))
+ end
+
+ # this is a check command
+ test "output: when the result is false, returns an error", context do
+ assert match?({:error, _}, @command.output(false, context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/check_virtual_hosts_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/check_virtual_hosts_command_test.exs
new file mode 100644
index 0000000000..2fab76ae9b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/check_virtual_hosts_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 CheckVirtualHostsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CheckVirtualHostsCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "merge_defaults: is a no-op" 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "output: when all virtual hosts are reported as up, returns a success", context do
+ assert match?({:ok, _}, @command.output([], context[:opts]))
+ end
+
+ test "output: when target node reports a virtual host as down, returns a failure", context do
+ assert match?({:error, _}, @command.output(["a-down-vhost"], context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/cipher_suites_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/cipher_suites_command_test.exs
new file mode 100644
index 0000000000..2ee5edddb8
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/cipher_suites_command_test.exs
@@ -0,0 +1,101 @@
+## 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 CipherSuitesCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CipherSuitesCommand
+
+ 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] || "openssl",
+ all: false
+ }}
+ end
+
+ test "merge_defaults: defaults to the OpenSSL format" do
+ assert @command.merge_defaults([], %{}) == {[], %{format: "openssl", all: false}}
+ end
+
+ test "merge_defaults: format is case insensitive" do
+ assert @command.merge_defaults([], %{format: "OpenSSL"}) == {[], %{format: "openssl", all: false}}
+ assert @command.merge_defaults([], %{format: "Erlang"}) == {[], %{format: "erlang", all: false}}
+ assert @command.merge_defaults([], %{format: "Map"}) == {[], %{format: "map", all: false}}
+ end
+
+ test "merge_defaults: format can be overridden" do
+ assert @command.merge_defaults([], %{format: "map"}) == {[], %{format: "map", all: false}}
+ end
+
+ test "validate: treats positional arguments as a failure", context do
+ assert @command.validate(["extra-arg"], context[:opts]) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats empty positional arguments and default switches as a success", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: supports openssl, erlang and map formats", context do
+ assert @command.validate([], Map.merge(context[:opts], %{format: "openssl"})) == :ok
+ assert @command.validate([], Map.merge(context[:opts], %{format: "erlang"})) == :ok
+ assert @command.validate([], Map.merge(context[:opts], %{format: "map"})) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag format: "openssl"
+ test "run: returns a list of cipher suites in OpenSSL format", context do
+ res = @command.run([], context[:opts])
+ for cipher <- res, do: assert true == is_list(cipher)
+ # the list is long and its values are environment-specific,
+ # so we simply assert that it is non-empty. MK.
+ assert length(res) > 0
+ end
+
+ @tag format: "erlang"
+ test "run: returns a list of cipher suites in erlang format", context do
+ res = @command.run([], context[:opts])
+
+ for cipher <- res, do: assert true = is_tuple(cipher)
+ # the list is long and its values are environment-specific,
+ # so we simply assert that it is non-empty. MK.
+ assert length(res) > 0
+ end
+
+ @tag format: "map"
+ test "run: returns a list of cipher suites in map format", context do
+ res = @command.run([], context[:opts])
+ for cipher <- res, do: assert true = is_map(cipher)
+ # the list is long and its values are environment-specific,
+ # so we simply assert that it is non-empty. MK.
+ assert length(res) > 0
+ end
+
+ test "run: returns more cipher suites when all suites requested", context do
+ default_suites_opts = Map.merge(context[:opts], %{all: false})
+ default_suites = @command.run([], default_suites_opts)
+
+ all_suites_opts = Map.merge(context[:opts], %{all: true})
+ all_suites = @command.run([], all_suites_opts)
+
+ assert length(all_suites) > length(default_suites)
+ assert length(default_suites -- all_suites) == 0
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/command_line_arguments_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/command_line_arguments_command_test.exs
new file mode 100644
index 0000000000..caa959ce44
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/command_line_arguments_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 CommandLineArgumentsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.CommandLineArgumentsCommand
+
+ 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, command line arguments returns an arg count error", context do
+ assert @command.validate(["extra"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ test "run: command line arguments request to a reachable node succeeds", context do
+ output = @command.run([], context[:opts]) |> Enum.to_list
+
+ assert_stream_without_errors(output)
+ end
+
+ test "run: command line arguments 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/Command line arguments of node #{get_rabbit_hostname()}/
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/consume_event_stream_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/consume_event_stream_command_test.exs
new file mode 100644
index 0000000000..b11cdb38c2
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/consume_event_stream_command_test.exs
@@ -0,0 +1,73 @@
+## 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 ConsumeEventStreamCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ConsumeEventStreamCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ duration: :infinity,
+ pattern: ".*"
+ }}
+ end
+
+ test "merge_defaults: duration defaults to infinity, pattern to anything" do
+ assert @command.merge_defaults([], %{}) == {[], %{duration: :infinity,
+ pattern: ".*",
+ quiet: true}}
+ 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", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: consumes events for N seconds", context do
+
+ stream = @command.run([], Map.merge(context[:opts], %{duration: 5}))
+ :rpc.call(get_rabbit_hostname(), :rabbit_event, :notify, [String.to_atom("event_type1"),
+ [{String.to_atom("args"), 1}]])
+ :rpc.call(get_rabbit_hostname(), :rabbit_event, :notify, [String.to_atom("event_type2"),
+ [{String.to_atom("pid"), self()}]])
+
+
+ event1 = Enum.find(stream, nil, fn x -> Keyword.get(x, :event, nil) == "event.type1" end)
+ event2 = Enum.find(stream, nil, fn x -> Keyword.get(x, :event, nil) == "event.type2" end)
+ assert event1 != nil
+ assert event2 != nil
+ assert Keyword.get(event1, :args, nil) == 1
+ assert is_binary(Keyword.get(event2, :pid, nil))
+
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/disable_auth_attempt_source_tracking_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/disable_auth_attempt_source_tracking_command_test.exs
new file mode 100644
index 0000000000..7a2b4295c7
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/disable_auth_attempt_source_tracking_command_test.exs
@@ -0,0 +1,39 @@
+## 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 DisbleAuthAttemptSourceTrackingCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.DisableAuthAttemptSourceTrackingCommand
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}}
+ end
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 15000
+ test "run: disables source tracking for auth attempt stats", context do
+ assert :ok = @command.run([], context[:opts])
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/discover_peers_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/discover_peers_command_test.exs
new file mode 100644
index 0000000000..dd54d6eed9
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/discover_peers_command_test.exs
@@ -0,0 +1,39 @@
+## 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 DiscoverPeersCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.DiscoverPeersCommand
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}}
+ end
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 15000
+ test "run: returns a list of nodes when the backend isn't configured", context do
+ assert match?({:ok, {[], _}}, @command.run([], context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/enable_auth_attempt_source_tracking_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/enable_auth_attempt_source_tracking_command_test.exs
new file mode 100644
index 0000000000..c55ac6134b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/enable_auth_attempt_source_tracking_command_test.exs
@@ -0,0 +1,39 @@
+## 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 EnableAuthAttemptSourceTrackingCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.EnableAuthAttemptSourceTrackingCommand
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}}
+ end
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 15000
+ test "run: enables source tracking for auth attempt stats", context do
+ assert :ok = @command.run([], context[:opts])
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/erlang_cookie_hash_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/erlang_cookie_hash_command_test.exs
new file mode 100644
index 0000000000..5dff653989
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/erlang_cookie_hash_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 ErlangCookieHashCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ErlangCookieHashCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 5000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = %{node: :jake@thedog, timeout: 200}
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], opts)))
+ end
+
+ test "run: returns the erlang cookie hash", context do
+ res = @command.run([], context[:opts])
+ assert is_bitstring(res)
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/erlang_cookie_sources_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/erlang_cookie_sources_command_test.exs
new file mode 100644
index 0000000000..794dd52a44
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/erlang_cookie_sources_command_test.exs
@@ -0,0 +1,37 @@
+## 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 ErlangCookieSourcesCommandTest do
+ use ExUnit.Case, async: true
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ErlangCookieSourcesCommand
+
+ setup _context do
+ {:ok, opts: %{}}
+ end
+
+ 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
+
+ test "run: returns Erlang cookie sources info", context do
+ result = @command.run([], context[:opts])
+
+ assert result[:effective_user] != nil
+ assert result[:home_dir] != nil
+ assert result[:cookie_file_path] != nil
+ assert result[:cookie_file_exists] != nil
+ assert result[:cookie_file_access] != nil
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/erlang_version_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/erlang_version_command_test.exs
new file mode 100644
index 0000000000..3bdaa645e2
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/erlang_version_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 ErlangVersionCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ErlangVersionCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ details: false,
+ offline: false
+ }}
+ end
+
+ test "merge_defaults: defaults to remote version and abbreviated output" do
+ assert @command.merge_defaults([], %{}) == {[], %{details: false, offline: false}}
+ 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 "validate: treats empty positional arguments and --details as a success" do
+ assert @command.validate([], %{details: true}) == :ok
+ end
+
+ test "validate: treats empty positional arguments and --offline as a success" do
+ assert @command.validate([], %{offline: true}) == :ok
+ end
+
+ test "validate: treats empty positional arguments, --details and --offline as a success" do
+ assert @command.validate([], %{details: true, offline: true}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, details: false})))
+ end
+
+ test "run: returns Erlang/OTP version on the target node", context do
+ res = @command.run([], context[:opts])
+ assert is_bitstring(res)
+ end
+
+ test "run with --details: returns Erlang/OTP version on the target node", context do
+ res = @command.run([], Map.merge(%{details: true}, context[:opts]))
+ assert is_bitstring(res)
+ end
+
+ test "run: when --offline is used, returns local Erlang/OTP version", context do
+ res = @command.run([], Map.merge(context[:opts], %{offline: true}))
+ assert is_bitstring(res)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/is_booting_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/is_booting_command_test.exs
new file mode 100644
index 0000000000..fc7c2595a9
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/is_booting_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 IsBootingCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.IsBootingCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when the RabbitMQ app is fully booted and running, returns false", context do
+ await_rabbitmq_startup()
+
+ refute @command.run([], context[:opts])
+ end
+
+ test "run: when the RabbitMQ app is stopped, returns false", context do
+ stop_rabbitmq_app()
+
+ refute is_rabbitmq_app_running()
+ refute @command.run([], context[:opts])
+
+ start_rabbitmq_app()
+ end
+
+ test "output: when the result is true, returns successfully", context do
+ assert match?({:ok, _}, @command.output(true, context[:opts]))
+ end
+
+ # this is an info command and not a check one
+ test "output: when the result is false, returns successfully", context do
+ assert match?({:ok, _}, @command.output(false, context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/is_running_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/is_running_command_test.exs
new file mode 100644
index 0000000000..120af9d7d7
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/is_running_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 IsRunningCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.IsRunningCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: when the RabbitMQ app is booted and started, returns true", context do
+ await_rabbitmq_startup()
+
+ assert @command.run([], context[:opts])
+ end
+
+ test "run: when the RabbitMQ app is stopped, returns false", context do
+ stop_rabbitmq_app()
+
+ refute is_rabbitmq_app_running()
+ refute @command.run([], context[:opts])
+
+ start_rabbitmq_app()
+ end
+
+ test "output: when the result is true, returns successfully", context do
+ assert match?({:ok, _}, @command.output(true, context[:opts]))
+ end
+
+ # this is an info command and not a check one
+ test "output: when the result is false, returns successfully", context do
+ assert match?({:ok, _}, @command.output(false, context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/list_network_interfaces_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/list_network_interfaces_command_test.exs
new file mode 100644
index 0000000000..ccaac33d9b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/list_network_interfaces_command_test.exs
@@ -0,0 +1,39 @@
+## 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 ListNetworkInterfacesCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ListNetworkInterfacesCommand
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout]}}
+ end
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 15000
+ test "run: returns a map of interfaces", context do
+ assert match?(%{}, @command.run([], context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/list_node_auth_attempt_stats_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/list_node_auth_attempt_stats_command_test.exs
new file mode 100644
index 0000000000..c6ac28a340
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/list_node_auth_attempt_stats_command_test.exs
@@ -0,0 +1,39 @@
+## 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 ListNodeAuthAttemptStatsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ListNodeAuthAttemptStatsCommand
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{node: get_rabbit_hostname(), timeout: context[:test_timeout], by_source: false}}
+ end
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 15000
+ test "run: returns auth attempt stats", context do
+ assert is_list(@command.run([], context[:opts]))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/listeners_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/listeners_command_test.exs
new file mode 100644
index 0000000000..fc20cae7fc
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/listeners_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 ListenersCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+ import RabbitMQ.CLI.Core.Listeners, only: [listener_maps: 1]
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ListenersCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: returns a list of node-local listeners", context do
+ xs = @command.run([], context[:opts]) |> listener_maps
+
+ assert length(xs) >= 3
+ for p <- [5672, 61613, 25672] do
+ assert Enum.any?(xs, fn %{port: port} -> port == p end)
+ end
+ end
+
+ test "output: returns a formatted list of node-local listeners", context do
+ raw = @command.run([], context[:opts])
+ {:ok, msg} = @command.output(raw, context[:opts])
+
+ for p <- [5672, 61613, 25672] do
+ assert msg =~ ~r/#{p}/
+ end
+ end
+
+ test "output: when formatter is JSON, returns an array of listener maps", context do
+ raw = @command.run([], context[:opts])
+ {:ok, doc} = @command.output(raw, Map.merge(%{formatter: "json"}, context[:opts]))
+ xs = doc["listeners"]
+
+ assert length(xs) >= 3
+ for p <- [5672, 61613, 25672] do
+ assert Enum.any?(xs, fn %{port: port} -> port == p end)
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/log_location_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/log_location_command_test.exs
new file mode 100644
index 0000000000..64a85fc519
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/log_location_command_test.exs
@@ -0,0 +1,98 @@
+## 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 LogLocationCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.LogLocationCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ all: false
+ }}
+ end
+
+ test "merge_defaults: all is false" do
+ assert @command.merge_defaults([], %{}) == {[], %{all: :false}}
+ 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([], %{all: :false}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: prints default log location", context do
+ # Let Lager's log message rate lapse or else some messages
+ # we assert on might be dropped. MK.
+ Process.sleep(1000)
+ {:ok, logfile} = @command.run([], context[:opts])
+ log_message = "file location"
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, [log_message])
+ wait_for_log_message(log_message, logfile)
+ {:ok, log_file_data} = File.read(logfile)
+ assert String.match?(log_file_data, Regex.compile!(log_message))
+ end
+
+ test "run: shows all log locations", context do
+ # Let Lager's log message rate lapse or else some messages
+ # we assert on might be dropped. MK.
+ Process.sleep(1000)
+ # This assumes default configuration
+ [logfile, upgrade_log_file] =
+ @command.run([], Map.merge(context[:opts], %{all: true}))
+
+ log_message = "checking the default log file when checking all"
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, [log_message])
+ wait_for_log_message(log_message, logfile)
+
+ log_message_upgrade = "checking the upgrade log file when checking all"
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_log, :log, [:upgrade, :error, log_message_upgrade, []])
+ wait_for_log_message(log_message_upgrade, upgrade_log_file)
+ end
+
+ test "run: fails if there is no log file configured", context do
+ {:ok, upgrade_file} = :rpc.call(get_rabbit_hostname(), :application, :get_env, [:rabbit, :lager_upgrade_file])
+ {:ok, default_file} = :rpc.call(get_rabbit_hostname(), :application, :get_env, [:rabbit, :lager_default_file])
+ on_exit([], fn ->
+ :rpc.call(get_rabbit_hostname(), :application, :set_env, [:rabbit, :lager_upgrade_file, upgrade_file])
+ :rpc.call(get_rabbit_hostname(), :application, :set_env, [:rabbit, :lager_default_file, default_file])
+ :rpc.call(get_rabbit_hostname(), :rabbit_lager, :configure_lager, [])
+ start_rabbitmq_app()
+ end)
+ stop_rabbitmq_app()
+ :rpc.call(get_rabbit_hostname(), :application, :unset_env, [:rabbit, :lager_upgrade_file])
+ :rpc.call(get_rabbit_hostname(), :application, :unset_env, [:rabbit, :lager_default_file])
+ :rpc.call(get_rabbit_hostname(), :application, :unset_env, [:rabbit, :log])
+ :rpc.call(get_rabbit_hostname(), :rabbit_lager, :configure_lager, [])
+ {:error, "No log files configured on the node"} = @command.run([], context[:opts])
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/log_tail_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/log_tail_command_test.exs
new file mode 100644
index 0000000000..fb19821d55
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/log_tail_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) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
+
+defmodule LogTailCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.LogTailCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ number: 50
+ }}
+ end
+
+ test "merge_defaults: number is 50" do
+ assert @command.merge_defaults([], %{}) == {[], %{number: 50}}
+ 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", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: shows last 50 lines from the log by default", context do
+ # Let Lager's log message rate lapse or else some messages
+ # we assert on might be dropped. MK.
+ Process.sleep(1000)
+ clear_log_files()
+ log_messages =
+ Enum.map(:lists.seq(1, 50),
+ fn(n) ->
+ message = "Getting log tail #{n}"
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, [message])
+ message
+ end)
+ wait_for_log_message("Getting log tail 50")
+ lines = @command.run([], context[:opts])
+ assert Enum.count(lines) == 50
+
+ Enum.map(Enum.zip(log_messages, lines),
+ fn({message, line}) ->
+ assert String.match?(line, Regex.compile!(message))
+ end)
+ end
+
+ test "run: returns N lines", context do
+ # Let Lager's log message rate lapse or else some messages
+ # we assert on might be dropped. MK.
+ Process.sleep(1000)
+
+ ## Log a bunch of lines
+ Enum.map(:lists.seq(1, 50),
+ fn(n) ->
+ message = "More lines #{n}"
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, [message])
+ message
+ end)
+ wait_for_log_message("More lines 50")
+ assert Enum.count(@command.run([], Map.merge(context[:opts], %{number: 20}))) == 20
+ assert Enum.count(@command.run([], Map.merge(context[:opts], %{number: 30}))) == 30
+ assert Enum.count(@command.run([], Map.merge(context[:opts], %{number: 40}))) == 40
+ end
+
+ test "run: may return less than N lines if N is high", context do
+ # Let Lager's log message rate lapse or else some messages
+ # we assert on might be dropped. MK.
+ Process.sleep(1000)
+ clear_log_files()
+ ## Log a bunch of lines
+ Enum.map(:lists.seq(1, 100),
+ fn(n) ->
+ message = "More lines #{n}"
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, [message])
+ message
+ end)
+ wait_for_log_message("More lines 50")
+ assert Enum.count(@command.run([], Map.merge(context[:opts], %{number: 50}))) == 50
+ assert Enum.count(@command.run([], Map.merge(context[:opts], %{number: 200}))) < 200
+ end
+
+ def clear_log_files() do
+ [_|_] = logs = :rpc.call(get_rabbit_hostname(), :rabbit_lager, :log_locations, [])
+ Enum.map(logs, fn(log) ->
+ File.write(log, "")
+ end)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/log_tail_stream_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/log_tail_stream_command_test.exs
new file mode 100644
index 0000000000..4ad2785604
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/log_tail_stream_command_test.exs
@@ -0,0 +1,107 @@
+## 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 LogTailStreamCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.LogTailStreamCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ duration: :infinity
+ }}
+ end
+
+ test "merge_defaults: duration defaults to infinity" do
+ assert @command.merge_defaults([], %{}) == {[], %{duration: :infinity}}
+ 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", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: streams messages for N seconds", context do
+ ensure_log_file()
+ time_before = System.system_time(:second)
+
+ stream = @command.run([], Map.merge(context[:opts], %{duration: 15}))
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, ["Message"])
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, ["Message1"])
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, ["Message2"])
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, ["Message3"])
+
+ # This may take a long time and fail with an ExUnit timeout
+ data = Enum.join(stream)
+
+ time_after = System.system_time(:second)
+
+ assert String.match?(data, ~r/Message/)
+ assert String.match?(data, ~r/Message1/)
+ assert String.match?(data, ~r/Message2/)
+ assert String.match?(data, ~r/Message3/)
+
+ time_spent = time_after - time_before
+ assert time_spent > 15
+ # This my take longer then duration but not too long
+ assert time_spent < 45
+ end
+
+ test "run: may return an error if there is no log", context do
+ delete_log_files()
+ {:error, :enoent} = @command.run([], Map.merge(context[:opts], %{duration: 5}))
+ end
+
+ def ensure_log_file() do
+ [log|_] = :rpc.call(get_rabbit_hostname(), :rabbit_lager, :log_locations, [])
+ ensure_file(log, 100)
+ end
+
+ def ensure_file(log, 0) do
+ flunk("timed out trying to ensure the log file #{log}")
+ end
+ def ensure_file(log, attempts) do
+ case File.exists?(log) do
+ true -> :ok
+ false ->
+ :rpc.call(get_rabbit_hostname(), :rabbit_log, :error, ["Ping"])
+ :timer.sleep(100)
+ ensure_file(log, attempts - 1)
+ end
+ end
+
+ def delete_log_files() do
+ [_|_] = logs = :rpc.call(get_rabbit_hostname(), :rabbit_lager, :log_locations, [])
+ Enum.map(logs, fn(log) ->
+ File.rm(log)
+ end)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/maybe_stuck_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/maybe_stuck_command_test.exs
new file mode 100644
index 0000000000..3b70966d1c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/maybe_stuck_command_test.exs
@@ -0,0 +1,48 @@
+## 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 MaybeStuckCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.MaybeStuckCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 15000
+ }}
+ end
+
+ test "merge_defaults: returns inputs" do
+ assert @command.merge_defaults([], %{timeout: 30}) == {[], %{timeout: 30}}
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 0
+ test "run: timeout throws a badrpc", context do
+ assert @command.run([], context[:opts]) == {:badrpc, :timeout}
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/memory_breakdown_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/memory_breakdown_command_test.exs
new file mode 100644
index 0000000000..8f7ffb14dc
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/memory_breakdown_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) 2016-2020 VMware, Inc. or its affiliates. All rights reserved.
+
+defmodule MemoryBreakdownCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.MemoryBreakdownCommand
+
+ 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(),
+ timeout: 5000,
+ unit: "gb"
+ }}
+ end
+
+ test "validate: specifying a positional argument fails validation", context do
+ assert @command.validate(["abc"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+
+ assert @command.validate(["abc", "def"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ test "validate: specifying no positional arguments and no options succeeds", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: specifying gigabytes as a --unit succeeds", context do
+ assert @command.validate([], Map.merge(context[:opts], %{unit: "gb"})) == :ok
+ end
+
+ test "validate: specifying bytes as a --unit succeeds", context do
+ assert @command.validate([], Map.merge(context[:opts], %{unit: "bytes"})) == :ok
+ end
+
+ test "validate: specifying megabytes as a --unit succeeds", context do
+ assert @command.validate([], Map.merge(context[:opts], %{unit: "mb"})) == :ok
+ end
+
+ test "validate: specifying glip-glops as a --unit fails validation", context do
+ assert @command.validate([], Map.merge(context[:opts], %{unit: "glip-glops"})) ==
+ {:validation_failure, "unit 'glip-glops' is not supported. Please use one of: bytes, mb, gb"}
+ end
+
+ test "run: request to a non-existent RabbitMQ node returns a nodedown" do
+ opts = %{node: :jake@thedog, timeout: 200, unit: "gb"}
+ assert match?({:badrpc, _}, @command.run([], opts))
+ end
+
+ test "banner", context do
+ s = @command.banner([], context[:opts])
+
+ assert s =~ ~r/Reporting memory breakdown on node/
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/observer_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/observer_command_test.exs
new file mode 100644
index 0000000000..8ff97abb0b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/observer_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 ObserverCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ObserverCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ interval: 5,
+ timeout: context[:test_timeout] || 15000
+ }}
+ end
+
+ test "merge_defaults: injects a default interval of 5s" do
+ assert @command.merge_defaults([], %{}) == {[], %{interval: 5}}
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run([], %{node: :jake@thedog, interval: 5}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/os_env_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/os_env_command_test.exs
new file mode 100644
index 0000000000..254b41c9f2
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/os_env_command_test.exs
@@ -0,0 +1,62 @@
+## 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 OsEnvCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.OsEnvCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ all: false
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: returns defined RabbitMQ-specific environment variables", context do
+ vars = @command.run([], context[:opts])
+
+ # Only variables that are used by RABBITMQ are returned.
+ # They can be prefixed with RABBITMQ_ or not, rabbit_env tries both
+ # when filtering env variables down.
+ assert Enum.any?(vars, fn({k, _v}) ->
+ String.starts_with?(k, "RABBITMQ_") or String.starts_with?(k, "rabbitmq_")
+ end)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/resolve_hostname_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/resolve_hostname_command_test.exs
new file mode 100644
index 0000000000..2019154f0c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/resolve_hostname_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 ResolveHostnameCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ResolveHostnameCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ address_family: "ipv4",
+ offline: false
+ }}
+ end
+
+ test "merge_defaults: defaults to IPv4 address family" do
+ assert @command.merge_defaults([], %{}) == {[], %{address_family: "IPv4", offline: false}}
+ end
+
+ test "validate: a single positional argument passes validation" do
+ assert @command.validate(["rabbitmq.com"], %{}) == :ok
+ end
+
+ test "validate: treats positional arguments as a failure" do
+ assert @command.validate(["elixir-lang.org", "extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: address family other than IPv4 or IPv6 fails validation" do
+ assert match?({:validation_failure, {:bad_argument, _}},
+ @command.validate(["elixir-lang.org"], %{address_family: "ipv5"}))
+
+ assert match?({:validation_failure, {:bad_argument, _}},
+ @command.validate(["elixir-lang.org"], %{address_family: "IPv5"}))
+ end
+
+ test "validate: IPv4 for address family passes validation" do
+ assert @command.validate(["elixir-lang.org"], %{address_family: "ipv4"}) == :ok
+ assert @command.validate(["elixir-lang.org"], %{address_family: "IPv4"}) == :ok
+ end
+
+ test "validate: IPv6 for address family passes validation" do
+ assert @command.validate(["elixir-lang.org"], %{address_family: "ipv6"}) == :ok
+ assert @command.validate(["elixir-lang.org"], %{address_family: "IPv6"}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})
+ assert match?({:badrpc, _}, @command.run(["elixir-lang.org"], opts))
+ end
+
+ test "run: returns a resolution result", context do
+ case @command.run(["github.com"], context[:opts]) do
+ {:ok, _hostent} -> :ok
+ {:error, :nxdomain} -> :ok
+ other -> flunk("hostname resolution returned #{other}")
+ end
+ end
+
+ test "run with --offline: returns a resolution result", context do
+ case @command.run(["github.com"], Map.merge(context[:opts], %{offline: true})) do
+ {:ok, _hostent} -> :ok
+ {:error, :nxdomain} -> :ok
+ other -> flunk("hostname resolution returned #{other}")
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/resolver_info_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/resolver_info_command_test.exs
new file mode 100644
index 0000000000..001371ed37
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/resolver_info_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 ResolverInfoCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ResolverInfoCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ start_rabbitmq_app()
+
+ ExUnit.configure([max_cases: 1])
+
+ on_exit([], fn ->
+ start_rabbitmq_app()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ offline: false
+ }}
+ end
+
+ test "merge_defaults: defaults to offline mode" do
+ assert @command.merge_defaults([], %{}) == {[], %{offline: false}}
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog, timeout: 100})))
+ end
+
+ test "run: returns host resolver (inetrc) information", context do
+ result = @command.run([], context[:opts])
+
+ assert result["lookup"] != nil
+ assert result["hosts_file"] != nil
+ end
+
+ test "run: returns host resolver (inetrc) information with --offline", context do
+ result = @command.run([], Map.merge(context[:opts], %{offline: true}))
+
+ assert result["lookup"] != nil
+ assert result["hosts_file"] != nil
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/runtime_thread_stats_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/runtime_thread_stats_command_test.exs
new file mode 100644
index 0000000000..34ab7b9c63
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/runtime_thread_stats_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 RuntimeThreadStatsCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.RuntimeThreadStatsCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 10000,
+ sample_interval: 1
+ }}
+ end
+
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 2000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ @tag test_timeout: 6000
+ test "run: returns msacc-formatted output", context do
+ res = @command.run([], context[:opts])
+ # the output is long and its values are environment-specific,
+ # so we simply assert that it is non-empty. MK.
+ assert length(res) > 0
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/schema_info_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/schema_info_command_test.exs
new file mode 100644
index 0000000000..369592522a
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/schema_info_command_test.exs
@@ -0,0 +1,69 @@
+defmodule SchemaInfoCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.SchemaInfoCommand
+ @default_timeout :infinity
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {
+ :ok,
+ opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || @default_timeout
+ }
+ }
+ end
+
+ test "merge_defaults: adds all keys if none specificed", context do
+ default_keys = ~w(name cookie active_replicas user_properties)
+
+ {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", "cookie"], context[:opts]) ==
+ {:validation_failure, {:bad_info_key, [:quack]}}
+ assert @command.validate(["access_mode", "oink"], context[:opts]) ==
+ {:validation_failure, {:bad_info_key, [:oink]}}
+ assert @command.validate(["access_mode", "oink", "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}
+ end
+
+ test "run: can filter info keys", context do
+ wanted_keys = ~w(name access_mode)
+ assert match?([[name: _, access_mode: _] | _], run_command_to_list(@command, [wanted_keys, context[:opts]]))
+ end
+
+ test "banner" do
+ assert String.starts_with?(@command.banner([], %{node: "node@node"}), "Asking node")
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/server_version_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/server_version_command_test.exs
new file mode 100644
index 0000000000..72c32e32f1
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/server_version_command_test.exs
@@ -0,0 +1,48 @@
+## 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 ServerVersionCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.ServerVersionCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run: returns RabbitMQ version on the target node", context do
+ res = @command.run([], context[:opts])
+ assert is_bitstring(res)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/diagnostics/tls_versions_command_test.exs b/deps/rabbitmq_cli/test/diagnostics/tls_versions_command_test.exs
new file mode 100644
index 0000000000..0e38a0461e
--- /dev/null
+++ b/deps/rabbitmq_cli/test/diagnostics/tls_versions_command_test.exs
@@ -0,0 +1,60 @@
+## 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 TlsVersionsCommandTest do
+ use ExUnit.Case
+ import TestHelper
+
+ @command RabbitMQ.CLI.Diagnostics.Commands.TlsVersionsCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "merge_defaults: is a no-op" 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
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], %{node: :jake@thedog})))
+ end
+
+ test "run when formatter is set to JSON: return a document with a list of supported TLS versions", context do
+ m = @command.run([], Map.merge(context[:opts], %{formatter: "json"})) |> Map.new
+ xs = Map.get(m, :available)
+
+ # assert that we have a list and tlsv1.2 is included
+ assert length(xs) > 0
+ assert Enum.member?(xs, :"tlsv1.2")
+ end
+
+ test "run and output: return a list of supported TLS versions", context do
+ m = @command.run([], context[:opts])
+ {:ok, res} = @command.output(m, context[:opts])
+
+ # assert that we have a list and tlsv1.2 is included
+ assert length(res) > 0
+ assert Enum.member?(res, :"tlsv1.2")
+ end
+end
diff --git a/deps/rabbitmq_cli/test/fixtures/files/definitions.json b/deps/rabbitmq_cli/test/fixtures/files/definitions.json
new file mode 100644
index 0000000000..1391870028
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/definitions.json
@@ -0,0 +1,40 @@
+{
+ "rabbit_version": "3.7.21",
+ "vhosts": [
+ {
+ "name": "\/"
+ }
+ ],
+ "queues": [
+
+ ],
+ "exchanges": [
+ {
+ "name": "project.topic.default",
+ "vhost": "\/",
+ "type": "topic",
+ "durable": true,
+ "auto_delete": false,
+ "internal": false,
+ "arguments": {
+
+ }
+ }
+ ],
+ "bindings": [
+
+ ],
+
+ "parameters": [
+ {
+ "component": "federation-upstream",
+ "name": "up-1",
+ "value": {
+ "ack-mode": "on-confirm",
+ "trust-user-id": false,
+ "uri": "amqp://127.0.0.1:5672"
+ },
+ "vhost": "/"
+ }
+ ]
+}
diff --git a/deps/rabbitmq_cli/test/fixtures/files/empty_pidfile.pid b/deps/rabbitmq_cli/test/fixtures/files/empty_pidfile.pid
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/empty_pidfile.pid
diff --git a/deps/rabbitmq_cli/test/fixtures/files/invalid_erl_expression.escript b/deps/rabbitmq_cli/test/fixtures/files/invalid_erl_expression.escript
new file mode 100644
index 0000000000..6802266390
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/invalid_erl_expression.escript
@@ -0,0 +1 @@
+1 + . $$$ ///\\\
diff --git a/deps/rabbitmq_cli/test/fixtures/files/invalid_pidfile.pid b/deps/rabbitmq_cli/test/fixtures/files/invalid_pidfile.pid
new file mode 100644
index 0000000000..a4fa4cf4e4
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/invalid_pidfile.pid
@@ -0,0 +1 @@
+invalid///&
diff --git a/deps/rabbitmq_cli/test/fixtures/files/loaded_applications.escript b/deps/rabbitmq_cli/test/fixtures/files/loaded_applications.escript
new file mode 100644
index 0000000000..6db0ab52d3
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/loaded_applications.escript
@@ -0,0 +1 @@
+application:loaded_applications().
diff --git a/deps/rabbitmq_cli/test/fixtures/files/valid_erl_expression.escript b/deps/rabbitmq_cli/test/fixtures/files/valid_erl_expression.escript
new file mode 100644
index 0000000000..fb18c2507a
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/valid_erl_expression.escript
@@ -0,0 +1 @@
+1 + 1.
diff --git a/deps/rabbitmq_cli/test/fixtures/files/valid_pidfile.pid b/deps/rabbitmq_cli/test/fixtures/files/valid_pidfile.pid
new file mode 100644
index 0000000000..8b64142ea1
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/valid_pidfile.pid
@@ -0,0 +1 @@
+13566
diff --git a/deps/rabbitmq_cli/test/fixtures/files/valid_pidfile_with_spaces.pid b/deps/rabbitmq_cli/test/fixtures/files/valid_pidfile_with_spaces.pid
new file mode 100644
index 0000000000..83a97170f4
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/files/valid_pidfile_with_spaces.pid
@@ -0,0 +1 @@
+ 83777
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/.gitignore b/deps/rabbitmq_cli/test/fixtures/plugins/.gitignore
new file mode 100644
index 0000000000..8f884eb3ab
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/.gitignore
@@ -0,0 +1 @@
+!*.ez
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.1.0.ez
new file mode 100644
index 0000000000..8cbe118971
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.2.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.2.0.ez
new file mode 100644
index 0000000000..57e93ba7a0
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_01-0.2.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_02-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_02-0.1.0.ez
new file mode 100644
index 0000000000..c9cbef855c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-01/mock_rabbitmq_plugins_02-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-02/mock_rabbitmq_plugins_02-0.2.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-02/mock_rabbitmq_plugins_02-0.2.0.ez
new file mode 100644
index 0000000000..c68a17d33b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-02/mock_rabbitmq_plugins_02-0.2.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-03/mock_rabbitmq_plugins_03-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-03/mock_rabbitmq_plugins_03-0.1.0.ez
new file mode 100644
index 0000000000..448518b9d6
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-03/mock_rabbitmq_plugins_03-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-04/mock_rabbitmq_plugins_04.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-04/mock_rabbitmq_plugins_04.ez
new file mode 100644
index 0000000000..8d48fe534a
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins-subdirectory-04/mock_rabbitmq_plugins_04.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_7-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_7-0.1.0.ez
new file mode 100644
index 0000000000..1596be2d90
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_7-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0.ez
new file mode 100644
index 0000000000..d5d32bd490
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugin_for_3_8.app b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugin_for_3_8.app
new file mode 100644
index 0000000000..dae70550b6
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugin_for_3_8.app
@@ -0,0 +1,10 @@
+{application, mock_rabbitmq_plugin_for_3_8, [
+ {description, "New project"},
+ {vsn, "0.1.0"},
+ {modules, ['mock_rabbitmq_plugins_01_app','mock_rabbitmq_plugins_01_sup']},
+ {registered, [mock_rabbitmq_plugins_01_sup]},
+ {applications, [kernel,stdlib,rabbit]},
+ {mod, {mock_rabbitmq_plugins_01_app, []}},
+ {env, []},
+ {broker_version_requirements, ["3.8.0", "3.9.0"]}
+]}.
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_app.beam b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_app.beam
new file mode 100644
index 0000000000..903e1c3f6e
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_app.beam
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_sup.beam b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_sup.beam
new file mode 100644
index 0000000000..7d6dd6820c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugin_for_3_8-0.1.0/ebin/mock_rabbitmq_plugins_01_sup.beam
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_01-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_01-0.1.0.ez
new file mode 100644
index 0000000000..6eacd2cd1e
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_01-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_02-0.1.0.ez b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_02-0.1.0.ez
new file mode 100644
index 0000000000..1c085e6104
--- /dev/null
+++ b/deps/rabbitmq_cli/test/fixtures/plugins/plugins_with_version_requirements/mock_rabbitmq_plugins_02-0.1.0.ez
Binary files differ
diff --git a/deps/rabbitmq_cli/test/json_formatting.exs b/deps/rabbitmq_cli/test/json_formatting.exs
new file mode 100644
index 0000000000..c0e35e2ad3
--- /dev/null
+++ b/deps/rabbitmq_cli/test/json_formatting.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 JSONFormattingTest do
+ use ExUnit.Case, async: false
+ import ExUnit.CaptureIO
+ import RabbitMQ.CLI.Core.ExitCodes
+ import TestHelper
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ set_scope(:all)
+
+ :ok
+ end
+
+ test "JSON output of status" do
+ set_scope(:ctl)
+
+ node = to_string(get_rabbit_hostname())
+ command = ["status", "-n", node, "--formatter=json"]
+ output = capture_io(:stdio, fn ->
+ error_check(command, exit_ok())
+ end)
+ {:ok, doc} = JSON.decode(output)
+
+ assert Map.has_key?(doc, "memory")
+ assert Map.has_key?(doc, "file_descriptors")
+ assert Map.has_key?(doc, "listeners")
+ assert Map.has_key?(doc, "processes")
+ assert Map.has_key?(doc, "os")
+ assert Map.has_key?(doc, "pid")
+ assert Map.has_key?(doc, "rabbitmq_version")
+
+ assert doc["alarms"] == []
+ end
+
+ test "JSON output of cluster_status" do
+ set_scope(:ctl)
+
+ node = to_string(get_rabbit_hostname())
+ command = ["cluster_status", "-n", node, "--formatter=json"]
+ output = capture_io(:stdio, fn ->
+ error_check(command, exit_ok())
+ end)
+ {:ok, doc} = JSON.decode(output)
+
+ assert Enum.member?(doc["disk_nodes"], node)
+ assert Map.has_key?(doc["listeners"], node)
+ assert Map.has_key?(doc["versions"], node)
+ assert doc["alarms"] == []
+ assert doc["partitions"] == %{}
+ end
+end
diff --git a/deps/rabbitmq_cli/test/plugins/directories_command_test.exs b/deps/rabbitmq_cli/test/plugins/directories_command_test.exs
new file mode 100644
index 0000000000..cae418717a
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/directories_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 DirectoriesCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Plugins.Commands.DirectoriesCommand
+
+ 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])
+ {:ok, plugins_expand_dir} = :rabbit_misc.rpc_call(node,
+ :application, :get_env,
+ [:rabbit, :plugins_expand_dir])
+
+ rabbitmq_home = :rabbit_misc.rpc_call(node, :code, :lib_dir, [:rabbit])
+
+ {:ok, opts: %{
+ plugins_file: plugins_file,
+ plugins_dir: plugins_dir,
+ plugins_expand_dir: plugins_expand_dir,
+ rabbitmq_home: rabbitmq_home,
+ }}
+ end
+
+ setup context do
+ {
+ :ok,
+ opts: Map.merge(context[:opts], %{
+ node: get_rabbit_hostname(),
+ timeout: 1000
+ })
+ }
+ end
+
+ test "validate: providing no arguments passes validation", context do
+ assert @command.validate([], context[:opts]) == :ok
+ end
+
+ test "validate: providing --online passes validation", context do
+ assert @command.validate([], Map.merge(%{online: true}, context[:opts])) == :ok
+ end
+
+ test "validate: providing --offline passes validation", context do
+ assert @command.validate([], Map.merge(%{offline: true}, context[:opts])) == :ok
+ end
+
+ test "validate: providing any arguments fails validation", context do
+ assert @command.validate(["a", "b", "c"], context[:opts]) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ test "validate: setting both --online and --offline to false fails validation", context do
+ assert @command.validate([], Map.merge(context[:opts], %{online: false, offline: false})) ==
+ {:validation_failure, {:bad_argument, "Cannot set online and offline to false"}}
+ end
+
+ test "validate: setting both --online and --offline to true fails validation", context do
+ assert @command.validate([], Map.merge(context[:opts], %{online: true, offline: true})) ==
+ {:validation_failure, {:bad_argument, "Cannot set both online and offline"}}
+ end
+
+ test "validate_execution_environment: when --offline is used, specifying a non-existent enabled_plugins_file passes validation", context do
+ opts = context[:opts] |> Map.merge(%{offline: true, enabled_plugins_file: "none"})
+ assert @command.validate_execution_environment([], opts) == :ok
+ end
+
+ test "validate_execution_environment: when --offline is used, specifying a non-existent plugins_dir fails validation", context do
+ opts = context[:opts] |> Map.merge(%{offline: true, plugins_dir: "none"})
+ assert @command.validate_execution_environment([], opts) == {:validation_failure, :plugins_dir_does_not_exist}
+ end
+
+ test "validate_execution_environment: when --online is used, specifying a non-existent enabled_plugins_file passes validation", context do
+ opts = context[:opts] |> Map.merge(%{online: true, enabled_plugins_file: "none"})
+ assert @command.validate_execution_environment([], opts) == :ok
+ end
+
+ test "validate_execution_environment: when --online is used, specifying a non-existent plugins_dir passes validation", context do
+ opts = context[:opts] |> Map.merge(%{online: true, plugins_dir: "none"})
+ assert @command.validate_execution_environment([], opts) == :ok
+ end
+
+
+ test "run: when --online is used, lists plugin directories", context do
+ opts = Map.merge(context[:opts], %{online: true})
+ dirs = %{plugins_dir: to_string(Map.get(opts, :plugins_dir)),
+ plugins_expand_dir: to_string(Map.get(opts, :plugins_expand_dir)),
+ enabled_plugins_file: to_string(Map.get(opts, :plugins_file))}
+
+ assert @command.run([], opts) == {:ok, dirs}
+ end
+end
diff --git a/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs
new file mode 100644
index 0000000000..dc5d92e090
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/disable_plugins_command_test.exs
@@ -0,0 +1,187 @@
+## 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 DisablePluginsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ alias RabbitMQ.CLI.Core.ExitCodes
+
+ @command RabbitMQ.CLI.Plugins.Commands.DisableCommand
+
+ 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])
+
+ IO.puts("plugins disable tests default env: enabled plugins = #{plugins_file}, plugins dir = #{plugins_dir}, RabbitMQ home directory = #{rabbitmq_home}")
+
+ {:ok, [enabled_plugins]} = :file.consult(plugins_file)
+ IO.puts("plugins disable tests will assume tnat #{Enum.join(enabled_plugins, ",")} is the list of enabled plugins to revert to")
+
+ opts = %{enabled_plugins_file: plugins_file,
+ plugins_dir: plugins_dir,
+ rabbitmq_home: rabbitmq_home,
+ online: false, offline: false,
+ all: false}
+
+ on_exit(fn ->
+ set_enabled_plugins(enabled_plugins, :online, get_rabbit_hostname(), opts)
+ end)
+
+ {:ok, opts: opts}
+ end
+
+ setup context do
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation],
+ :online,
+ get_rabbit_hostname(),
+ context[:opts])
+
+
+ {
+ :ok,
+ opts: Map.merge(context[:opts], %{
+ node: get_rabbit_hostname(),
+ timeout: 1000
+ })
+ }
+ end
+
+ test "validate: specifying both --online and --offline is reported as invalid", context do
+ assert match?(
+ {:validation_failure, {:bad_argument, _}},
+ @command.validate(["a"], Map.merge(context[:opts], %{online: true, offline: true}))
+ )
+ end
+
+ test "validate: not specifying plugins to enable is reported as invalid", context do
+ assert match?(
+ {:validation_failure, :not_enough_args},
+ @command.validate([], Map.merge(context[:opts], %{online: true, offline: false}))
+ )
+ end
+
+ test "validate_execution_environment: specifying a non-existent enabled_plugins_file is fine", context do
+ assert @command.validate_execution_environment(["a"], Map.merge(context[:opts], %{enabled_plugins_file: "none"})) == :ok
+ end
+
+ test "validate_execution_environment: specifying a non-existent plugins_dir is reported as an error", context do
+ assert @command.validate_execution_environment(["a"], Map.merge(context[:opts], %{plugins_dir: "none"})) ==
+ {:validation_failure, :plugins_dir_does_not_exist}
+ end
+
+ test "node is inaccessible, writes out enabled plugins file and returns implicitly enabled plugin list", context do
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{node: :nonode}))
+ assert [[:rabbitmq_federation],
+ %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_federation]}] ==
+ Enum.to_list(test_stream)
+ assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] ==
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "in offline mode, writes out enabled plugins and reports implicitly enabled plugin list", context do
+ assert {:stream, test_stream} = @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[:rabbitmq_federation],
+ %{mode: :offline, disabled: [:rabbitmq_stomp], set: [:rabbitmq_federation]}] == Enum.to_list(test_stream)
+ assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] ==
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "in offline mode , removes implicitly enabled plugins when last explicitly enabled one is removed", context do
+ assert {:stream, test_stream0} =
+ @command.run(["rabbitmq_federation"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[:rabbitmq_stomp],
+ %{mode: :offline, disabled: [:rabbitmq_federation], set: [:rabbitmq_stomp]}] == Enum.to_list(test_stream0)
+ assert {:ok, [[:rabbitmq_stomp]]} == :file.consult(context[:opts][:enabled_plugins_file])
+
+ assert {:stream, test_stream1} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[],
+ %{mode: :offline, disabled: [:rabbitmq_stomp], set: []}] ==
+ Enum.to_list(test_stream1)
+ assert {:ok, [[]]} = :file.consult(context[:opts][:enabled_plugins_file])
+ end
+
+ test "updates plugin list and stops disabled plugins", context do
+ assert {:stream, test_stream0} =
+ @command.run(["rabbitmq_stomp"], context[:opts])
+ assert [[:rabbitmq_federation],
+ %{mode: :online,
+ started: [], stopped: [:rabbitmq_stomp],
+ disabled: [:rabbitmq_stomp],
+ set: [:rabbitmq_federation]}] ==
+ Enum.to_list(test_stream0)
+ assert {:ok, [[:rabbitmq_federation]]} == :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation] ==
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+
+ assert {:stream, test_stream1} =
+ @command.run(["rabbitmq_federation"], context[:opts])
+ assert [[],
+ %{mode: :online,
+ started: [], stopped: [:rabbitmq_federation],
+ disabled: [:rabbitmq_federation],
+ set: []}] ==
+ Enum.to_list(test_stream1)
+ assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file])
+ assert Enum.empty?(Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])))
+ end
+
+ test "can disable multiple plugins at once", context do
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp", "rabbitmq_federation"], context[:opts])
+ assert [[],
+ %{mode: :online,
+ started: [], stopped: [:rabbitmq_federation, :rabbitmq_stomp],
+ disabled: [:rabbitmq_federation, :rabbitmq_stomp],
+ set: []}] ==
+ Enum.to_list(test_stream)
+ assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file])
+ assert Enum.empty?(Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])))
+ end
+
+ test "disabling a dependency disables all plugins that depend on it", context do
+ assert {:stream, test_stream} = @command.run(["amqp_client"], context[:opts])
+ assert [[],
+ %{mode: :online,
+ started: [], stopped: [:rabbitmq_federation, :rabbitmq_stomp],
+ disabled: [:rabbitmq_federation, :rabbitmq_stomp],
+ set: []}] ==
+ Enum.to_list(test_stream)
+ assert {:ok, [[]]} == :file.consult(context[:opts][:enabled_plugins_file])
+ assert Enum.empty?(Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, [])))
+ end
+
+ test "formats enabled plugins mismatch errors", context do
+ err = {:enabled_plugins_mismatch, '/tmp/a/cli/path', '/tmp/a/server/path'}
+ assert {:error, ExitCodes.exit_dataerr(),
+ "Could not update enabled plugins file at /tmp/a/cli/path: target node #{context[:opts][:node]} uses a different path (/tmp/a/server/path)"}
+ == @command.output({:error, err}, context[:opts])
+ end
+
+ test "formats enabled plugins write errors", context do
+ err1 = {:cannot_write_enabled_plugins_file, "/tmp/a/path", :eacces}
+ assert {:error, ExitCodes.exit_dataerr(),
+ "Could not update enabled plugins file at /tmp/a/path: the file does not exist or permission was denied (EACCES)"} ==
+ @command.output({:error, err1}, context[:opts])
+
+ err2 = {:cannot_write_enabled_plugins_file, "/tmp/a/path", :enoent}
+ assert {:error, ExitCodes.exit_dataerr(),
+ "Could not update enabled plugins file at /tmp/a/path: the file does not exist (ENOENT)"} ==
+ @command.output({:error, err2}, context[:opts])
+ end
+end
diff --git a/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs
new file mode 100644
index 0000000000..09aaf38351
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/enable_plugins_command_test.exs
@@ -0,0 +1,243 @@
+## 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 EnablePluginsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ alias RabbitMQ.CLI.Core.ExitCodes
+
+ @command RabbitMQ.CLI.Plugins.Commands.EnableCommand
+
+ 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])
+
+ IO.puts("plugins enable tests default env: enabled plugins = #{plugins_file}, plugins dir = #{plugins_dir}, RabbitMQ home directory = #{rabbitmq_home}")
+
+ {:ok, [enabled_plugins]} = :file.consult(plugins_file)
+ IO.puts("plugins enable tests will assume tnat #{Enum.join(enabled_plugins, ",")} is the list of enabled plugins to revert to")
+
+ opts = %{enabled_plugins_file: plugins_file,
+ plugins_dir: plugins_dir,
+ rabbitmq_home: rabbitmq_home,
+ online: false, offline: false,
+ all: false}
+
+ on_exit(fn ->
+ set_enabled_plugins(enabled_plugins, :online, get_rabbit_hostname(), opts)
+ end)
+
+
+ {:ok, opts: opts}
+ end
+
+ setup context do
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation],
+ :online,
+ get_rabbit_hostname(),
+ context[:opts])
+
+ {
+ :ok,
+ opts: Map.merge(context[:opts], %{
+ node: get_rabbit_hostname(),
+ timeout: 1000
+ })
+ }
+ end
+
+ test "validate: specifying both --online and --offline is reported as invalid", context do
+ assert match?(
+ {:validation_failure, {:bad_argument, _}},
+ @command.validate(["a"], Map.merge(context[:opts], %{online: true, offline: true}))
+ )
+ end
+
+ test "validate: not specifying any plugins to enable is reported as invalid", context do
+ assert match?(
+ {:validation_failure, :not_enough_args},
+ @command.validate([], Map.merge(context[:opts], %{online: true, offline: false}))
+ )
+ end
+
+ test "validate_execution_environment: specifying a non-existent enabled_plugins_file is fine", context do
+ assert @command.validate_execution_environment(["a"], Map.merge(context[:opts], %{enabled_plugins_file: "none"})) == :ok
+ end
+
+ test "validate_execution_environment: specifying a non-existent plugins_dir is reported as an error", context do
+ assert @command.validate_execution_environment(["a"], Map.merge(context[:opts], %{plugins_dir: "none"})) ==
+ {:validation_failure, :plugins_dir_does_not_exist}
+ end
+
+ test "if node is inaccessible, writes enabled plugins file and reports implicitly enabled plugin list", context do
+ # Clears enabled plugins file
+ set_enabled_plugins([], :offline, :nonode, context[:opts])
+
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{node: :nonode}))
+ assert [[:rabbitmq_stomp],
+ %{mode: :offline, enabled: [:rabbitmq_stomp], set: [:rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream)
+ check_plugins_enabled([:rabbitmq_stomp], context)
+ assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] ==
+ currently_active_plugins(context)
+ end
+
+ test "in offline mode, writes enabled plugins and reports implicitly enabled plugin list", context do
+ # Clears enabled plugins file
+ set_enabled_plugins([], :offline, :nonode, context[:opts])
+
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[:rabbitmq_stomp],
+ %{mode: :offline, enabled: [:rabbitmq_stomp], set: [:rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream)
+ check_plugins_enabled([:rabbitmq_stomp], context)
+ assert_equal_sets(
+ [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp],
+ currently_active_plugins(context))
+ end
+
+ test "adds additional plugins to those already enabled", context do
+ # Clears enabled plugins file
+ set_enabled_plugins([], :offline, :nonode, context[:opts])
+
+ assert {:stream, test_stream0} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[:rabbitmq_stomp],
+ %{mode: :offline, enabled: [:rabbitmq_stomp], set: [:rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream0)
+ check_plugins_enabled([:rabbitmq_stomp], context)
+ assert {:stream, test_stream1} =
+ @command.run(["rabbitmq_federation"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[:rabbitmq_federation, :rabbitmq_stomp],
+ %{mode: :offline, enabled: [:rabbitmq_federation],
+ set: [:rabbitmq_federation, :rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream1)
+ check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation], context)
+ end
+
+ test "updates plugin list and starts newly enabled plugins", context do
+ # Clears enabled plugins file and stop all plugins
+ set_enabled_plugins([], :online, context[:opts][:node], context[:opts])
+
+ assert {:stream, test_stream0} =
+ @command.run(["rabbitmq_stomp"], context[:opts])
+ assert [[:rabbitmq_stomp],
+ %{mode: :online,
+ started: [:rabbitmq_stomp], stopped: [],
+ enabled: [:rabbitmq_stomp],
+ set: [:rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream0)
+
+ check_plugins_enabled([:rabbitmq_stomp], context)
+ assert_equal_sets([:amqp_client, :rabbitmq_stomp], currently_active_plugins(context))
+
+ {:stream, test_stream1} =
+ @command.run(["rabbitmq_federation"], context[:opts])
+ assert [[:rabbitmq_federation, :rabbitmq_stomp],
+ %{mode: :online,
+ started: [:rabbitmq_federation], stopped: [],
+ enabled: [:rabbitmq_federation],
+ set: [:rabbitmq_federation, :rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream1)
+
+ check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation], context)
+ assert_equal_sets([:amqp_client, :rabbitmq_federation, :rabbitmq_stomp], currently_active_plugins(context))
+ end
+
+ test "can enable multiple plugins at once", context do
+ # Clears plugins file and stop all plugins
+ set_enabled_plugins([], :online, context[:opts][:node], context[:opts])
+
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp", "rabbitmq_federation"], context[:opts])
+ assert [[:rabbitmq_federation, :rabbitmq_stomp],
+ %{mode: :online,
+ started: [:rabbitmq_federation, :rabbitmq_stomp], stopped: [],
+ enabled: [:rabbitmq_federation, :rabbitmq_stomp],
+ set: [:rabbitmq_federation, :rabbitmq_stomp]}] ==
+ Enum.to_list(test_stream)
+ check_plugins_enabled([:rabbitmq_stomp, :rabbitmq_federation], context)
+
+ assert_equal_sets([:amqp_client, :rabbitmq_federation, :rabbitmq_stomp], currently_active_plugins(context))
+ end
+
+ test "does not enable an already implicitly enabled plugin", context do
+ # Clears enabled plugins file and stop all plugins
+ set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ assert {:stream, test_stream} =
+ @command.run(["amqp_client"], context[:opts])
+ assert [[:rabbitmq_federation],
+ %{mode: :online,
+ started: [], stopped: [],
+ enabled: [],
+ set: [:rabbitmq_federation]}] ==
+ Enum.to_list(test_stream)
+ check_plugins_enabled([:rabbitmq_federation], context)
+ assert [:amqp_client, :rabbitmq_federation] ==
+ currently_active_plugins(context)
+
+ end
+
+ test "run: does not enable plugins with unmet version requirements", context do
+ set_enabled_plugins([], :online, context[:opts][:node], context[:opts])
+
+ plugins_directory = fixture_plugins_path("plugins_with_version_requirements")
+ opts = get_opts_with_plugins_directories(context, [plugins_directory])
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+
+ {:stream, _} =
+ @command.run(["mock_rabbitmq_plugin_for_3_8"], opts)
+ check_plugins_enabled([:mock_rabbitmq_plugin_for_3_8], context)
+
+ # Not changed
+ {:error, _version_error} = @command.run(["mock_rabbitmq_plugin_for_3_7"], opts)
+ check_plugins_enabled([:mock_rabbitmq_plugin_for_3_8], context)
+
+ end
+
+ test "run: does not enable plugins with unmet version requirements even when enabling all plugins", context do
+ set_enabled_plugins([], :online, context[:opts][:node], context[:opts])
+
+ plugins_directory = fixture_plugins_path("plugins_with_version_requirements")
+ opts = get_opts_with_plugins_directories(context, [plugins_directory])
+ opts = Map.merge(opts, %{all: true})
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+
+ {:error, _version_error} = @command.run([], opts)
+
+ check_plugins_enabled([], context)
+ end
+
+ test "formats enabled plugins mismatch errors", context do
+ err = {:enabled_plugins_mismatch, '/tmp/a/cli/path', '/tmp/a/server/path'}
+ assert {:error, ExitCodes.exit_dataerr(),
+ "Could not update enabled plugins file at /tmp/a/cli/path: target node #{context[:opts][:node]} uses a different path (/tmp/a/server/path)"}
+ == @command.output({:error, err}, context[:opts])
+ end
+
+ test "formats enabled plugins write errors", context do
+ err1 = {:cannot_write_enabled_plugins_file, "/tmp/a/path", :eacces}
+ assert {:error, ExitCodes.exit_dataerr(),
+ "Could not update enabled plugins file at /tmp/a/path: the file does not exist or permission was denied (EACCES)"} ==
+ @command.output({:error, err1}, context[:opts])
+
+ err2 = {:cannot_write_enabled_plugins_file, "/tmp/a/path", :enoent}
+ assert {:error, ExitCodes.exit_dataerr(),
+ "Could not update enabled plugins file at /tmp/a/path: the file does not exist (ENOENT)"} ==
+ @command.output({:error, err2}, context[:opts])
+ end
+end
diff --git a/deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs b/deps/rabbitmq_cli/test/plugins/is_enabled_command_test.exs
new file mode 100644
index 0000000000..af2900228b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/is_enabled_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 PluginIsEnabledCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Plugins.Commands.IsEnabledCommand
+
+ 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,
+ online: false, offline: false}
+
+ on_exit(fn ->
+ set_enabled_plugins(enabled_plugins, :online, get_rabbit_hostname(), opts)
+ end)
+
+
+ {:ok, opts: opts}
+ end
+
+ setup context do
+ {
+ :ok,
+ opts: Map.merge(context[:opts], %{
+ node: get_rabbit_hostname(),
+ timeout: 1000
+ })
+ }
+ end
+
+
+
+ test "validate: specifying both --online and --offline is reported as invalid", context do
+ assert match?(
+ {:validation_failure, {:bad_argument, _}},
+ @command.validate(["rabbitmq_stomp"], Map.merge(context[:opts], %{online: true, offline: true}))
+ )
+ end
+
+ test "validate: not specifying any plugins to check is reported as invalid", context do
+ opts = context[:opts] |> Map.merge(%{online: true, offline: false})
+ assert match?({:validation_failure, :not_enough_args}, @command.validate([], opts))
+ end
+
+ test "validate_execution_environment: specifying a non-existent enabled_plugins_file is fine", context do
+ assert @command.validate_execution_environment(["rabbitmq_stomp"],
+ Map.merge(context[:opts], %{online: false,
+ offline: true,
+ enabled_plugins_file: "none"})) == :ok
+ end
+
+ test "validate_execution_environment: specifying a non-existent plugins_dir is reported as an error", context do
+ opts = context[:opts] |> Map.merge(%{online: false,
+ offline: true,
+ plugins_dir: "none"})
+
+ assert @command.validate_execution_environment(["rabbitmq_stomp"], opts) ==
+ {:validation_failure, :plugins_dir_does_not_exist}
+ end
+
+ test "run: when given a single enabled plugin, reports it as such", context do
+ opts = context[:opts] |> Map.merge(%{online: true, offline: false})
+ assert match?({:ok, _},
+ assert @command.run(["rabbitmq_stomp"], opts))
+ end
+
+ test "run: when given a list of actually enabled plugins, reports them as such", context do
+ opts = context[:opts] |> Map.merge(%{online: true, offline: false})
+ assert match?({:ok, _},
+ assert @command.run(["rabbitmq_stomp", "rabbitmq_federation"], opts))
+ end
+
+ test "run: when given a list of non-existent plugins, reports them as not enabled", context do
+ opts = context[:opts] |> Map.merge(%{online: true, offline: false})
+ assert match?({:error, _},
+ assert @command.run(["rabbitmq_xyz", "abc_xyz"], opts))
+ end
+
+ test "run: when given a list with one non-existent plugin, reports the group as not [all] enabled", context do
+ opts = context[:opts] |> Map.merge(%{online: true, offline: false})
+ assert match?({:error, _},
+ assert @command.run(["rabbitmq_stomp", "abc_xyz"], opts))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs
new file mode 100644
index 0000000000..33d9420435
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/list_plugins_command_test.exs
@@ -0,0 +1,235 @@
+## 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 ListPluginsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Plugins.Commands.ListCommand
+
+ def reset_enabled_plugins_to_preconfigured_defaults(context) do
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation],
+ :online,
+ get_rabbit_hostname(), context[:opts])
+ end
+
+ 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])
+ IO.puts("plugins list tests default env: enabled plugins = #{plugins_file}, plugins dir = #{plugins_dir}, RabbitMQ home directory = #{rabbitmq_home}")
+
+ {:ok, [enabled_plugins]} = :file.consult(plugins_file)
+ IO.puts("plugins list tests will assume tnat #{Enum.join(enabled_plugins, ",")} is the list of enabled plugins to revert to")
+
+ opts = %{enabled_plugins_file: plugins_file,
+ plugins_dir: plugins_dir,
+ rabbitmq_home: rabbitmq_home,
+ minimal: false, verbose: false,
+ enabled: false, implicitly_enabled: false}
+
+ on_exit(fn ->
+ set_enabled_plugins(enabled_plugins, :online, get_rabbit_hostname(), opts)
+ end)
+
+
+ {:ok, opts: opts}
+ end
+
+ setup context do
+ reset_enabled_plugins_to_preconfigured_defaults(context)
+
+ {
+ :ok,
+ opts: Map.merge(context[:opts], %{
+ node: get_rabbit_hostname(),
+ timeout: 1000
+ })
+ }
+ end
+
+ test "validate: specifying both --minimal and --verbose is reported as invalid", context do
+ assert match?(
+ {:validation_failure, {:bad_argument, _}},
+ @command.validate([], Map.merge(context[:opts], %{minimal: true, verbose: true}))
+ )
+ end
+
+ test "validate: specifying multiple patterns 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: specifying a non-existent enabled_plugins_file is fine", context do
+ assert @command.validate_execution_environment(["a"], Map.merge(context[:opts], %{enabled_plugins_file: "none"})) == :ok
+ end
+
+ test "validate_execution_environment: specifying non existent plugins_dir is reported as an error", context do
+ assert @command.validate_execution_environment(["a"], Map.merge(context[:opts], %{plugins_dir: "none"})) ==
+ {:validation_failure, :plugins_dir_does_not_exist}
+ end
+
+ test "will report list of plugins from file for stopped node", context do
+ node = context[:opts][:node]
+ :ok = :rabbit_misc.rpc_call(node, :application, :stop, [:rabbitmq_stomp])
+ on_exit(fn ->
+ :rabbit_misc.rpc_call(node, :application, :start, [:rabbitmq_stomp])
+ end)
+ assert %{status: :node_down,
+ plugins: [%{name: :rabbitmq_federation, enabled: :enabled, running: false},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: false}]} =
+ @command.run([".*"], Map.merge(context[:opts], %{node: :nonode}))
+ end
+
+ test "will report list of started plugins for started node", context do
+ node = context[:opts][:node]
+ :ok = :rabbit_misc.rpc_call(node, :application, :stop, [:rabbitmq_stomp])
+ on_exit(fn ->
+ :rabbit_misc.rpc_call(node, :application, :start, [:rabbitmq_stomp])
+ end)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation, enabled: :enabled, running: true},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: false}]} =
+ @command.run([".*"], context[:opts])
+ end
+
+ test "will report description and dependencies for verbose mode", context do
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation, enabled: :enabled, running: true, description: _, dependencies: [:amqp_client]},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: true, description: _, dependencies: [:amqp_client]}]} =
+ @command.run([".*"], Map.merge(context[:opts], %{verbose: true}))
+ end
+
+ test "will report plugin names in minimal mode", context do
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation}, %{name: :rabbitmq_stomp}]} =
+ @command.run([".*"], Map.merge(context[:opts], %{minimal: true}))
+ end
+
+ test "by default lists all plugins", context do
+ set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ on_exit(fn ->
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ end)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation, enabled: :enabled, running: true},
+ %{name: :rabbitmq_stomp, enabled: :not_enabled, running: false}]} =
+ @command.run([".*"], context[:opts])
+ end
+
+ test "with enabled flag lists only explicitly enabled plugins", context do
+ set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ on_exit(fn ->
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ end)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation, enabled: :enabled, running: true}]} =
+ @command.run([".*"], Map.merge(context[:opts], %{enabled: true}))
+ end
+
+ test "with implicitly_enabled flag lists explicitly and implicitly enabled plugins", context do
+ set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ on_exit(fn ->
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ end)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation, enabled: :enabled, running: true}]} =
+ @command.run([".*"], Map.merge(context[:opts], %{implicitly_enabled: true}))
+ end
+
+ test "will filter plugins by name with pattern provided", context do
+ set_enabled_plugins([:rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ on_exit(fn ->
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation], :online, context[:opts][:node], context[:opts])
+ end)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation}]} =
+ @command.run(["fede"], Map.merge(context[:opts], %{minimal: true}))
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_stomp}]} =
+ @command.run(["stomp$"], Map.merge(context[:opts], %{minimal: true}))
+ end
+
+ test "validate: validation is OK when we use multiple plugins directories, one of them does not exist", context do
+ opts = get_opts_with_non_existing_plugins_directory(context)
+ assert @command.validate([], opts) == :ok
+ end
+
+ test "validate: validation is OK when we use multiple plugins directories, directories do exist", context do
+ opts = get_opts_with_existing_plugins_directory(context)
+ assert @command.validate([], opts) == :ok
+ end
+
+ test "should succeed when using multiple plugins directories, one of them does not exist", context do
+ opts = get_opts_with_non_existing_plugins_directory(context)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation}, %{name: :rabbitmq_stomp}]} =
+ @command.run([".*"], Map.merge(opts, %{minimal: true}))
+ end
+
+
+ test "should succeed when using multiple plugins directories, directories do exist and do contain plugins", context do
+ opts = get_opts_with_existing_plugins_directory(context)
+ assert %{status: :running,
+ plugins: [%{name: :rabbitmq_federation}, %{name: :rabbitmq_stomp}]} =
+ @command.run([".*"], Map.merge(opts, %{minimal: true}))
+ end
+
+ test "should list plugins when using multiple plugins directories", context do
+ plugins_directory = fixture_plugins_path("plugins-subdirectory-01")
+ opts = get_opts_with_plugins_directories(context, [plugins_directory])
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+ reset_enabled_plugins_to_preconfigured_defaults(context)
+ assert %{status: :running,
+ plugins: [%{name: :mock_rabbitmq_plugins_01}, %{name: :mock_rabbitmq_plugins_02},
+ %{name: :rabbitmq_federation}, %{name: :rabbitmq_stomp}]} =
+ @command.run([".*"], Map.merge(opts, %{minimal: true}))
+ end
+
+ test "will report list of plugins with latest version picked", context do
+ plugins_directory_01 = fixture_plugins_path("plugins-subdirectory-01")
+ plugins_directory_02 = fixture_plugins_path("plugins-subdirectory-02")
+ opts = get_opts_with_plugins_directories(context, [plugins_directory_01, plugins_directory_02])
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+ reset_enabled_plugins_to_preconfigured_defaults(context)
+ assert %{status: :running,
+ plugins: [%{name: :mock_rabbitmq_plugins_01, enabled: :not_enabled, running: false, version: '0.2.0'},
+ %{name: :mock_rabbitmq_plugins_02, enabled: :not_enabled, running: false, version: '0.2.0'},
+ %{name: :rabbitmq_federation, enabled: :enabled, running: true},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: true}]} =
+ @command.run([".*"], opts)
+ end
+
+ test "will report both running and pending upgrade versions", context do
+ plugins_directory_01 = fixture_plugins_path("plugins-subdirectory-01")
+ plugins_directory_02 = fixture_plugins_path("plugins-subdirectory-02")
+ opts = get_opts_with_plugins_directories(context, [plugins_directory_01])
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+ set_enabled_plugins([:mock_rabbitmq_plugins_02, :rabbitmq_federation, :rabbitmq_stomp],
+ :online, get_rabbit_hostname(), opts)
+ assert %{status: :running,
+ plugins: [%{name: :mock_rabbitmq_plugins_01, enabled: :not_enabled, running: false, version: '0.2.0'},
+ %{name: :mock_rabbitmq_plugins_02, enabled: :enabled, running: true, version: '0.1.0', running_version: '0.1.0'},
+ %{name: :rabbitmq_federation, enabled: :enabled, running: true},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: true}]} =
+ @command.run([".*"], opts)
+ opts = get_opts_with_plugins_directories(context, [plugins_directory_01, plugins_directory_02])
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+ assert %{status: :running,
+ plugins: [%{name: :mock_rabbitmq_plugins_01, enabled: :not_enabled, running: false, version: '0.2.0'},
+ %{name: :mock_rabbitmq_plugins_02, enabled: :enabled, running: true, version: '0.2.0', running_version: '0.1.0'},
+ %{name: :rabbitmq_federation, enabled: :enabled, running: true},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: true}]} =
+ @command.run([".*"], opts)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs b/deps/rabbitmq_cli/test/plugins/plugins_formatter_test.exs
new file mode 100644
index 0000000000..9bf185d7e0
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/plugins_formatter_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 PluginsFormatterTest do
+ use ExUnit.Case, async: false
+
+ @formatter RabbitMQ.CLI.Formatters.Plugins
+
+ test "format_output with --silent and --minimal" do
+ result = @formatter.format_output(
+ %{status: :running,
+ plugins: [%{name: :amqp_client, enabled: :implicit, running: true, version: '3.7.0', running_version: nil},
+ %{name: :mock_rabbitmq_plugins_01, enabled: :not_enabled, running: false, version: '0.2.0', running_version: nil},
+ %{name: :mock_rabbitmq_plugins_02, enabled: :enabled, running: true, version: '0.2.0', running_version: '0.1.0'},
+ %{name: :rabbitmq_federation, enabled: :enabled, running: true, version: '3.7.0', running_version: nil},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: true, version: '3.7.0', running_version: nil}],
+ format: :minimal}, %{node: "rabbit@localhost", silent: true})
+ assert result == ["amqp_client",
+ "mock_rabbitmq_plugins_01",
+ "mock_rabbitmq_plugins_02",
+ "rabbitmq_federation",
+ "rabbitmq_stomp"]
+ end
+
+ test "format_output pending upgrade version message" do
+ result = @formatter.format_output(
+ %{status: :running,
+ plugins: [%{name: :amqp_client, enabled: :implicit, running: true, version: '3.7.0', running_version: nil},
+ %{name: :mock_rabbitmq_plugins_01, enabled: :not_enabled, running: false, version: '0.2.0', running_version: nil},
+ %{name: :mock_rabbitmq_plugins_02, enabled: :enabled, running: true, version: '0.2.0', running_version: '0.1.0'},
+ %{name: :rabbitmq_federation, enabled: :enabled, running: true, version: '3.7.0', running_version: nil},
+ %{name: :rabbitmq_stomp, enabled: :enabled, running: true, version: '3.7.0', running_version: nil}],
+ format: :normal}, %{node: "rabbit@localhost"})
+ assert result == [" Configured: E = explicitly enabled; e = implicitly enabled",
+ " | Status: * = running on rabbit@localhost", " |/",
+ "[e*] amqp_client 3.7.0", "[ ] mock_rabbitmq_plugins_01 0.2.0",
+ "[E*] mock_rabbitmq_plugins_02 0.1.0 (pending upgrade to 0.2.0)",
+ "[E*] rabbitmq_federation 3.7.0", "[E*] rabbitmq_stomp 3.7.0"]
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs b/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs
new file mode 100644
index 0000000000..3ebc3dfc98
--- /dev/null
+++ b/deps/rabbitmq_cli/test/plugins/set_plugins_command_test.exs
@@ -0,0 +1,157 @@
+## 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 SetPluginsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Plugins.Commands.SetCommand
+
+
+ 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,
+ online: false, offline: false}
+
+ on_exit(fn ->
+ set_enabled_plugins(enabled_plugins, :online, get_rabbit_hostname(),opts)
+ end)
+
+ {:ok, opts: opts}
+ end
+
+ setup context do
+
+ set_enabled_plugins([:rabbitmq_stomp, :rabbitmq_federation],
+ :online,
+ get_rabbit_hostname(),
+ context[:opts])
+
+ {
+ :ok,
+ opts: Map.merge(context[:opts], %{
+ node: get_rabbit_hostname(),
+ timeout: 1000
+ })
+ }
+ end
+
+ test "validate: specifying both --online and --offline is reported as invalid", context do
+ assert match?(
+ {:validation_failure, {:bad_argument, _}},
+ @command.validate([], Map.merge(context[:opts], %{online: true, offline: true}))
+ )
+ end
+
+ test "validate_execution_environment: specifying a non-existent enabled_plugins_file is fine", context do
+ assert @command.validate_execution_environment([], Map.merge(context[:opts], %{enabled_plugins_file: "none"})) ==
+ :ok
+ end
+
+ test "validate_execution_environment: specifying non existent plugins_dir is reported as an error", context do
+ assert @command.validate_execution_environment([], Map.merge(context[:opts], %{plugins_dir: "none"})) ==
+ {:validation_failure, :plugins_dir_does_not_exist}
+ end
+
+ test "will write enabled plugins file if node is inaccessible and report implicitly enabled list", context do
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{node: :nonode}))
+ assert [[:rabbitmq_stomp],
+ %{mode: :offline, set: [:rabbitmq_stomp]}] =
+ Enum.to_list(test_stream)
+ assert {:ok, [[:rabbitmq_stomp]]} = :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] =
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "will write enabled plugins in offline mode and report implicitly enabled list", context do
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_stomp"], Map.merge(context[:opts], %{offline: true, online: false}))
+ assert [[:rabbitmq_stomp],
+ %{mode: :offline, set: [:rabbitmq_stomp]}] =
+ Enum.to_list(test_stream)
+ assert {:ok, [[:rabbitmq_stomp]]} = :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] =
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "will update list of plugins and start/stop enabled/disabled plugins", context do
+ assert {:stream, test_stream0} = @command.run(["rabbitmq_stomp"], context[:opts])
+ assert [[:rabbitmq_stomp],
+ %{mode: :online,
+ started: [], stopped: [:rabbitmq_federation],
+ set: [:rabbitmq_stomp]}] =
+ Enum.to_list(test_stream0)
+ assert {:ok, [[:rabbitmq_stomp]]} = :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_stomp] =
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ assert {:stream, test_stream1} = @command.run(["rabbitmq_federation"], context[:opts])
+ assert [[:rabbitmq_federation],
+ %{mode: :online,
+ started: [:rabbitmq_federation], stopped: [:rabbitmq_stomp],
+ set: [:rabbitmq_federation]}] =
+ Enum.to_list(test_stream1)
+ assert {:ok, [[:rabbitmq_federation]]} = :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation] =
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "can disable all plugins", context do
+ assert {:stream, test_stream} = @command.run([], context[:opts])
+ assert [[],
+ %{mode: :online,
+ started: [], stopped: [:rabbitmq_federation, :rabbitmq_stomp],
+ set: []}] =
+ Enum.to_list(test_stream)
+ assert {:ok, [[]]} = :file.consult(context[:opts][:enabled_plugins_file])
+ assert [] = Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "can set multiple plugins", context do
+ set_enabled_plugins([], :online, get_rabbit_hostname(), context[:opts])
+ assert {:stream, test_stream} =
+ @command.run(["rabbitmq_federation", "rabbitmq_stomp"], context[:opts])
+ assert [[:rabbitmq_federation, :rabbitmq_stomp],
+ %{mode: :online,
+ started: [:rabbitmq_federation, :rabbitmq_stomp],
+ stopped: [],
+ set: [:rabbitmq_federation, :rabbitmq_stomp]}] =
+ Enum.to_list(test_stream)
+ assert {:ok, [[:rabbitmq_federation, :rabbitmq_stomp]]} =
+ :file.consult(context[:opts][:enabled_plugins_file])
+ assert [:amqp_client, :rabbitmq_federation, :rabbitmq_stomp] =
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ test "run: does not enable plugins with unmet version requirements", context do
+ set_enabled_plugins([], :online, context[:opts][:node], context[:opts])
+
+ plugins_directory = fixture_plugins_path("plugins_with_version_requirements")
+ opts = get_opts_with_plugins_directories(context, [plugins_directory])
+ switch_plugins_directories(context[:opts][:plugins_dir], opts[:plugins_dir])
+
+
+ {:stream, _} = @command.run(["mock_rabbitmq_plugin_for_3_8"], opts)
+ check_plugins_enabled([:mock_rabbitmq_plugin_for_3_8], context)
+
+ {:error, _version_error} = @command.run(["mock_rabbitmq_plugin_for_3_7"], opts)
+ check_plugins_enabled([:mock_rabbitmq_plugin_for_3_8], context)
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/add_member_command_test.exs b/deps/rabbitmq_cli/test/queues/add_member_command_test.exs
new file mode 100644
index 0000000000..71705ccb2c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/add_member_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 RabbitMQ.CLI.Queues.Commands.AddMemberCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.AddMemberCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when three or more arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "rabbit@new-node", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "rabbit@new-node", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats two positional arguments and default switches as a success" do
+ assert @command.validate(["quorum-queue-a", "rabbit@new-node"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a", "rabbit@new-node"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/check_if_node_is_mirror_sync_critical_command_test.exs b/deps/rabbitmq_cli/test/queues/check_if_node_is_mirror_sync_critical_command_test.exs
new file mode 100644
index 0000000000..cbab5a6470
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/check_if_node_is_mirror_sync_critical_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 RabbitMQ.CLI.Queues.Commands.CheckIfNodeIsMirrorSyncCriticalCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.CheckIfNodeIsMirrorSyncCriticalCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ test "validate: any positional arguments fail validation" do
+ assert @command.validate(["quorum-queue-a"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "two"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "two", "three"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run([], %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/check_if_node_is_quorum_critical_command_test.exs b/deps/rabbitmq_cli/test/queues/check_if_node_is_quorum_critical_command_test.exs
new file mode 100644
index 0000000000..3a1b8abf34
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/check_if_node_is_quorum_critical_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 RabbitMQ.CLI.Queues.Commands.CheckIfNodeIsQuorumCriticalCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.CheckIfNodeIsQuorumCriticalCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ test "validate: any positional arguments fail validation" do
+ assert @command.validate(["quorum-queue-a"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "two"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "two", "three"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run([], %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/delete_member_command_test.exs b/deps/rabbitmq_cli/test/queues/delete_member_command_test.exs
new file mode 100644
index 0000000000..6880de6399
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/delete_member_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 RabbitMQ.CLI.Queues.Commands.DeleteMemberCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.DeleteMemberCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when three or more arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "rabbit@new-node", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "rabbit@new-node", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats two positional arguments and default switches as a success" do
+ assert @command.validate(["quorum-queue-a", "rabbit@new-node"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a", "rabbit@new-node"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/grow_command_test.exs b/deps/rabbitmq_cli/test/queues/grow_command_test.exs
new file mode 100644
index 0000000000..d0d459065b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/grow_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 RabbitMQ.CLI.Queues.Commands.GrowCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.GrowCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ vhost_pattern: ".*",
+ queue_pattern: ".*",
+ errors_only: false
+ }}
+ end
+
+ test "merge_defaults: defaults to reporting complete results" do
+ assert @command.merge_defaults([], %{}) ==
+ {[], %{vhost_pattern: ".*",
+ queue_pattern: ".*",
+ errors_only: false}}
+ end
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when a node and even are provided, returns a success" do
+ assert @command.validate(["quorum-queue-a", "even"], %{}) == :ok
+ end
+
+ test "validate: when a node and all are provided, returns a success" do
+ assert @command.validate(["quorum-queue-a", "all"], %{}) == :ok
+ end
+
+ test "validate: when a node and something else is provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "banana"], %{}) ==
+ {:validation_failure, "strategy 'banana' is not recognised."}
+ end
+
+ test "validate: when three arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "extra-arg", "another-extra-arg"], %{}) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a", "all"],
+ Map.merge(context[:opts], %{node: :jake@thedog, timeout: 200})))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/peek_command_test.exs b/deps/rabbitmq_cli/test/queues/peek_command_test.exs
new file mode 100644
index 0000000000..567de4f4d2
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/peek_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 RabbitMQ.CLI.Queues.Commands.PeekCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.PeekCommand
+ @invalid_position {:validation_failure, "position value must be a positive integer"}
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: treats no arguments as a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: treats a single positional argument as a failure" do
+ assert @command.validate(["quorum-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when two or more arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "1"], %{}) == :ok
+ assert @command.validate(["quorum-queue-a", "extra-arg", "another-extra-arg"], %{}) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ test "validate: when position is a negative number, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "-1"], %{}) == @invalid_position
+ end
+
+ test "validate: when position is zero, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "0"], %{}) == @invalid_position
+ end
+
+ test "validate: when position cannot be parsed to an integer, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "third"], %{}) == @invalid_position
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a", "1"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/quorum_status_command_test.exs b/deps/rabbitmq_cli/test/queues/quorum_status_command_test.exs
new file mode 100644
index 0000000000..ec694db9ba
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/quorum_status_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 RabbitMQ.CLI.Queues.Commands.QuorumStatusCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.QuorumStatusCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: treats no arguments as a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: accepts a single positional argument" do
+ assert @command.validate(["quorum-queue-a"], %{}) == :ok
+ end
+
+ test "validate: when two or more arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/reclaim_quorum_memory_command_test.exs b/deps/rabbitmq_cli/test/queues/reclaim_quorum_memory_command_test.exs
new file mode 100644
index 0000000000..bec7bab50d
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/reclaim_quorum_memory_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 RabbitMQ.CLI.Queues.Commands.ReclaimQuorumMemoryCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.ReclaimQuorumMemoryCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: treats no arguments as a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: accepts a single positional argument" do
+ assert @command.validate(["quorum-queue-a"], %{}) == :ok
+ end
+
+ test "validate: when two or more arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/queues/shrink_command_test.exs b/deps/rabbitmq_cli/test/queues/shrink_command_test.exs
new file mode 100644
index 0000000000..8441deaeb0
--- /dev/null
+++ b/deps/rabbitmq_cli/test/queues/shrink_command_test.exs
@@ -0,0 +1,55 @@
+## 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 RabbitMQ.CLI.Queues.Commands.ShrinkCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Queues.Commands.ShrinkCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000,
+ errors_only: false
+ }}
+ end
+
+ test "merge_defaults: defaults to reporting complete results" do
+ assert @command.merge_defaults([], %{}) == {[], %{errors_only: false}}
+ end
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a success" do
+ assert @command.validate(["quorum-queue-a"], %{}) == :ok
+ end
+
+ test "validate: when three or more arguments are provided, returns a failure" do
+ assert @command.validate(["quorum-queue-a", "one-extra-arg"], %{}) ==
+ {:validation_failure, :too_many_args}
+ assert @command.validate(["quorum-queue-a", "extra-arg", "another-extra-arg"], %{}) ==
+ {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats one positional arguments and default switches as a success" do
+ assert @command.validate(["quorum-queue-a"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ assert match?({:badrpc, _}, @command.run(["quorum-queue-a"],
+ Map.merge(context[:opts], %{node: :jake@thedog, vhost: "/", timeout: 200})))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/rabbitmqctl_test.exs b/deps/rabbitmq_cli/test/rabbitmqctl_test.exs
new file mode 100644
index 0000000000..c6b085daad
--- /dev/null
+++ b/deps/rabbitmq_cli/test/rabbitmqctl_test.exs
@@ -0,0 +1,301 @@
+## 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 RabbitMQCtlTest do
+ use ExUnit.Case, async: false
+ import ExUnit.CaptureIO
+ import RabbitMQ.CLI.Core.ExitCodes
+ import TestHelper
+
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ set_scope(:all)
+
+ :ok
+ end
+
+ #
+ # --help and `help [command]`
+ #
+
+ test "--help option prints help for the command and exits with an OK" do
+ command = ["status", "--help"]
+ assert capture_io(fn ->
+ error_check(command, exit_ok())
+ end) =~ ~r/Usage/
+ end
+
+ test "bare --help prints general help and exits with an OK" do
+ command = ["--help"]
+ assert capture_io(fn ->
+ error_check(command, exit_ok())
+ end) =~ ~r/Usage/
+ end
+
+ test "help [command] prints help for the command and exits with an OK" do
+ command = ["help", "status"]
+ assert capture_io(fn ->
+ error_check(command, exit_ok())
+ end) =~ ~r/Usage/
+ end
+
+ test "bare help command prints general help and exits with an OK" do
+ command = ["help"]
+ assert capture_io(fn ->
+ error_check(command, exit_ok())
+ end) =~ ~r/Usage/
+ end
+
+ #
+ # Validation and Error Handling
+ #
+
+ test "print error message on a bad connection" do
+ command = ["status", "-n", "sandwich@pastrami"]
+ assert capture_io(:stderr, fn ->
+ error_check(command, exit_unavailable())
+ end) =~ ~r/unable to perform an operation on node 'sandwich@pastrami'/
+ end
+
+ test "when an RPC call times out, prints a timeout message" do
+ command = ["list_users", "-t", "0"]
+ assert capture_io(:stderr, fn ->
+ error_check(command, exit_tempfail())
+ end) =~ ~r/Error: operation list_users on node #{get_rabbit_hostname()} timed out. Timeout value used: 0/
+ end
+
+ test "when authentication fails, prints an authentication error message" do
+ add_user "kirk", "khaaaaaan"
+ command = ["authenticate_user", "kirk", "makeitso"]
+ assert capture_io(:stderr,
+ fn -> error_check(command, exit_dataerr())
+ end) =~ ~r/Error: failed to authenticate user \"kirk\"/
+ delete_user "kirk"
+ end
+
+ test "when invoked without arguments, displays a generic usage message and exits with a non-zero code" do
+ command = []
+ assert capture_io(:stderr, fn ->
+ error_check(command, exit_usage())
+ end) =~ ~r/usage/i
+ end
+
+ test "when invoked with only a --help, shows a generic usage message and exits with a success" do
+ command = ["--help"]
+ assert capture_io(:stdio, fn ->
+ error_check(command, exit_ok())
+ end) =~ ~r/usage/i
+ end
+
+ test "when invoked with --help [command], shows a generic usage message and exits with a success" do
+ command = ["--help", "status"]
+ assert capture_io(:stdio, fn ->
+ error_check(command, exit_ok())
+ end) =~ ~r/usage/i
+ end
+
+ test "when no command name is provided, displays usage" do
+ command = ["-n", "sandwich@pastrami"]
+ assert capture_io(:stderr, fn ->
+ error_check(command, exit_usage())
+ end) =~ ~r/usage/i
+ end
+
+ test "short node name without the host part connects properly" do
+ command = ["status", "-n", "rabbit"]
+ capture_io(:stderr, fn -> error_check(command, exit_ok()) end)
+ end
+
+ test "a non-existent command results in help message displayed" do
+ command = ["not_real"]
+ assert capture_io(:stderr, fn ->
+ error_check(command, exit_usage())
+ end) =~ ~r/Usage/
+ end
+
+ test "a command that's been provided extra arguments exits with a reasonable error code" do
+ command = ["status", "extra"]
+ output = capture_io(:stderr, fn ->
+ error_check(command, exit_usage())
+ end)
+ assert output =~ ~r/too many arguments/
+ assert output =~ ~r/Usage/
+ assert output =~ ~r/status/
+ end
+
+ test "a command that's been provided insufficient arguments exits with a reasonable error code" do
+ command = ["list_user_permissions"]
+ output = capture_io(:stderr, fn ->
+ error_check(command, exit_usage())
+ end)
+ assert output =~ ~r/not enough arguments/
+ assert output =~ ~r/Usage/
+ assert output =~ ~r/list_user_permissions/
+ end
+
+ test "a command that's provided an invalid argument exits a reasonable error" do
+ command = ["set_disk_free_limit", "2097152bytes"]
+ capture_io(:stderr, fn -> error_check(command, exit_dataerr()) end)
+ end
+
+ test "a command that fails with an error exits with a reasonable error code" do
+ command = ["delete_user", "voldemort"]
+ capture_io(:stderr, fn -> error_check(command, exit_nouser()) end)
+ end
+
+ test "a mcommand with an unsupported option as the first command-line arg fails gracefully" do
+ command1 = ["--invalid=true", "list_permissions", "-p", "/"]
+ assert capture_io(:stderr, fn ->
+ error_check(command1, exit_usage())
+ end) =~ ~r/Invalid options for this command/
+
+ command2 = ["--node", "rabbit", "status", "quack"]
+ assert capture_io(:stderr, fn ->
+ error_check(command2, exit_usage())
+ end) =~ ~r/too many arguments./
+
+ command3 = ["--node", "rabbit", "add_user"]
+ assert capture_io(:stderr, fn ->
+ error_check(command3, exit_usage())
+ end) =~ ~r/not enough arguments./
+ end
+
+## ------------------------- Default Flags ------------------------------------
+
+ test "an empty node option is filled with the default rabbit node" do
+ assert RabbitMQCtl.merge_all_defaults(%{})[:node] ==
+ TestHelper.get_rabbit_hostname()
+ end
+
+ test "a non-empty node option is not overwritten" do
+ assert RabbitMQCtl.merge_all_defaults(%{node: :jake@thedog})[:node] ==
+ :jake@thedog
+ end
+
+ test "an empty timeout option is set to infinity" do
+ assert RabbitMQCtl.merge_all_defaults(%{})[:timeout] == :infinity
+ end
+
+ test "a non-empty timeout option is not overridden" do
+ assert RabbitMQCtl.merge_all_defaults(%{timeout: 60})[:timeout] == 60
+ end
+
+ test "other parameters are not overridden by the default" do
+ assert RabbitMQCtl.merge_all_defaults(%{vhost: "quack"})[:vhost] == "quack"
+ end
+
+ test "any flags that aren't global or command-specific cause a bad option" do
+ command1 = ["status", "--nod=rabbit"]
+ assert capture_io(:stderr, fn ->
+ error_check(command1, exit_usage())
+ end) =~ ~r/Invalid options for this command/
+
+ command2 = ["list_permissions", "-o", "/"]
+ assert capture_io(:stderr, fn ->
+ error_check(command2, exit_usage())
+ end) =~ ~r/Invalid options for this command/
+ end
+
+ #
+ # --auto-complete and `autocomplete [command]`
+ #
+
+ test "--auto-complete delegates to the autocomplete command" do
+ # Note: these are not script name (scope) aware without --script-name
+ # but the actual command invoked in a shell will be
+ check_output(["--auto-complete", "list_q"], "list_queues\n")
+ check_output(["--auto-complete", "list_con", "--script-name", "rabbitmq-diagnostics"], "list_connections\nlist_consumers\n")
+ check_output(["--auto-complete", "--script-name", "rabbitmq-diagnostics", "mem"], "memory_breakdown\n")
+ check_output(["--auto-complete", "--script-name", "rabbitmq-queues", "add_m"], "add_member\n")
+ end
+
+ test "autocompletion command used directly" do
+ # Note: these are not script name (scope) aware without --script-name
+ # but the actual command invoked in a shell will be
+ check_output(["autocomplete", "list_q"], "list_queues\n")
+ check_output(["autocomplete", "list_con", "--script-name", "rabbitmq-diagnostics"], "list_connections\nlist_consumers\n")
+ check_output(["autocomplete", "--script-name", "rabbitmq-diagnostics", "mem"], "memory_breakdown\n")
+ check_output(["autocomplete", "--script-name", "rabbitmq-queues", "add_m"], "add_member\n")
+ end
+
+ defp check_output(cmd, out) do
+ assert capture_io(fn ->
+ error_check(cmd, exit_ok())
+ end) == out
+ end
+
+
+## ------------------------- Error formatting ---------------------------------
+
+ test "badrpc nodedown error" do
+ exit_code = exit_unavailable()
+ node = :example@node
+ {:error, ^exit_code, message} =
+ RabbitMQCtl.handle_command_output(
+ {:error, {:badrpc, :nodedown}},
+ :no_command, %{node: node},
+ fn(output, _, _) -> output end)
+
+ assert message =~ ~r/Error: unable to perform an operation on node/
+ assert message =~ ~r/DIAGNOSTICS/
+ assert message =~ ~r/attempted to contact/
+
+ localnode = :non_existent_node@localhost
+ {:error, ^exit_code, message} =
+ RabbitMQCtl.handle_command_output(
+ {:error, {:badrpc, :nodedown}},
+ :no_command, %{node: localnode},
+ fn(output, _, _) -> output end)
+ assert message =~ ~r/DIAGNOSTICS/
+ assert message =~ ~r/attempted to contact/
+ assert message =~ ~r/suggestion: start the node/
+ end
+
+ test "badrpc timeout error" do
+ exit_code = exit_tempfail()
+ timeout = 1000
+ nodename = :node@host
+ err_msg = "Error: operation example on node node@host timed out. Timeout value used: #{timeout}"
+ {:error, ^exit_code, ^err_msg} =
+ RabbitMQCtl.handle_command_output(
+ {:error, {:badrpc, :timeout}},
+ ExampleCommand, %{timeout: timeout, node: nodename},
+ fn(output, _, _) -> output end)
+ end
+
+ test "generic error" do
+ exit_code = exit_unavailable()
+ {:error, ^exit_code, "Error:\nerror message"} =
+ RabbitMQCtl.handle_command_output(
+ {:error, "error message"},
+ :no_command, %{},
+ fn(output, _, _) -> output end)
+ end
+
+ test "inspect arbitrary error" do
+ exit_code = exit_unavailable()
+ error = %{i: [am: "arbitrary", error: 1]}
+ inspected = inspect(error)
+ {:error, ^exit_code, "Error:\n" <> ^inspected} =
+ RabbitMQCtl.handle_command_output(
+ {:error, error},
+ :no_command, %{},
+ fn(output, _, _) -> output end)
+ end
+
+ test "atom error" do
+ exit_code = exit_unavailable()
+ {:error, ^exit_code, "Error:\nerror_message"} =
+ RabbitMQCtl.handle_command_output(
+ {:error, :error_message},
+ :no_command, %{},
+ fn(output, _, _) -> output end)
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/streams/add_replica_command_test.exs b/deps/rabbitmq_cli/test/streams/add_replica_command_test.exs
new file mode 100644
index 0000000000..cffcd2e34d
--- /dev/null
+++ b/deps/rabbitmq_cli/test/streams/add_replica_command_test.exs
@@ -0,0 +1,57 @@
+## 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.
+##
+## Copyright (c) 2012-2020 VMware, Inc. or its affiliates. All rights reserved.
+
+defmodule RabbitMQ.CLI.Streams.Commands.AddReplicaCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Streams.Commands.AddReplicaCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a failure" do
+ assert @command.validate(["stream-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when three or more arguments are provided, returns a failure" do
+ assert @command.validate(["stream-queue-a", "rabbit@new-node", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["stream-queue-a", "rabbit@new-node", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats two positional arguments and default switches as a success" do
+ assert @command.validate(["stream-queue-a", "rabbit@new-node"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["stream-queue-a", "rabbit@new-node"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/streams/delete_replica_command_test.exs b/deps/rabbitmq_cli/test/streams/delete_replica_command_test.exs
new file mode 100644
index 0000000000..cf6bcbe20d
--- /dev/null
+++ b/deps/rabbitmq_cli/test/streams/delete_replica_command_test.exs
@@ -0,0 +1,57 @@
+## 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.
+##
+## Copyright (c) 2012-2020 VMware, Inc. or its affiliates. All rights reserved.
+
+defmodule RabbitMQ.CLI.Streams.Commands.DeleteReplicaCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Streams.Commands.DeleteReplicaCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a failure" do
+ assert @command.validate(["stream-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when three or more arguments are provided, returns a failure" do
+ assert @command.validate(["stream-queue-a", "rabbit@new-node", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["stream-queue-a", "rabbit@new-node", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats two positional arguments and default switches as a success" do
+ assert @command.validate(["stream-queue-a", "rabbit@new-node"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["stream-queue-a", "rabbit@new-node"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/streams/set_stream_retention_policy_command_test.exs b/deps/rabbitmq_cli/test/streams/set_stream_retention_policy_command_test.exs
new file mode 100644
index 0000000000..56f960320b
--- /dev/null
+++ b/deps/rabbitmq_cli/test/streams/set_stream_retention_policy_command_test.exs
@@ -0,0 +1,63 @@
+## 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 Pivotal Software, Inc. All rights reserved.
+
+defmodule RabbitMQ.CLI.Streams.Commands.SetStreamRetentionPolicyCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Streams.Commands.SetStreamRetentionPolicyCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 30000
+ }}
+ end
+
+
+ test "validate: when no arguments are provided, returns a failure" do
+ assert @command.validate([], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when one argument is provided, returns a failure" do
+ assert @command.validate(["stream-queue-a"], %{}) == {:validation_failure, :not_enough_args}
+ end
+
+ test "validate: when three or more arguments are provided, returns a failure" do
+ assert @command.validate(["stream-queue-a", "1D", "one-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ assert @command.validate(["stream-queue-a", "1D", "extra-arg", "another-extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: treats two positional arguments and default switches as a success" do
+ assert @command.validate(["stream-queue-a", "2Y"], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc" do
+ assert match?({:badrpc, _}, @command.run(["stream-queue-a", "1Y"],
+ %{node: :jake@thedog, vhost: "/", timeout: 200}))
+ end
+
+ test "run: targeting an unknown queue returns an error", context do
+ assert match?({:error, _}, @command.run(["stream-queue-a", "1Y"],
+ Map.merge(context[:opts], %{vhost: "/"})))
+ end
+end
diff --git a/deps/rabbitmq_cli/test/test_helper.exs b/deps/rabbitmq_cli/test/test_helper.exs
new file mode 100644
index 0000000000..fca68e57bd
--- /dev/null
+++ b/deps/rabbitmq_cli/test/test_helper.exs
@@ -0,0 +1,620 @@
+## 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.
+
+four_hours = 240 * 60 * 1000
+ExUnit.configure(
+ exclude: [disabled: true],
+ module_load_timeout: four_hours,
+ timeout: four_hours)
+
+ExUnit.start()
+
+defmodule TestHelper do
+ import ExUnit.Assertions
+ alias RabbitMQ.CLI.Plugins.Helpers, as: PluginHelpers
+
+ alias RabbitMQ.CLI.Core.{CommandModules, Config, Helpers, NodeName}
+ import RabbitMQ.CLI.Core.Platform
+
+ def get_rabbit_hostname(node_name_type \\ :shortnames) do
+ Helpers.get_rabbit_hostname(node_name_type)
+ end
+
+ def hostname, do: NodeName.hostname()
+
+ def domain, do: NodeName.domain()
+
+ def fixture_file_path(filename) do
+ Path.join([File.cwd!(), "test", "fixtures", "files", filename])
+ end
+
+ def fixture_plugins_path(plugins_directory) do
+ Path.join([File.cwd!(), "test", "fixtures", "plugins", plugins_directory])
+ end
+
+ def get_cluster_name() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_nodes, :cluster_name, [])
+ end
+
+ def add_vhost(name) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_vhost, :add, [name, "acting-user"])
+ end
+
+ def delete_vhost(name) do
+ # some quick tests create and delete a vhost immediately, resulting
+ # in a high enough restart intensity in rabbit_vhost_sup_wrapper to
+ # make the rabbit app terminate. See https://github.com/rabbitmq/rabbitmq-server/issues/1280.
+ :timer.sleep(250)
+ :rpc.call(get_rabbit_hostname(), :rabbit_vhost, :delete, [name, "acting-user"])
+ end
+
+ def list_vhosts() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_vhost, :info_all, [])
+ end
+
+ def enable_feature_flag(feature_flag) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_feature_flags, :enable, [feature_flag])
+ end
+
+ def list_feature_flags(arg) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_feature_flags, :list, [arg])
+ end
+
+ def add_user(name, password) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :add_user,
+ [name, password, "acting-user"])
+ end
+
+ def delete_user(name) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :delete_user,
+ [name, "acting-user"])
+ end
+
+ def list_users() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :list_users, [])
+ end
+
+ def trace_on(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_trace, :start, [vhost])
+ end
+
+ def trace_off(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_trace, :stop, [vhost])
+ end
+
+ def set_user_tags(name, tags) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :set_tags,
+ [name, tags, "acting-user"])
+ end
+
+ def authenticate_user(name, password) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_access_control,:check_user_pass_login, [name, password])
+ end
+
+ def set_parameter(vhost, component_name, key, value) do
+ :ok = :rpc.call(get_rabbit_hostname(), :rabbit_runtime_parameters, :parse_set, [vhost, component_name, key, value, :nouser])
+ end
+
+ def clear_parameter(vhost, component_name, key) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_runtime_parameters, :clear, [vhost, component_name, key, <<"acting-user">>])
+ end
+
+ def list_parameters(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_runtime_parameters, :list_formatted, [vhost])
+ end
+
+ def set_global_parameter(key, value) do
+ :ok = :rpc.call(get_rabbit_hostname(), :rabbit_runtime_parameters, :parse_set_global,
+ [key, value, "acting-user"])
+ end
+
+ def clear_global_parameter(key) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_runtime_parameters, :clear_global,
+ [key, "acting-user"])
+ end
+
+ def list_global_parameters() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_runtime_parameters, :list_global_formatted, [])
+ end
+
+ def set_permissions(user, vhost, [conf, write, read]) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :set_permissions, [user, vhost, conf, write, read, "acting-user"])
+ end
+
+ def list_policies(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_policy, :list_formatted, [vhost])
+ end
+
+ def set_policy(vhost, name, pattern, value) do
+ {:ok, decoded} = :rabbit_json.try_decode(value)
+ parsed = :maps.to_list(decoded)
+ :ok = :rpc.call(get_rabbit_hostname(), :rabbit_policy, :set, [vhost, name, pattern, parsed, 0, "all", "acting-user"])
+ end
+
+ def clear_policy(vhost, key) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_policy, :delete, [vhost, key, "acting-user"])
+ end
+
+ def list_operator_policies(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_policy, :list_formatted_op, [vhost])
+ end
+
+ def set_operator_policy(vhost, name, pattern, value) do
+ {:ok, decoded} = :rabbit_json.try_decode(value)
+ parsed = :maps.to_list(decoded)
+ :ok = :rpc.call(get_rabbit_hostname(), :rabbit_policy, :set_op, [vhost, name, pattern, parsed, 0, "all", "acting-user"])
+ end
+
+ def clear_operator_policy(vhost, key) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_policy, :delete_op, [vhost, key, "acting-user"])
+ end
+
+ def declare_queue(name, vhost, durable \\ false, auto_delete \\ false, args \\ [], owner \\ :none) do
+ queue_name = :rabbit_misc.r(vhost, :queue, name)
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_amqqueue, :declare,
+ [queue_name, durable, auto_delete, args, owner, "acting-user"])
+ end
+
+ def delete_queue(name, vhost) do
+ queue_name = :rabbit_misc.r(vhost, :queue, name)
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_amqqueue, :delete,
+ [queue_name, false, false, "acting-user"])
+ end
+
+ def lookup_queue(name, vhost) do
+ queue_name = :rabbit_misc.r(vhost, :queue, name)
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_amqqueue, :lookup,
+ [queue_name])
+ end
+
+ def declare_exchange(name, vhost, type \\ :direct, durable \\ false, auto_delete \\ false, internal \\ false, args \\ []) do
+ exchange_name = :rabbit_misc.r(vhost, :exchange, name)
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_exchange, :declare,
+ [exchange_name, type, durable, auto_delete, internal, args, "acting-user"])
+ end
+
+ def list_permissions(vhost) do
+ :rpc.call(
+ get_rabbit_hostname(),
+ :rabbit_auth_backend_internal,
+ :list_vhost_permissions,
+ [vhost],
+ :infinity
+ )
+ end
+
+ def set_topic_permissions(user, vhost, exchange, writePerm, readPerm) do
+ :rpc.call(
+ get_rabbit_hostname(),
+ :rabbit_auth_backend_internal,
+ :set_topic_permissions,
+ [user, vhost, exchange, writePerm, readPerm, "acting-user"],
+ :infinity
+ )
+ end
+
+ def list_user_topic_permissions(user) do
+ :rpc.call(
+ get_rabbit_hostname(),
+ :rabbit_auth_backend_internal,
+ :list_user_topic_permissions,
+ [user],
+ :infinity
+ )
+ end
+
+ def clear_topic_permissions(user, vhost) do
+ :rpc.call(
+ get_rabbit_hostname(),
+ :rabbit_auth_backend_internal,
+ :clear_topic_permissions,
+ [user, vhost, "acting-user"],
+ :infinity
+ )
+ end
+
+ def set_vm_memory_high_watermark(limit) do
+ :rpc.call(get_rabbit_hostname(), :vm_memory_monitor, :set_vm_memory_high_watermark, [limit])
+ end
+
+ def set_disk_free_limit(limit) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_disk_monitor, :set_disk_free_limit, [limit])
+ end
+
+
+ #
+ # App lifecycle
+ #
+
+ def await_rabbitmq_startup() do
+ await_rabbitmq_startup_with_retries(100)
+ end
+
+ def await_rabbitmq_startup_with_retries(0) do
+ throw({:error, "Failed to call rabbit.await_startup/0 with retries: node #{get_rabbit_hostname()} was down"})
+ end
+ def await_rabbitmq_startup_with_retries(retries_left) do
+ case :rabbit_misc.rpc_call(get_rabbit_hostname(), :rabbit, :await_startup, []) do
+ :ok ->
+ :ok
+ {:badrpc, :nodedown} ->
+ :timer.sleep(50)
+ await_rabbitmq_startup_with_retries(retries_left - 1)
+ end
+ end
+
+ def await_condition(fun, timeout) do
+ retries = Integer.floor_div(timeout, 50)
+ await_condition_with_retries(fun, retries)
+ end
+
+ def await_condition_with_retries(_fun, 0) do
+ throw({:error, "Condition did not materialize"})
+ end
+ def await_condition_with_retries(fun, retries_left) do
+ case fun.() do
+ true -> :ok
+ _ ->
+ :timer.sleep(50)
+ await_condition_with_retries(fun, retries_left - 1)
+ end
+ end
+
+ def is_rabbitmq_app_running() do
+ :rabbit_misc.rpc_call(get_rabbit_hostname(), :rabbit, :is_booted, [])
+ end
+
+ def start_rabbitmq_app do
+ :rabbit_misc.rpc_call(get_rabbit_hostname(), :rabbit, :start, [])
+ await_rabbitmq_startup()
+ :timer.sleep(250)
+ end
+
+ def stop_rabbitmq_app do
+ :rabbit_misc.rpc_call(get_rabbit_hostname(), :rabbit, :stop, [])
+ :timer.sleep(1200)
+ end
+
+ def drain_node() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_maintenance, :drain, [])
+ end
+
+ def revive_node() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_maintenance, :revive, [])
+ end
+
+ def is_draining_node() do
+ node = get_rabbit_hostname()
+ :rpc.call(node, :rabbit_maintenance, :is_being_drained_local_read, [node])
+ end
+
+ def status do
+ :rpc.call(get_rabbit_hostname(), :rabbit, :status, [])
+ end
+
+ def error_check(cmd_line, code) do
+ assert catch_exit(RabbitMQCtl.main(cmd_line)) == {:shutdown, code}
+ end
+
+ def with_channel(vhost, fun) do
+ with_connection(vhost,
+ fn(conn) ->
+ {:ok, chan} = AMQP.Channel.open(conn)
+ AMQP.Confirm.select(chan)
+ fun.(chan)
+ end)
+ end
+
+ def with_connection(vhost, fun) do
+ Application.ensure_all_started(:amqp)
+ {:ok, conn} = AMQP.Connection.open(virtual_host: vhost)
+ ExUnit.Callbacks.on_exit(fn ->
+ try do
+ :amqp_connection.close(conn, 1000)
+ catch
+ :exit, _ -> :ok
+ end
+ end)
+ fun.(conn)
+ end
+
+ def with_connections(vhosts, fun) do
+ Application.ensure_all_started(:amqp)
+ conns = for v <- vhosts do
+ {:ok, conn} = AMQP.Connection.open(virtual_host: v)
+ conn
+ end
+ ExUnit.Callbacks.on_exit(fn ->
+ try do
+ for c <- conns, do: :amqp_connection.close(c, 1000)
+ catch
+ :exit, _ -> :ok
+ end
+ end)
+ fun.(conns)
+ end
+
+ def message_count(vhost, queue_name) do
+ with_channel(vhost, fn(channel) ->
+ {:ok, %{message_count: mc}} = AMQP.Queue.declare(channel, queue_name)
+ mc
+ end)
+ end
+
+ def publish_messages(vhost, queue_name, count) do
+ with_channel(vhost, fn(channel) ->
+ AMQP.Queue.purge(channel, queue_name)
+ for i <- 1..count do
+ AMQP.Basic.publish(channel, "", queue_name,
+ "test_message" <> Integer.to_string(i))
+ end
+ AMQP.Confirm.wait_for_confirms(channel, 30)
+ end)
+ end
+
+ def await_no_client_connections(node, timeout) do
+ iterations = timeout / 10
+ await_no_client_connections_with_iterations(node, iterations)
+ end
+
+ def await_no_client_connections_with_iterations(_node, n) when n <= 0 do
+ flunk "Ran out of retries, still have active client connections"
+ end
+ def await_no_client_connections_with_iterations(node, n) when n > 0 do
+ case :rpc.call(node, :rabbit_networking, :connections_local, []) do
+ [] -> :ok
+ _xs ->
+ :timer.sleep(10)
+ await_no_client_connections_with_iterations(node, n - 1)
+ end
+ end
+
+ def close_all_connections(node) do
+ # we intentionally use connections_local/0 here because connections/0,
+ # the cluster-wide version, loads some bits around cluster membership
+ # that are not normally ready with a single node. MK.
+ #
+ # when/if we decide to test
+ # this project against a cluster of nodes this will need revisiting. MK.
+ for pid <- :rpc.call(node, :rabbit_networking, :connections_local, []) do
+ :rpc.call(node, :rabbit_networking, :close_connection, [pid, :force_closed])
+ end
+ await_no_client_connections(node, 5000)
+ end
+
+ def expect_client_connection_failure() do
+ expect_client_connection_failure("/")
+ end
+ def expect_client_connection_failure(vhost) do
+ Application.ensure_all_started(:amqp)
+ assert {:error, :econnrefused} == AMQP.Connection.open(virtual_host: vhost)
+ end
+
+ def delete_all_queues() do
+ try do
+ immediately_delete_all_queues(:rabbit_amqqueue.list())
+ catch
+ _, _ -> :ok
+ end
+ end
+
+ def delete_all_queues(vhost) do
+ try do
+ immediately_delete_all_queues(:rabbit_amqqueue.list(vhost))
+ catch
+ _, _ -> :ok
+ end
+ end
+
+ defp immediately_delete_all_queues(qs) do
+ for q <- qs do
+ try do
+ :rpc.call(
+ get_rabbit_hostname(),
+ :rabbit_amqeueue,
+ :delete,
+ [q, false, false],
+ 5000
+ )
+ catch
+ _, _ -> :ok
+ end
+ end
+ end
+
+ def reset_vm_memory_high_watermark() do
+ try do
+ :rpc.call(
+ get_rabbit_hostname(),
+ :vm_memory_monitor,
+ :set_vm_memory_high_watermark,
+ [0.4],
+ 5000
+ )
+ catch
+ _, _ -> :ok
+ end
+ end
+
+ def emit_list_multiple_sources(list1, list2, ref, pid) do
+ pids = for list <- [list1, list2], do: Kernel.spawn_link(TestHelper, :emit_list, [list, ref, pid])
+ :rabbit_control_misc.await_emitters_termination(pids)
+ end
+
+ def emit_list(list, ref, pid) do
+ emit_list_map(list, &(&1), ref, pid)
+ end
+
+ def emit_list_map(list, fun, ref, pid) do
+ :rabbit_control_misc.emitting_map(pid, ref, fun, list)
+ end
+
+ def run_command_to_list(command, args) do
+ res = Kernel.apply(command, :run, args)
+ case Enumerable.impl_for(res) do
+ nil -> res;
+ _ -> Enum.to_list(res)
+ end
+ end
+
+ def vhost_exists?(vhost) do
+ Enum.any?(list_vhosts(), fn(v) -> v[:name] == vhost end)
+ end
+
+ def set_enabled_plugins(plugins, mode, node, opts) do
+ {:ok, enabled} = PluginHelpers.set_enabled_plugins(plugins, opts)
+
+ PluginHelpers.update_enabled_plugins(enabled, mode, node, opts)
+ end
+
+ def currently_active_plugins(context) do
+ Enum.sort(:rabbit_misc.rpc_call(context[:opts][:node], :rabbit_plugins, :active, []))
+ end
+
+ def enable_federation_plugin() do
+ 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,
+ online: true, offline: false}
+
+ plugins = currently_active_plugins(%{opts: %{node: node}})
+ case Enum.member?(plugins, :rabbitmq_federation) do
+ true -> :ok
+ false ->
+ set_enabled_plugins(plugins ++ [:rabbitmq_federation], :online, get_rabbit_hostname(), opts)
+ end
+ end
+
+ def set_vhost_limits(vhost, limits) do
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_vhost_limit, :parse_set, [vhost, limits, <<"acting-user">>])
+ end
+ def get_vhost_limits(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_vhost_limit, :list, [vhost])
+ |> Map.new
+ end
+
+ def clear_vhost_limits(vhost) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_vhost_limit, :clear, [vhost, <<"acting-user">>])
+ end
+
+ def resume_all_client_listeners() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_maintenance, :resume_all_client_listeners, [])
+ end
+
+ def suspend_all_client_listeners() do
+ :rpc.call(get_rabbit_hostname(), :rabbit_maintenance, :suspend_all_client_listeners, [])
+ end
+
+ def set_user_limits(user, limits) do
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_auth_backend_internal, :set_user_limits, [user, limits, <<"acting-user">>])
+ end
+
+ def get_user_limits(user) do
+ :rpc.call(get_rabbit_hostname(), :rabbit_auth_backend_internal, :get_user_limits, [user])
+ |> Map.new
+ end
+
+ def clear_user_limits(user) do
+ clear_user_limits user, "max-connections"
+ clear_user_limits user, "max-channels"
+ end
+
+ def clear_user_limits(user, limittype) do
+ :rpc.call(get_rabbit_hostname(),
+ :rabbit_auth_backend_internal, :clear_user_limits, [user, limittype, <<"acting-user">>])
+ end
+
+ def set_scope(scope) do
+ script_name = Config.get_option(:script_name, %{})
+ scopes = Keyword.put(Application.get_env(:rabbitmqctl, :scopes), script_name, scope)
+ Application.put_env(:rabbitmqctl, :scopes, scopes)
+ CommandModules.load(%{})
+ end
+
+ def switch_plugins_directories(old_value, new_value) do
+ :rabbit_misc.rpc_call(get_rabbit_hostname(), :application, :set_env,
+ [:rabbit, :plugins_dir, new_value])
+ ExUnit.Callbacks.on_exit(fn ->
+ :rabbit_misc.rpc_call(get_rabbit_hostname(), :application, :set_env,
+ [:rabbit, :plugins_dir, old_value])
+ end)
+ end
+
+ def get_opts_with_non_existing_plugins_directory(context) do
+ get_opts_with_plugins_directories(context, ["/tmp/non_existing_rabbitmq_dummy_plugins"])
+ end
+
+ def get_opts_with_plugins_directories(context, plugins_directories) do
+ opts = context[:opts]
+ plugins_dir = opts[:plugins_dir]
+ all_directories = Enum.join([to_string(plugins_dir) | plugins_directories], path_separator())
+ %{opts | plugins_dir: to_charlist(all_directories)}
+ end
+
+ def get_opts_with_existing_plugins_directory(context) do
+ extra_plugin_directory = System.tmp_dir!() |> Path.join("existing_rabbitmq_dummy_plugins")
+ File.mkdir!(extra_plugin_directory)
+ ExUnit.Callbacks.on_exit(fn ->
+ File.rm_rf(extra_plugin_directory)
+ end)
+ get_opts_with_plugins_directories(context, [extra_plugin_directory])
+ end
+
+ def check_plugins_enabled(plugins, context) do
+ {:ok, [xs]} = :file.consult(context[:opts][:enabled_plugins_file])
+ assert_equal_sets(plugins, xs)
+ end
+
+ def assert_equal_sets(a, b) do
+ asorted = Enum.sort(a)
+ bsorted = Enum.sort(b)
+ assert asorted == bsorted
+ end
+
+ def assert_stream_without_errors(stream) do
+ true = Enum.all?(stream, fn({:error, _}) -> false;
+ ({:error, _, _}) -> false;
+ (_) -> true end)
+ end
+
+ def wait_for_log_message(message, file \\ nil, attempts \\ 100) do
+ ## Assume default log is the first one
+ log_file = case file do
+ nil ->
+ [default_log | _] = :rpc.call(get_rabbit_hostname(), :rabbit_lager, :log_locations, [])
+ default_log
+ _ -> file
+ end
+ case File.read(log_file) do
+ {:ok, data} ->
+ case String.match?(data, Regex.compile!(message)) do
+ true -> :ok
+ false ->
+ :timer.sleep(100)
+ wait_for_log_message(message, log_file, attempts - 1)
+ end
+ _ ->
+ :timer.sleep(100)
+ wait_for_log_message(message, log_file, attempts - 1)
+ end
+ end
+end
diff --git a/deps/rabbitmq_cli/test/upgrade/await_online_quorum_plus_one_command_test.exs b/deps/rabbitmq_cli/test/upgrade/await_online_quorum_plus_one_command_test.exs
new file mode 100644
index 0000000000..c169f9ff5d
--- /dev/null
+++ b/deps/rabbitmq_cli/test/upgrade/await_online_quorum_plus_one_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 AwaitOnlineQuorumPlusOneCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Upgrade.Commands.AwaitOnlineQuorumPlusOneCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 5000
+ }}
+ end
+
+ test "merge_defaults: overrides a timeout" do
+ assert @command.merge_defaults([], %{}) == {[], %{timeout: 120_000}}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: succeeds with no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = %{node: :jake@thedog, timeout: 200}
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], opts)))
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/upgrade/await_online_synchronized_mirror_command_test.exs b/deps/rabbitmq_cli/test/upgrade/await_online_synchronized_mirror_command_test.exs
new file mode 100644
index 0000000000..7089dada2c
--- /dev/null
+++ b/deps/rabbitmq_cli/test/upgrade/await_online_synchronized_mirror_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 AwaitOnlineSynchronizedMirrorsCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Upgrade.Commands.AwaitOnlineSynchronizedMirrorCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 5000
+ }}
+ end
+
+ test "merge_defaults: overrides a timeout" do
+ assert @command.merge_defaults([], %{}) == {[], %{timeout: 120_000}}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: succeeds with no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = %{node: :jake@thedog, timeout: 200}
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], opts)))
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/upgrade/drain_command_test.exs b/deps/rabbitmq_cli/test/upgrade/drain_command_test.exs
new file mode 100644
index 0000000000..3533f7feff
--- /dev/null
+++ b/deps/rabbitmq_cli/test/upgrade/drain_command_test.exs
@@ -0,0 +1,57 @@
+## 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 DrainCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Upgrade.Commands.DrainCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ revive_node()
+
+ on_exit(fn ->
+ revive_node()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 5000
+ }}
+ end
+
+ test "merge_defaults: nothing to do" do
+ assert @command.merge_defaults([], %{}) == {[], %{}}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: succeeds with no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = %{node: :jake@thedog, timeout: 200}
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], opts)))
+ end
+
+ test "run: puts target node into maintenance mode", context do
+ assert not is_draining_node()
+ assert :ok == @command.run([], context[:opts])
+
+ await_condition(fn -> is_draining_node() end, 7000)
+ revive_node()
+ end
+end
diff --git a/deps/rabbitmq_cli/test/upgrade/post_upgrade_command_test.exs b/deps/rabbitmq_cli/test/upgrade/post_upgrade_command_test.exs
new file mode 100644
index 0000000000..e77390ecf0
--- /dev/null
+++ b/deps/rabbitmq_cli/test/upgrade/post_upgrade_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 PostUpgradeCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Upgrade.Commands.PostUpgradeCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 5000
+ }}
+ end
+
+ test "merge_defaults: nothing to do" do
+ assert @command.merge_defaults([], %{}) == {[], %{}}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: succeeds with no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = %{node: :jake@thedog, timeout: 200}
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], opts)))
+ end
+
+ test "run: returns an OK", context do
+ assert match?({:ok, _}, @command.run([], context[:opts]))
+ end
+
+end
diff --git a/deps/rabbitmq_cli/test/upgrade/revive_command_test.exs b/deps/rabbitmq_cli/test/upgrade/revive_command_test.exs
new file mode 100644
index 0000000000..6d43d59b83
--- /dev/null
+++ b/deps/rabbitmq_cli/test/upgrade/revive_command_test.exs
@@ -0,0 +1,57 @@
+## 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 ReviveCommandTest do
+ use ExUnit.Case, async: false
+ import TestHelper
+
+ @command RabbitMQ.CLI.Upgrade.Commands.ReviveCommand
+
+ setup_all do
+ RabbitMQ.CLI.Core.Distribution.start()
+
+ revive_node()
+
+ on_exit(fn ->
+ revive_node()
+ end)
+
+ :ok
+ end
+
+ setup context do
+ {:ok, opts: %{
+ node: get_rabbit_hostname(),
+ timeout: context[:test_timeout] || 5000
+ }}
+ end
+
+ test "merge_defaults: nothing to do" do
+ assert @command.merge_defaults([], %{}) == {[], %{}}
+ end
+
+ test "validate: accepts no positional arguments" do
+ assert @command.validate(["extra-arg"], %{}) == {:validation_failure, :too_many_args}
+ end
+
+ test "validate: succeeds with no positional arguments" do
+ assert @command.validate([], %{}) == :ok
+ end
+
+ @tag test_timeout: 3000
+ test "run: targeting an unreachable node throws a badrpc", context do
+ opts = %{node: :jake@thedog, timeout: 200}
+ assert match?({:badrpc, _}, @command.run([], Map.merge(context[:opts], opts)))
+ end
+
+ test "run: puts target node into regular operating mode", context do
+ assert not is_draining_node()
+ drain_node()
+ await_condition(fn -> is_draining_node() end, 7000)
+ assert :ok == @command.run([], context[:opts])
+ await_condition(fn -> not is_draining_node() end, 7000)
+ end
+end