unittest/shell_cmdline_options_t.cc (1,509 lines of code) (raw):

/* * Copyright (c) 2014, 2024, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdarg.h> #include <cstdio> #include <cstdlib> #include <fstream> #include <string> #include "mysqlshdk/include/shellcore/shell_options.h" #include "mysqlshdk/libs/utils/utils_general.h" #include "unittest/test_utils/mocks/gmock_clean.h" #include "unittest/test_utils/shell_base_test.h" using mysqlsh::Shell_options; namespace shcore { #define IS_CONNECTION_DATA true #define IS_NULLABLE true #define AS__STRING(x) std::to_string(x) std::string session_type_name(mysqlsh::SessionType type) { std::string ret_val; switch (type) { case mysqlsh::SessionType::Auto: ret_val = "auto"; break; case mysqlsh::SessionType::Classic: ret_val = "mysql"; break; case mysqlsh::SessionType::X: ret_val = "mysqlx"; break; } return ret_val; } std::string shell_mode_name(IShell_core::Mode mode) { std::string ret_val; switch (mode) { case IShell_core::Mode::JavaScript: ret_val = "JavaScript"; break; case IShell_core::Mode::Python: ret_val = "Python"; break; case IShell_core::Mode::SQL: ret_val = "SQL"; break; case IShell_core::Mode::None: ret_val = "None"; break; } return ret_val; } class Shell_cmdline_options : public tests::Shell_base_test { public: Shell_cmdline_options() { mycnf_path = getenv("TMPDIR"); mycnf_path += "/testmy.cnf"; defaults_file = "--defaults-file=" + mycnf_path; } std::string mycnf_path; std::string defaults_file; void make_mycnf(const std::string &text) { create_file(mycnf_path, text); } std::string get_string(const Shell_options::Storage *options, const std::string &option) { if (option == "host") return options->connection_options().get_host(); else if (option == "user") return options->connection_options().get_user(); else if (option == "password" || option == "password1") return options->connection_options().get_mfa_passwords()[0].has_value() ? std::string( *options->connection_options().get_mfa_passwords()[0]) : ""; else if (option == "password2") return options->connection_options().get_mfa_passwords()[1].has_value() ? std::string( *options->connection_options().get_mfa_passwords()[1]) : ""; else if (option == "password3") return options->connection_options().get_mfa_passwords()[2].has_value() ? std::string( *options->connection_options().get_mfa_passwords()[2]) : ""; else if (option == "port") return AS__STRING(options->connection_options().get_port()); else if (option == "schema") return options->connection_options().get_schema(); else if (option == "sock") return options->connection_options().has_socket() ? options->connection_options().get_socket() : ""; else if (option == "ssl-ca") return options->connection_options().get_ssl_options().get_ca(); else if (option == "ssl-cert") return options->connection_options().get_ssl_options().get_cert(); else if (option == "ssl-key") return options->connection_options().get_ssl_options().get_key(); else if (option == "ssl-capath") return options->connection_options().get_ssl_options().get_capath(); else if (option == "ssl-crl") return options->connection_options().get_ssl_options().get_crl(); else if (option == "ssl-crlpath") return options->connection_options().get_ssl_options().get_crlpath(); else if (option == "ssl-cipher") return options->connection_options().get_ssl_options().get_cipher(); else if (option == "tls-version") return options->connection_options().get_ssl_options().get_tls_version(); else if (option == "tls-ciphersuites") return options->connection_options() .get_ssl_options() .get_tls_ciphersuites(); else if (option == "uri") return options->connection_options().as_uri(); else if (option == "result_format") return options->result_format; else if (option == "wrap_json") return options->wrap_json; else if (option == "session_type" || option == "session-type") return options->connection_options().has_scheme() ? options->connection_options().get_scheme() : "auto"; else if (option == "force") return AS__STRING(options->force); else if (option == "interactive") return AS__STRING(options->interactive); else if (option == "full_interactive") return AS__STRING(options->full_interactive); else if (option == "passwords_from_stdin") return AS__STRING(options->passwords_from_stdin); else if (option == "prompt_password") return AS__STRING(options->prompt_password); else if (option == "recreate_database") return AS__STRING(options->recreate_database); else if (option == "trace_protocol") return AS__STRING(options->trace_protocol); else if (option == "log_level") return AS__STRING(options->log_level); else if (option == "initial-mode") return shell_mode_name(options->initial_mode); else if (option == "wizards") return AS__STRING(options->wizards); else if (option == "execute_statement") return options->execute_statement; else if (option == "run_file") return options->run_file; else if (option == "connect-timeout") return options->connection_options().get(mysqlshdk::db::kConnectTimeout); else if (option == "quiet-start") return AS__STRING(static_cast<int>(options->quiet_start)); else if (option == "showColumnTypeInfo") return AS__STRING(options->show_column_type_info); else if (option == "compress") return options->connection_options().get_compression(); else if (option == "mysqlPluginDir") return options->mysql_plugin_dir; else if (option == "log-sql") return options->log_sql; throw std::logic_error(option); } void test_option_equal_value(const std::string &option, const std::string &value, bool connection_options, const std::string &target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option=value std::string arg; arg.append("--").append(option).append("=").append(value); SCOPED_TRACE("TESTING: " + arg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_EQ(connection_options, options.has_connection_data()); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = value; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_option_equal_invalid_value(const std::string &option, const std::string &value, const std::string &error) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option=value std::string arg; arg.append("--").append(option).append("=").append(value); SCOPED_TRACE("TESTING: " + arg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(1, options.exit_code); EXPECT_STREQ(error.c_str(), cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_option_equal_invalid_values(const std::string &option, const std::vector<std::string> &values, const std::string &error) { for (const auto &v : values) { test_option_equal_invalid_value(option, v, error); } } void test_option_space_value(const std::string &option, const std::string &value, bool connection_options, const std::string &target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option [value] std::string arg; arg.append("--").append(option); SCOPED_TRACE("TESTING: " + arg + " " + value); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), const_cast<char *>(value.c_str()), NULL}; Shell_options cmd_options(3, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_EQ(connection_options, options.has_connection_data()); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = value; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_short_option_value(const std::string &option, const std::string &soption, const std::string &value, bool connection_options, const std::string &target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --ovalue std::string arg; arg.append("-").append(soption).append(value); SCOPED_TRACE("TESTING: " + arg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_EQ(connection_options, options.has_connection_data()); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = value; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_short_option_space_value(const std::string &option, const std::string &soption, const std::string &value, bool connection_options, const std::string &target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option [value] std::string arg; arg.append("-").append(soption); SCOPED_TRACE("TESTING: " + arg + " " + value); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), const_cast<char *>(value.c_str()), NULL}; Shell_options cmd_options(3, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_EQ(connection_options, options.has_connection_data()); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = value; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_option_space_no_value(const std::string &option, bool valid, const std::string &defval, const std::string target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option [value] std::string arg; arg.append("--").append(option); SCOPED_TRACE("TESTING: " + arg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); if (valid) { EXPECT_EQ(0, options.exit_code); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = defval; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); } else { EXPECT_EQ(1, options.exit_code); std::string message = "ut: option "; message.append("--").append(option).append(" requires an argument\n"); EXPECT_STREQ(message.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } void test_option_space_empty_value(const std::string &option, bool nullable) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option "" std::string arg; arg.append("--").append(option); SCOPED_TRACE("TESTING: " + arg + " \"\""); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), const_cast<char *>(""), NULL}; Shell_options cmd_options(3, argv); const Shell_options::Storage &options = cmd_options.get(); if (nullable) { if (options.exit_code == 1) { const std::string message = "Value for --" + option; EXPECT_THAT(cerr.str(), ::testing::StartsWith(message)); } } else { EXPECT_EQ(1, options.exit_code); std::string message = "ut: option "; message.append("--").append(option).append(" requires an argument\n"); EXPECT_STREQ(message.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } void test_short_option_no_value(const std::string &option, const std::string &soption, bool valid, const std::string &defval, const std::string target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests -o std::string arg; arg.append("-").append(soption); SCOPED_TRACE("TESTING: " + arg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); if (valid) { EXPECT_EQ(0, options.exit_code); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = defval; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); } else { EXPECT_EQ(1, options.exit_code); std::string message = "ut: option "; message.append("-").append(soption).append(" requires an argument\n"); EXPECT_STREQ(message.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } void test_short_option_space_empty_value(const std::string &option, const std::string &soption, bool valid, const std::string &defval, const std::string target_option = "", const char *target_value = NULL) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests -o "" std::string arg; arg.append("-").append(soption); SCOPED_TRACE("TESTING: " + arg + " \"\""); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), const_cast<char *>(""), NULL}; Shell_options cmd_options(3, argv); const Shell_options::Storage &options = cmd_options.get(); if (valid) { EXPECT_EQ(0, options.exit_code); std::string tgt_val; if (target_value) tgt_val.assign(target_value); else tgt_val = defval; std::string tgt_option = target_option.empty() ? option : target_option; EXPECT_STREQ(tgt_val.c_str(), get_string(&options, tgt_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); } else { EXPECT_EQ(1, options.exit_code); std::string message = "ut: option "; message.append("-").append(soption).append(" requires an argument\n"); EXPECT_STREQ(message.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } void test_option_equal_no_value(const std::string &option, bool valid) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option= std::string arg; arg.append("--").append(option).append("="); SCOPED_TRACE("TESTING: " + arg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(arg.c_str()), NULL}; Shell_options options(2, argv); if (valid) { EXPECT_EQ(0, options.get().exit_code); EXPECT_STREQ("", cerr.str().c_str()); } else { EXPECT_EQ(1, options.get().exit_code); std::string message = "ut: option "; message.append("--").append(option).append(" requires an argument\n"); EXPECT_STREQ(message.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } void test_deprecated_ssl(const std::string &scope, std::vector<char *> *args, const std::string &warning, const std::string &error, int expected_exit_code, mysqlshdk::db::Ssl_mode mode) { // Redirect cerr. std::streambuf *cerr_backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Redirect cout. std::streambuf *cout_backup = std::cout.rdbuf(); std::ostringstream cout; std::cout.rdbuf(cout.rdbuf()); SCOPED_TRACE("TESTING: " + scope); Shell_options options(args->size() - 1, &(args->at(0))); EXPECT_EQ(expected_exit_code, options.get().exit_code); if (!expected_exit_code) { MY_EXPECT_OUTPUT_CONTAINS(warning.c_str(), cout.str()); EXPECT_EQ( mode, options.get().connection_options().get_ssl_options().get_mode()); } else { EXPECT_STREQ(error.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(cerr_backup); // Restore old cout. std::cout.rdbuf(cout_backup); } void test_option_with_value(const std::string &option, const std::string &soption, const std::string &value, const std::string &defval, bool is_connection_data, bool nullable, const std::string &target_option = "", const char *target_value = NULL) { // --option=<value> test_option_equal_value(option, value, is_connection_data, target_option, target_value); // --option value test_option_space_value(option, value, is_connection_data, target_option, target_value); // --option test_option_space_no_value(option, !defval.empty() || nullable, defval, target_option); // --option "" test_option_space_empty_value(option, nullable); if (!soption.empty()) { // -o<value> test_short_option_value(option, soption, value, is_connection_data, target_option, target_value); // -o <value> test_short_option_space_value(option, soption, value, is_connection_data, target_option, target_value); // -o test_short_option_no_value(option, soption, !defval.empty() || nullable, defval, target_option); // -o "" test_short_option_space_empty_value( option, soption, !defval.empty() || nullable, defval, target_option); } // --option= test_option_equal_no_value(option, !defval.empty() || nullable); } void test_option_with_value_list(const std::string &option, const std::string &soption, const std::vector<std::string> &values, const std::string &defval, bool is_connection_data, bool nullable, const std::string &target_option = "", const char *target_value = NULL) { for (const auto &v : values) { test_option_with_value(option, soption, v, defval, is_connection_data, nullable, target_option, target_value); } } void test_option_with_no_value(const std::string &option, const std::string &target_option, const std::string &target_value) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); // Tests --option or -o SCOPED_TRACE("TESTING: " + option); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(option.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_STREQ(target_value.c_str(), get_string(&options, target_option).c_str()); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_session_type_conflicts(const std::string &firstArg, const std::string &secondArg, int ret_code) { std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); { SCOPED_TRACE("TESTING: " + firstArg + " " + secondArg); char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; Shell_options options(3, argv); EXPECT_EQ(ret_code, options.get().exit_code); std::string error = ""; if (options.get().exit_code) { std::string first_opt; std::string second_opt; if (firstArg[0] == '-') { first_opt = "option " + firstArg; } else { if (shcore::str_beginswith(firstArg, "mysql:")) first_opt = "URI scheme mysql://"; else first_opt = "URI scheme mysqlx://"; } if (secondArg[0] == '-') { second_opt = "Option " + secondArg; } else { if (shcore::str_beginswith(secondArg, "mysql:")) second_opt = "URI scheme mysql://"; else second_opt = "URI scheme mysqlx://"; } error = second_opt + " cannot be combined with " + first_opt + "\n"; } EXPECT_STREQ(error.c_str(), cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } void test_conflicting_options(std::string context, size_t argc, char *argv[], std::string error) { std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); SCOPED_TRACE("TESTING: " + context); Shell_options options(argc, argv); EXPECT_EQ(1, options.get().exit_code); EXPECT_STREQ(error.c_str(), cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } void test_overriding_options(std::string context, size_t argc, char *argv[], const std::string &option, const std::string &value) { SCOPED_TRACE("TESTING: " + context); Shell_options options(argc, argv); EXPECT_EQ(0, options.get().exit_code); EXPECT_EQ(value, get_string(&options.get(), option)); } }; TEST_F(Shell_cmdline_options, default_values) { int argc = 0; char **argv = nullptr; Shell_options cmd_options(argc, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_FALSE(options.force); EXPECT_FALSE(options.full_interactive); EXPECT_FALSE(options.has_connection_data()); EXPECT_EQ(options.initial_mode, IShell_core::Mode::None); EXPECT_FALSE(options.interactive); EXPECT_EQ(options.log_level, shcore::Logger::LOG_INFO); EXPECT_EQ("table", options.result_format); EXPECT_EQ("off", options.wrap_json); EXPECT_FALSE(options.connection_options().get_mfa_passwords()[0].has_value()); EXPECT_FALSE(options.connection_options().get_mfa_passwords()[1].has_value()); EXPECT_FALSE(options.connection_options().get_mfa_passwords()[2].has_value()); EXPECT_FALSE(options.passwords_from_stdin); EXPECT_FALSE(options.prompt_password); EXPECT_TRUE(options.protocol.empty()); EXPECT_FALSE(options.recreate_database); EXPECT_TRUE(options.run_file.empty()); EXPECT_TRUE(!options.connection_options().has_schema()); EXPECT_TRUE(!options.connection_options().has_scheme()); EXPECT_TRUE(!options.connection_options().has_socket()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_ca()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_cert()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_key()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_capath()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_crl()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_crlpath()); EXPECT_TRUE(!options.connection_options().get_ssl_options().has_cipher()); EXPECT_TRUE( !options.connection_options().get_ssl_options().has_tls_version()); EXPECT_FALSE( options.connection_options().get_ssl_options().has_tls_ciphersuites()); EXPECT_FALSE(options.trace_protocol); EXPECT_TRUE(options.execute_statement.empty()); EXPECT_TRUE(options.wizards); EXPECT_FALSE( options.connection_options().has(mysqlshdk::db::kConnectTimeout)); EXPECT_EQ(Shell_options::Quiet_start::NOT_SET, options.quiet_start); EXPECT_FALSE(options.show_column_type_info); EXPECT_TRUE(!options.connection_options().has_compression()); EXPECT_FALSE(options.default_compress); EXPECT_TRUE(!options.connection_options().has_compression_algorithms()); EXPECT_TRUE(!options.connection_options().has_compression_level()); EXPECT_EQ("error", options.log_sql); EXPECT_EQ("*SELECT*:SHOW*", options.log_sql_ignore); EXPECT_EQ("*IDENTIFIED*:*PASSWORD*", options.log_sql_ignore_unsafe); } TEST_F(Shell_cmdline_options, app) { test_option_with_value("host", "h", "localhost", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("port", "P", "3306", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("schema", "D", "sakila", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("database", "", "sakila", "", IS_CONNECTION_DATA, !IS_NULLABLE, "schema"); test_option_with_value("user", "u", "root", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("dbuser", "u", "root", "", IS_CONNECTION_DATA, !IS_NULLABLE, "user"); #ifdef _WIN32 test_option_with_value("socket", "S", "/some/socket/path", "", IS_CONNECTION_DATA, !IS_NULLABLE, "sock"); #else test_option_with_value("socket", "S", "/some/socket/path", "", IS_CONNECTION_DATA, IS_NULLABLE, "sock"); #endif test_option_with_value("connect-timeout", "", "1000", "", !IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_no_value("-C", "compress", "REQUIRED"); test_option_with_no_value("--compress", "compress", "REQUIRED"); test_option_with_no_value("-p", "prompt_password", "1"); test_option_with_no_value("--password", "prompt_password", "1"); test_option_with_no_value("--password1", "prompt_password", "1"); test_option_equal_value("dbpassword", "mypwd", IS_CONNECTION_DATA, "password"); test_option_equal_value("password1", "mypwd", IS_CONNECTION_DATA, "password"); test_option_equal_value("password2", "mypwd", IS_CONNECTION_DATA); test_option_equal_value("password3", "mypwd", IS_CONNECTION_DATA); test_option_space_value("dbpassword", "mypwd", IS_CONNECTION_DATA, "password", ""); test_option_space_value("dbpassword", "mypwd", IS_CONNECTION_DATA, "prompt_password", "1"); test_option_space_value("password1", "mypwd", IS_CONNECTION_DATA, "prompt_password", "1"); test_option_space_value("password2", "mypwd", IS_CONNECTION_DATA); test_option_space_value("password3", "mypwd", IS_CONNECTION_DATA); test_short_option_value("dbpassword", "p", "mypwd", IS_CONNECTION_DATA, "password"); test_short_option_space_value("dbpassword", "p", "mypwd", IS_CONNECTION_DATA, "password", ""); test_short_option_space_value("dbpassword", "p", "mypwd", IS_CONNECTION_DATA, "prompt_password", "1"); test_option_equal_no_value("dbpassword", true); test_option_with_no_value("--dbpassword", "prompt_password", "1"); test_option_with_value("ssl-ca", "", "some_value", "", IS_CONNECTION_DATA, false); test_option_with_value("ssl-cert", "", "some_value", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("ssl-key", "", "some_value", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("tls-ciphersuites", "", "some_value", "", IS_CONNECTION_DATA, !IS_NULLABLE); test_option_with_value("execute", "e", "show databases;", "", !IS_CONNECTION_DATA, !IS_NULLABLE, "execute_statement"); test_option_with_no_value("--mysql", "session-type", session_type_name(mysqlsh::SessionType::Classic)); test_option_with_no_value("--mysqlx", "session-type", session_type_name(mysqlsh::SessionType::X)); test_option_with_no_value("--sql", "session-type", session_type_name(mysqlsh::SessionType::Auto)); test_option_with_no_value("--sql", "initial-mode", shell_mode_name(IShell_core::Mode::SQL)); test_option_with_no_value("--sqlc", "session-type", session_type_name(mysqlsh::SessionType::Classic)); test_option_with_no_value("--sqlc", "initial-mode", shell_mode_name(IShell_core::Mode::SQL)); test_option_with_no_value("--sqlx", "session-type", session_type_name(mysqlsh::SessionType::X)); test_option_with_no_value("--sqlx", "initial-mode", shell_mode_name(IShell_core::Mode::SQL)); #ifdef HAVE_JS test_option_with_no_value("--javascript", "initial-mode", shell_mode_name(IShell_core::Mode::JavaScript)); test_option_with_no_value("--js", "initial-mode", shell_mode_name(IShell_core::Mode::JavaScript)); #endif test_option_with_no_value("--python", "initial-mode", shell_mode_name(IShell_core::Mode::Python)); test_option_with_no_value("--py", "initial-mode", shell_mode_name(IShell_core::Mode::Python)); test_option_with_no_value("--recreate-schema", "recreate_database", "1"); test_option_with_no_value("--table", "result_format", "table"); test_option_with_no_value("--tabbed", "result_format", "tabbed"); test_option_with_no_value("--vertical", "result_format", "vertical"); test_option_with_no_value("-E", "result_format", "vertical"); test_option_equal_value("result-format", "table", false, "result_format", "table"); test_option_equal_value("result-format", "tabbed", false, "result_format", "tabbed"); test_option_equal_value("result-format", "vertical", false, "result_format", "vertical"); test_option_equal_value("result-format", "json", false, "result_format", "json"); test_option_equal_value("result-format", "json/raw", false, "result_format", "json/raw"); test_option_with_no_value("--json", "wrap_json", "json"); test_option_equal_value("json", "pretty", false, "wrap_json", "json"); test_option_equal_value("json", "raw", false, "wrap_json", "json/raw"); test_option_equal_value("json", "off", false, "wrap_json", "off"); test_option_with_no_value("--trace-proto", "trace_protocol", "1"); test_option_with_no_value("--force", "force", "1"); test_option_with_no_value("--interactive", "interactive", "1"); test_option_with_no_value("-i", "interactive", "1"); test_option_with_no_value("--no-wizard", "wizards", "0"); test_option_with_no_value("--nw", "wizards", "0"); test_option_with_no_value("--quiet-start", "quiet-start", "1"); test_option_with_value("quiet-start", "", "2", "1", !IS_CONNECTION_DATA, IS_NULLABLE, "quiet-start", "2"); test_option_with_no_value("--column-type-info", "showColumnTypeInfo", "1"); test_option_with_value("interactive", "", "full", "1", !IS_CONNECTION_DATA, IS_NULLABLE, "interactive", "1"); // test_option_with_value("interactive", "", "full", "1", !IS_CONNECTION_DATA, // IS_NULLABLE, "full_interactive", "1"); test_option_with_no_value("--passwords-from-stdin", "passwords_from_stdin", "1"); test_option_with_value("file", "f", "/some/file", "", !IS_CONNECTION_DATA, !IS_NULLABLE, "run_file"); test_option_with_value("mysql-plugin-dir", "", "/some/path", "", !IS_CONNECTION_DATA, IS_NULLABLE, "mysqlPluginDir"); test_option_with_value_list( "log-sql", "", {"off", "error", "on", "all", "ALL", "unfiltered"}, "", false, false); test_option_equal_invalid_values("log-sql", {"filtered"}, "--log-sql: The log level value must be any " "of {off, error, on, all, unfiltered}.\n"); } TEST_F(Shell_cmdline_options, test_session_type_conflicts) { test_session_type_conflicts("--sqlc", "--sqlc", 0); test_session_type_conflicts("--sqlc", "--mysql", 0); test_session_type_conflicts("--sqlc", "--mysqlx", 1); test_session_type_conflicts("--sqlc", "--sqlx", 1); test_session_type_conflicts("--sqlx", "--sqlx", 0); test_session_type_conflicts("--sqlx", "--mysqlx", 0); test_session_type_conflicts("--sqlx", "--mysql", 1); test_session_type_conflicts("--sqlx", "--sqlc", 1); test_session_type_conflicts("--mx", "--mysqlx", 0); test_session_type_conflicts("--mysqlx", "--sqlx", 0); test_session_type_conflicts("--mx", "--mysql", 0); test_session_type_conflicts("--mysqlx", "--sqlc", 1); test_session_type_conflicts("--mysqlx", "--mysql", 0); test_session_type_conflicts("--mc", "--mysql", 0); test_session_type_conflicts("--mysql", "--sqlc", 0); test_session_type_conflicts("--mc", "--mysqlx", 0); test_session_type_conflicts("--mysql", "--sqlx", 1); test_session_type_conflicts("--mysql", "--mysqlx", 0); test_session_type_conflicts("-ma", "--mysql", 0); test_session_type_conflicts("--mysql", "-ma", 0); test_session_type_conflicts("-ma", "--mysqlx", 0); test_session_type_conflicts("--mysqlx", "-ma", 0); test_session_type_conflicts("--mc", "-ma", 0); test_session_type_conflicts("--mx", "-ma", 0); test_session_type_conflicts("mysql://root@localhost", "--sqlc", 0); test_session_type_conflicts("mysql://root@localhost", "--mysql", 0); test_session_type_conflicts("mysql://root@localhost", "-ma", 0); test_session_type_conflicts("mysqlx://root@localhost", "--sqlx", 0); test_session_type_conflicts("mysqlx://root@localhost", "--mysqlx", 0); test_session_type_conflicts("mysqlx://root@localhost", "-ma", 0); test_session_type_conflicts("mysql://root@localhost", "--sqlx", 1); test_session_type_conflicts("mysqlx://root@localhost", "--sqlc", 1); test_session_type_conflicts("mysql://root@localhost", "--mysqlx", 0); test_session_type_conflicts("mysqlx://root@localhost", "--mysql", 0); } TEST_F(Shell_cmdline_options, test_deprecated_arguments) { std::string firstArg, secondArg; { SCOPED_TRACE("TESTING: deprecated --node argument"); firstArg = "root@localhost:3301"; secondArg = "--node"; char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; // Redirect cout. std::streambuf *cout_backup = std::cout.rdbuf(); std::ostringstream cout; std::cout.rdbuf(cout.rdbuf()); Shell_options cmd_options(3, argv); // Restore old cout. std::cout.rdbuf(cout_backup); EXPECT_EQ(0, cmd_options.get().exit_code); EXPECT_STREQ(cmd_options.get().connection_options().as_uri().c_str(), "mysqlx://root@localhost:3301"); MY_EXPECT_OUTPUT_CONTAINS( "The --node option was deprecated, " "please use --mysqlx instead. (Option has been processed " "as --mysqlx).", cout.str()); } { SCOPED_TRACE("TESTING: deprecated --classic argument"); firstArg = "root@localhost:3301"; secondArg = "--classic"; char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; // Redirect cout. std::streambuf *cout_backup = std::cout.rdbuf(); std::ostringstream cout; std::cout.rdbuf(cout.rdbuf()); Shell_options cmd_options(3, argv); // Restore old cout. std::cout.rdbuf(cout_backup); EXPECT_EQ(0, cmd_options.get().exit_code); EXPECT_STREQ(cmd_options.get().connection_options().as_uri().c_str(), "mysql://root@localhost:3301"); MY_EXPECT_OUTPUT_CONTAINS( "The --classic option was deprecated, " "please use --mysql instead. (Option has been processed as " "--mysql).", cout.str()); } { SCOPED_TRACE("TESTING: deprecated --dbpassword argument"); firstArg = "root@localhost:3301"; secondArg = "--dbpassword=pass"; char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; // Redirect cout. std::streambuf *cout_backup = std::cout.rdbuf(); std::ostringstream cout; std::cout.rdbuf(cout.rdbuf()); Shell_options cmd_options(3, argv); // Restore old cout. std::cout.rdbuf(cout_backup); EXPECT_EQ(0, cmd_options.get().exit_code); EXPECT_STREQ(cmd_options.get().connection_options().as_uri().c_str(), "root@localhost:3301"); MY_EXPECT_OUTPUT_CONTAINS( "The --dbpassword option was deprecated, please use --password " "instead.", cout.str()); } { SCOPED_TRACE("TESTING: deprecated --dbuser argument"); firstArg = "root@localhost:3301"; secondArg = "--dbuser=root"; char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; // Redirect cout. std::streambuf *cout_backup = std::cout.rdbuf(); std::ostringstream cout; std::cout.rdbuf(cout.rdbuf()); Shell_options cmd_options(3, argv); // Restore old cout. std::cout.rdbuf(cout_backup); EXPECT_EQ(0, cmd_options.get().exit_code); EXPECT_STREQ(cmd_options.get().connection_options().as_uri().c_str(), "root@localhost:3301"); MY_EXPECT_OUTPUT_CONTAINS( "The --dbuser option was deprecated, please use --user instead.", cout.str()); } { SCOPED_TRACE("TESTING: deprecated --recreate-schema argument"); firstArg = "root@localhost:3301/some-schema"; secondArg = "--recreate-schema"; char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; // Redirect cout. std::streambuf *cout_backup = std::cout.rdbuf(); std::ostringstream cout; std::cout.rdbuf(cout.rdbuf()); Shell_options cmd_options(3, argv); // Restore old cout. std::cout.rdbuf(cout_backup); EXPECT_EQ(0, cmd_options.get().exit_code); EXPECT_STREQ(cmd_options.get().connection_options().as_uri().c_str(), "root@localhost:3301/some-schema"); MY_EXPECT_OUTPUT_CONTAINS("The --recreate-schema option was deprecated.", cout.str()); } } TEST_F(Shell_cmdline_options, test_positional_argument) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); std::string firstArg, secondArg; // Using a correct uri should yield no error { SCOPED_TRACE("TESTING: uri as positional argument."); firstArg = "root@localhost:3301"; char *argv[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; Shell_options cmd_options(2, argv); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_STREQ(options.connection_options().as_uri().c_str(), "root@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); } // Using a correct uri plus the --uri option { SCOPED_TRACE( "TESTING: uri as positional argument followed by --uri option"); firstArg = "root@localhost:3301"; secondArg = "--uri=user2:pass@localhost"; char *argv2[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; Shell_options cmd_options(3, argv2); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_STREQ(options.connection_options() .as_uri(mysqlshdk::db::uri::formats::full()) .c_str(), "user2:pass@localhost"); EXPECT_STREQ("", cerr.str().c_str()); } { SCOPED_TRACE( "TESTING: --uri option followed by uri as positional argument"); firstArg = "--uri=user2@localhost"; secondArg = "root:pass@localhost:3301"; char *argv3[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), const_cast<char *>(secondArg.c_str()), NULL}; Shell_options cmd_options(3, argv3); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(0, options.exit_code); EXPECT_STREQ(options.connection_options() .as_uri(mysqlshdk::db::uri::formats::full()) .c_str(), "root:pass@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); } // Using an invalid uri as positional argument { SCOPED_TRACE("TESTING: invalid uri as positional argument"); firstArg = "not:valid_uri"; char *argv4[] = {const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; Shell_options cmd_options(2, argv4); const Shell_options::Storage &options = cmd_options.get(); EXPECT_EQ(1, options.exit_code); EXPECT_STREQ("Invalid URI: Illegal character [v] found at position 4\n", cerr.str().c_str()); } // Restore old cerr. std::cerr.rdbuf(backup); } TEST_F(Shell_cmdline_options, override_session_type) { // first option then uri { char uri[] = "--uri=mysqlx://root@localhost"; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--mysql"), uri, NULL}; test_overriding_options("--mysql --uri", 3, argv0, "session-type", "mysqlx"); } return; { char uri[] = "--uri=mysqlx://root@localhost"; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--sqlc"), uri, NULL}; test_conflicting_options( "--mysql --uri", 3, argv0, "URI scheme mysqlx:// cannot be combined with option --sqlc\n"); } return; { char uri[] = "--uri=mysql://root@localhost"; char *argv1[] = {const_cast<char *>("ut"), const_cast<char *>("--mysql"), uri, NULL}; test_overriding_options("--mysql --uri", 3, argv1, "session-type", "mysql"); } { char uri[] = "--uri=mysql://root@localhost"; char *argv1[] = {const_cast<char *>("ut"), const_cast<char *>("--sqlx"), uri, NULL}; test_conflicting_options( "--mysql --uri", 3, argv1, "URI scheme mysql:// cannot be combined with option --sqlx\n"); } // first uri then option { char uri[] = "--uri=mysqlx://root@localhost"; char *argv0[] = {const_cast<char *>("ut"), uri, const_cast<char *>("--mysql"), NULL}; test_overriding_options("--mysql --uri", 3, argv0, "session-type", "mysql"); } { char uri[] = "--uri=mysqlx://root@localhost"; char *argv0[] = {const_cast<char *>("ut"), uri, const_cast<char *>("--sqlc"), NULL}; test_conflicting_options( "--mysql --uri", 3, argv0, "Option --sqlc cannot be combined with URI scheme mysqlx://\n"); } { char uri[] = "--uri=mysql://root@localhost"; char *argv1[] = {const_cast<char *>("ut"), uri, const_cast<char *>("--mysqlx"), NULL}; test_overriding_options("--mysql --uri", 3, argv1, "session-type", "mysqlx"); } { char uri[] = "--uri=mysql://root@localhost"; char *argv1[] = {const_cast<char *>("ut"), uri, const_cast<char *>("--sqlx"), NULL}; test_conflicting_options( "--mysql --uri", 3, argv1, "Option --sqlx cannot be combined with URI scheme mysql://\n"); } } TEST_F(Shell_cmdline_options, override_user) { char uri[] = "--uri=mysqlx://root@localhost"; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--user=guest"), uri, NULL}; test_overriding_options("--user --uri", 3, argv0, "user", "root"); char *argv1[] = {const_cast<char *>("ut"), uri, const_cast<char *>("--user=guest"), NULL}; test_overriding_options("--user --uri", 3, argv1, "user", "guest"); } TEST_F(Shell_cmdline_options, override_password) { { char pwd[] = {"--password=example"}; char uri[] = {"--uri=mysqlx://root:password@localhost"}; char *argv0[] = {const_cast<char *>("ut"), pwd, uri, NULL}; test_overriding_options("--password --uri", 3, argv0, "password", "password"); } { char pwd[] = {"--password=example"}; char uri[] = {"--uri=mysqlx://root:password@localhost"}; char *argv1[] = {const_cast<char *>("ut"), uri, pwd, NULL}; test_overriding_options("--password --uri", 3, argv1, "password", "example"); } } TEST_F(Shell_cmdline_options, override_host) { char uri[] = "--uri=mysqlx://root:password@localhost"; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--host=127.0.0.1"), uri, NULL}; test_overriding_options("--host --uri", 3, argv0, "host", "localhost"); char *argv1[] = {const_cast<char *>("ut"), uri, const_cast<char *>("--host=127.0.0.1"), NULL}; test_overriding_options("--host --uri", 3, argv1, "host", "127.0.0.1"); } TEST_F(Shell_cmdline_options, conflicts_output) { auto error = "Conflicting options: resultFormat cannot be set to 'table' when --json " "option implying 'json' value is used.\n"; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--json"), const_cast<char *>("--table"), NULL}; test_conflicting_options("--json --table", 3, argv0, error); char *argv1[] = {const_cast<char *>("ut"), const_cast<char *>("--table"), const_cast<char *>("--json"), NULL}; test_conflicting_options("--table --json", 3, argv1, error); char *argv2[] = {const_cast<char *>("ut"), const_cast<char *>("--result-format=meh"), NULL}; test_conflicting_options("--result-format=meh", 2, argv2, "The acceptable values for the option " "--result-format are: table, tabbed, vertical, " "json, ndjson, json/raw, json/array, json/pretty\n"); } TEST_F(Shell_cmdline_options, override_port) { char uri[] = {"--uri=mysqlx://root:password@localhost:3307"}; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--port=3306"), uri, NULL}; test_overriding_options("--port --uri", 3, argv0, "port", "3307"); } TEST_F(Shell_cmdline_options, override_socket) { #ifdef _WIN32 char socket[] = "named.pipe"; char uri[] = "--uri=mysql://root@\\\\.\\named.pipe"; #else // !_WIN32 char socket[] = "/socket"; char uri[] = "--uri=mysqlx://root@/socket"; #endif // !_WIN32 char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--socket=/path/to/socket"), uri, NULL}; test_overriding_options("--socket --uri", 3, argv0, "sock", socket); } TEST_F(Shell_cmdline_options, override_port_and_socket) { // port overrides socket and vice-versa char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--port=3307"), const_cast<char *>("--socket=/some/weird/path"), NULL}; test_overriding_options("--port --socket", 3, argv0, "sock", "/some/weird/path"); #ifndef _WIN32 char *argv0b[] = {const_cast<char *>("ut"), const_cast<char *>("--port=3307"), const_cast<char *>("--socket"), NULL}; test_overriding_options("--port --socket", 3, argv0b, "sock", ""); #endif #ifdef _WIN32 char uri1[] = "--uri=root@\\\\.\\named.pipe"; #else // !_WIN32 char uri1[] = "--uri=root@/socket"; #endif // !_WIN32 char *argv1[] = {const_cast<char *>("ut"), uri1, const_cast<char *>("--port=3306"), NULL}; test_overriding_options("--uri --port", 3, argv1, "port", "3306"); char uri2[] = "--uri=root@localhost:3306"; char *argv2[] = {const_cast<char *>("ut"), uri2, const_cast<char *>("--socket=/some/socket/path"), NULL}; test_overriding_options("--uri --socket", 3, argv2, "sock", "/some/socket/path"); } TEST_F(Shell_cmdline_options, conflicts_compression) { char uri[] = {"--uri=mysqlx://root:password@localhost"}; char *argv0[] = {const_cast<char *>("ut"), const_cast<char *>("--compress"), const_cast<char *>("--compression-algorithms=uncompressed"), uri, NULL}; test_conflicting_options( "compressoon conflicts", 3, argv0, "Conflicting connection options: compression=REQUIRED, " "compression-algorithms=uncompressed.\n"); } TEST_F(Shell_cmdline_options, test_uri_with_password) { // Redirect cerr. std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); std::string firstArg, secondArg; SCOPED_TRACE("TESTING: user with password in uri"); firstArg = "--uri=root:pass@localhost:3301"; char *argv[]{const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; std::shared_ptr<Shell_options> options = std::make_shared<Shell_options>(2, argv); EXPECT_EQ(0, options->get().exit_code); EXPECT_STREQ(argv[1], "--uri=root@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); SCOPED_TRACE("TESTING: single letter user without password in uri"); firstArg = "--uri=r:@localhost:3301"; char *argv1[]{const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; options = std::make_shared<Shell_options>(2, argv1); EXPECT_EQ(0, options->get().exit_code); EXPECT_STREQ(argv1[1], "--uri=r@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); SCOPED_TRACE("TESTING: single letter user with password in uri"); firstArg = "--uri=r:pass@localhost:3301"; char *argv2[]{const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; options = std::make_shared<Shell_options>(2, argv2); EXPECT_EQ(0, options->get().exit_code); EXPECT_STREQ(argv2[1], "--uri=r@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); SCOPED_TRACE("TESTING: single letter user with single letter password"); firstArg = "--uri=r:r@localhost:3301"; char *argv3[]{const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; options = std::make_shared<Shell_options>(2, argv3); EXPECT_EQ(0, options->get().exit_code); EXPECT_STREQ(argv3[1], "--uri=r@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); SCOPED_TRACE("TESTING: user with password with special sign encoded in uri"); firstArg = "--uri=root:123%21@localhost:3301"; char *argv4[]{const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; options = std::make_shared<Shell_options>(2, argv4); EXPECT_EQ(0, options->get().exit_code); EXPECT_STREQ(argv4[1], "--uri=root@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); SCOPED_TRACE( "TESTING: anonymous uri with with password with special sign encoded"); firstArg = "root:123%21@localhost:3301"; char *argv5[]{const_cast<char *>("ut"), const_cast<char *>(firstArg.c_str()), NULL}; options = std::make_shared<Shell_options>(2, argv5); EXPECT_EQ(0, options->get().exit_code); EXPECT_STREQ(argv5[1], "root@localhost:3301"); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } TEST_F(Shell_cmdline_options, test_deprecated_ssl) { std::string warning = "The --ssl option was deprecated, please use --ssl-mode instead. "; { std::vector<char *> options = {const_cast<char *>("ut"), const_cast<char *>("--ssl"), const_cast<char *>("something"), NULL}; std::string error = "--ssl-mode must be any of [DISABLED, PREFERRED, REQUIRED, VERIFY_CA, " "VERIFY_IDENTITY]\n"; test_deprecated_ssl("--ssl=something", &options, "", error, 1, mysqlshdk::db::Ssl_mode::Preferred); // This last param is // ignored on this case } { std::vector<char *> options = {const_cast<char *>("ut"), const_cast<char *>("--ssl"), NULL}; std::string mywarning(warning); mywarning.append("(Option has been processed as --ssl-mode=REQUIRED)."); test_deprecated_ssl("--ssl", &options, mywarning, "", 0, mysqlshdk::db::Ssl_mode::Required); } { std::vector<char *> options = {const_cast<char *>("ut"), const_cast<char *>("--ssl=1"), NULL}; std::string mywarning(warning); mywarning.append("(Option has been processed as --ssl-mode=REQUIRED)."); test_deprecated_ssl("--ssl=1", &options, mywarning, "", 0, mysqlshdk::db::Ssl_mode::Required); } { std::vector<char *> options = {const_cast<char *>("ut"), const_cast<char *>("--ssl=yes"), NULL}; std::string mywarning(warning); mywarning.append("(Option has been processed as --ssl-mode=REQUIRED)."); test_deprecated_ssl("--ssl=yes", &options, mywarning, "", 0, mysqlshdk::db::Ssl_mode::Required); } { std::vector<char *> options = {const_cast<char *>("ut"), const_cast<char *>("--ssl=0"), NULL}; std::string mywarning(warning); mywarning.append("(Option has been processed as --ssl-mode=DISABLED)."); test_deprecated_ssl("--ssl=0", &options, mywarning, "", 0, mysqlshdk::db::Ssl_mode::Disabled); } { std::vector<char *> options = {const_cast<char *>("ut"), const_cast<char *>("--ssl=no"), NULL}; std::string mywarning(warning); mywarning.append("(Option has been processed as --ssl-mode=DISABLED)."); test_deprecated_ssl("--ssl=no", &options, mywarning, "", 0, mysqlshdk::db::Ssl_mode::Disabled); } } TEST_F(Shell_cmdline_options, dict_access_for_named_options) { Shell_options options(0, nullptr); for (const std::string &optname : options.get_named_options()) { EXPECT_NO_THROW(options.get(optname)); } } TEST_F(Shell_cmdline_options, invalid_connect_timeout) { auto invalid_values = {"-1", "10.0", "whatever"}; for (auto value : invalid_values) { std::string option("--connect-timeout="); option.append(value); char *argv[]{const_cast<char *>("ut"), const_cast<char *>(option.c_str()), NULL}; std::string error("Invalid value '"); error.append(value); error.append( "' for 'connect-timeout'. The connection timeout value must " "be a positive integer (including 0).\n"); std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); Shell_options opts(3, argv); EXPECT_EQ(1, opts.get().exit_code); EXPECT_STREQ(error.c_str(), cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); } } TEST_F(Shell_cmdline_options, conflicts_execute_and_file) { static constexpr auto error = "Conflicting options: --execute and --file cannot be used at the same " "time.\n"; { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("-e"), const_cast<char *>("SELECT 1"), const_cast<char *>("-f"), const_cast<char *>("select_2.sql"), nullptr}; test_conflicting_options("-e -f", 5, argv, error); } { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("--execute"), const_cast<char *>("SELECT 1"), const_cast<char *>("-f"), const_cast<char *>("select_2.sql"), nullptr}; test_conflicting_options("--execute -f", 5, argv, error); } { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("-e"), const_cast<char *>("SELECT 1"), const_cast<char *>("--file"), const_cast<char *>("select_2.sql"), nullptr}; test_conflicting_options("-e --file", 5, argv, error); } { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("--execute"), const_cast<char *>("SELECT 1"), const_cast<char *>("--file"), const_cast<char *>("select_2.sql"), nullptr}; test_conflicting_options("--execute --file", 5, argv, error); } } TEST_F(Shell_cmdline_options, test_file_and_execute) { const auto test_options = [](const std::string &context, size_t argc, char *argv[]) { std::streambuf *backup = std::cerr.rdbuf(); std::ostringstream cerr; std::cerr.rdbuf(cerr.rdbuf()); SCOPED_TRACE("TESTING: " + context); Shell_options options(argc, argv); EXPECT_EQ(0, options.get().exit_code); EXPECT_STREQ("", cerr.str().c_str()); // Restore old cerr. std::cerr.rdbuf(backup); }; // if --file is before --execute then there's no conflict between these two, // as all command line options after --file are used as options of the // processed file { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("-f"), const_cast<char *>("select_2.sql"), const_cast<char *>("-e"), const_cast<char *>("SELECT 1"), nullptr}; test_options("-f -e", 5, argv); } { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("-f"), const_cast<char *>("select_2.sql"), const_cast<char *>("--execute"), const_cast<char *>("SELECT 1"), nullptr}; test_options("-f --execute", 5, argv); } { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("--file"), const_cast<char *>("select_2.sql"), const_cast<char *>("-e"), const_cast<char *>("SELECT 1"), nullptr}; test_options("--file -e", 5, argv); } { char *argv[] = { const_cast<char *>("ut"), const_cast<char *>("--file"), const_cast<char *>("select_2.sql"), const_cast<char *>("--execute"), const_cast<char *>("SELECT 1"), nullptr}; test_options("--file --execute", 5, argv); } } TEST_F(Shell_cmdline_options, mfa_tests) { { // Password in --password1 differs from password in URI char uri[] = {"--uri=mysqlx://root:password@localhost"}; char wrong_pwd1[] = {"--password1=example"}; char *argv0[] = {const_cast<char *>("ut"), uri, wrong_pwd1, NULL}; test_overriding_options("Different passwords", 3, argv0, "password", "example"); } { // Password 1 and X Protocol is OK char uri1[] = "mysqlx://root@localhost"; char pwd1[] = {"--password1=example"}; char *argv0[] = {const_cast<char *>("ut"), uri1, pwd1, NULL}; Shell_options so(3, argv0); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); } { char *passwords[] = {const_cast<char *>("--password2=whatever"), const_cast<char *>("--password3=whatever")}; char *x_proto_args[] = {const_cast<char *>("--mysqlx"), const_cast<char *>("--mx"), const_cast<char *>("--sqlx")}; // MFA and X Protocol are not OK for (size_t index = 0; index < 2; index++) { for (size_t pindex = 0; pindex < 2; pindex++) { char uri0[] = "root@localhost"; char *argv0[] = {const_cast<char *>("ut"), uri0, passwords[pindex], x_proto_args[index], NULL}; test_conflicting_options("Different passwords 2", 4, argv0, "Multi-factor authentication is only " "compatible with MySQL protocol\n"); } } } } TEST_F(Shell_cmdline_options, mycnf) { make_mycnf(R"*([mysqlsh] user=rooot host=localhost mysqlx )*"); { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], NULL}; Shell_options so(2, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_EQ("rooot", so.get().connection_options().get_user()); EXPECT_EQ("mysqlx", so.get().connection_options().get_scheme()); } make_mycnf(R"*([client] user=rooot host=localhost mysqlx )*"); { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], NULL}; Shell_options so(2, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_EQ("rooot", so.get().connection_options().get_user()); EXPECT_EQ("mysqlx", so.get().connection_options().get_scheme()); } } TEST_F(Shell_cmdline_options, mycnf_default_session_type) { // check that we'll default to mysql:// if there's any connection option // in my.cnf make_mycnf(R"*([mysqlsh] user=rooot host=localhost )*"); { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], const_cast<char *>("--mysqlx"), NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_EQ("rooot", so.get().connection_options().get_user()); EXPECT_EQ("mysqlx", so.get().connection_options().get_scheme()); } { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], const_cast<char *>("--sqlx"), NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_EQ("rooot", so.get().connection_options().get_user()); EXPECT_EQ("mysqlx", so.get().connection_options().get_scheme()); } } TEST_F(Shell_cmdline_options, mycnf_option_override) { // override socket with port make_mycnf(R"*([mysqlsh] user=rooot socket=/sock/et )*"); { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], NULL}; Shell_options so(2, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_FALSE(so.get().connection_options().has_port()); EXPECT_EQ("/sock/et", so.get().connection_options().get_socket()); } { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], const_cast<char *>("--port=3232"), NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_EQ(3232, so.get().connection_options().get_port()); EXPECT_FALSE(so.get().connection_options().has_socket()); } { char uri[] = "root@localhost:3333"; char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], uri, NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_EQ(3333, so.get().connection_options().get_port()); EXPECT_FALSE(so.get().connection_options().has_socket()); } { char uri[] = "root@localhost"; char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], uri, NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_FALSE(so.get().connection_options().has_port()); EXPECT_FALSE(so.get().connection_options().has_socket()); } // override port with socket make_mycnf(R"*([mysqlsh] user=rooot host=localhost port=3232 )*"); { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], NULL}; Shell_options so(2, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_FALSE(so.get().connection_options().has_socket()); EXPECT_EQ(3232, so.get().connection_options().get_port()); } { char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], const_cast<char *>("--socket=/tmp/sock"), NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_FALSE(so.get().connection_options().has_port()); EXPECT_EQ("/tmp/sock", so.get().connection_options().get_socket()); } { char uri[] = "root@/tmp%2Fsock"; char *argv0[] = {const_cast<char *>("ut"), &defaults_file[0], uri, NULL}; Shell_options so(3, argv0, "", mysqlsh::Shell_options::Option_flags_set( mysqlsh::Shell_options::Option_flags::READ_MYCNF)); EXPECT_EQ(0, so.get().exit_code); EXPECT_NO_THROW(so.get().connection_options()); EXPECT_FALSE(so.get().connection_options().has_port()); EXPECT_EQ("/tmp/sock", so.get().connection_options().get_socket()); } } } // namespace shcore