unittest/interactive_shell_t.cc (2,798 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 <cstdio> #include "unittest/gprod_clean.h" #include "src/mysqlsh/cmdline_shell.h" #include "unittest/test_utils.h" #include "utils/utils_file.h" #include "utils/utils_general.h" extern mysqlshdk::utils::Version g_target_server_version; extern bool g_test_parallel_execution; extern "C" const char *g_test_home; namespace mysqlsh { class Interactive_shell_test : public Shell_core_test_wrapper { protected: #ifdef HAVE_JS const std::string to_scripting = "\\js"; #else const std::string to_scripting = "\\py"; #endif public: virtual void set_options() { _options->interactive = true; _options->wizards = true; } static void SetUpTestCase() { run_script_classic({"drop user if exists expired@localhost"}); } }; // This test verifies the shell starts in the requested mode TEST_F(Interactive_shell_test, startup_modes) { testutil->call_mysqlsh_c( {_uri, "--sql", "--vertical", "-e", "show databases"}); MY_EXPECT_STDOUT_CONTAINS("Database: mysql"); wipe_all(); testutil->call_mysqlsh_c( {_uri, "--js", "-e", "shell.dumpRows(session.runSql('show databases'), 'vertical')"}); MY_EXPECT_STDOUT_CONTAINS("Database: mysql"); wipe_all(); testutil->call_mysqlsh_c( {_uri, "--py", "-e", "shell.dump_rows(session.run_sql('show databases'), 'vertical')"}); MY_EXPECT_STDOUT_CONTAINS("Database: mysql"); wipe_all(); } TEST_F(Interactive_shell_test, test_quit_command) { testutil->call_mysqlsh_c( {"-ifull"}, "\\quit\n", {"MYSQLSH_PROMPT_THEME=" + shcore::get_binary_folder() + "/prompt_classic.json"}); MY_EXPECT_MULTILINE_OUTPUT("Testing \\quit", multiline({"mysql-js> \\quit", "Bye!"}), output_handler.std_out); wipe_all(); testutil->call_mysqlsh_c( {"-ifull"}, "\\q\n", {"MYSQLSH_PROMPT_THEME=" + shcore::get_binary_folder() + "/prompt_classic.json"}); MY_EXPECT_MULTILINE_OUTPUT("Testing \\q", multiline({"mysql-js> \\q", "Bye!"}), output_handler.std_out); wipe_all(); testutil->call_mysqlsh_c( {"-ifull"}, "\\exit\n", {"MYSQLSH_PROMPT_THEME=" + shcore::get_binary_folder() + "/prompt_classic.json"}); MY_EXPECT_MULTILINE_OUTPUT("Testing \\exit", multiline({"mysql-js> \\exit", "Bye!"}), output_handler.std_out); wipe_all(); } TEST_F(Interactive_shell_test, test_swicth_mode_commands) { testutil->call_mysqlsh_c( {"-ifull"}, "\\sql\n", {"MYSQLSH_PROMPT_THEME=" + shcore::get_binary_folder() + "/prompt_classic.json"}); MY_EXPECT_MULTILINE_OUTPUT( "Testing \\sql", multiline({"mysql-js> \\sql", "Switching to SQL mode... Commands end with ;", "mysql-sql> Bye!"}), output_handler.std_out); wipe_all(); testutil->call_mysqlsh_c( {"-ifull"}, "\\py\n", {"MYSQLSH_PROMPT_THEME=" + shcore::get_binary_folder() + "/prompt_classic.json"}); MY_EXPECT_MULTILINE_OUTPUT( "Testing \\py", multiline( {"mysql-js> \\py", "Switching to Python mode...", "mysql-py> Bye!"}), output_handler.std_out); wipe_all(); testutil->call_mysqlsh_c( {"--sql", "-ifull"}, "\\js\n", {"MYSQLSH_PROMPT_THEME=" + shcore::get_binary_folder() + "/prompt_classic.json"}); MY_EXPECT_MULTILINE_OUTPUT( "Testing \\js", multiline({"mysql-sql> \\js", "Switching to JavaScript mode...", "mysql-js> Bye!"}), output_handler.std_out); wipe_all(); } TEST_F(Interactive_shell_test, test_use_system_user) { testutil->call_mysqlsh_c( {"-i", "--host", "localhost", "--passwords-from-stdin"}, "whatever"); auto user = shcore::get_system_user(); MY_EXPECT_STDOUT_CONTAINS("Please provide the password for '" + user + "@localhost'"); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + user + "@localhost'"); wipe_all(); } TEST_F(Interactive_shell_test, shell_get_session_BUG27809310) { EXPECT_NO_THROW(execute("shell.getSession()")); } TEST_F(Interactive_shell_test, shell_command_connect_node) { execute("\\connect --mx " + _uri); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("(X protocol)"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); EXPECT_STREQ("", output_handler.std_err.c_str()); execute("session.close()"); execute("\\connect --mx " + _uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("(X protocol)"); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("db"); MY_EXPECT_STDOUT_CONTAINS("<Schema:mysql>"); output_handler.wipe_all(); execute("session.close()"); execute("\\connect --mx mysql://" + _mysql_uri); MY_EXPECT_STDERR_EMPTY(); output_handler.wipe_all(); execute("\\connect --mx " + _mysql_uri); // wrong protocol can manifest as this error or a 2006 'gone away' error if (output_handler.std_err.find("MySQL Error 2006") == std::string::npos) MY_EXPECT_STDERR_CONTAINS( "Requested session assumes MySQL X Protocol but '" + _host + ":" + _mysql_port + "' seems to speak the classic MySQL protocol"); output_handler.wipe_all(); // Invalid user/password output_handler.passwords.push_back({"*", "whatever", {}}); execute("\\connect --mx " + _uri_nopasswd); if (g_target_server_version >= mysqlshdk::utils::Version(8, 0, 12)) { MY_EXPECT_STDERR_CONTAINS("Access denied for user 'root'@'localhost'"); } else { MY_EXPECT_STDERR_CONTAINS("MySQL Error 1045: Invalid user or password"); } output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_connect_classic) { execute("\\connect --mc " + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); EXPECT_STREQ("", output_handler.std_err.c_str()); execute("session.close()"); execute("\\connect --mc " + _mysql_uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("db"); output_handler.wipe_all(); execute("session.close()"); execute("\\connect --mc mysqlx://" + _uri); MY_EXPECT_STDERR_NOT_CONTAINS( "URI scheme mysqlx:// cannot be combined with option --mc"); output_handler.wipe_all(); // FR_EXTRA_SUCCEED_7 : \connect --mc mysql://user@host:3306/db { execute("\\connect --mc mysql://" + _mysql_uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // FR_EXTRA_SUCCEED_8 : \c --mc mysql://user@host:3306/db { execute("\\c --mc mysql://" + _mysql_uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } } TEST_F(Interactive_shell_test, shell_command_connect_x) { // FR_EXTRA_SUCCEED_11 : \connect --mx mysqlx://user@host:33060/db { execute("\\connect --mx mysqlx://" + _uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // FR_EXTRA_SUCCEED_12 : \c --mx mysqlx://user@host:33060/db { execute("\\c --mx mysqlx://" + _uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } } TEST_F(Interactive_shell_test, shell_command_connect_auto) { // Session type determined from connection success execute("\\connect " + _uri); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); // Session type determined from connection success { execute("\\connect " + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Session type determined by the URI scheme { execute("\\connect mysql://" + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // FR_EXTRA_SUCCEED_5 : \connect -ma mysql://user@host:3306/db { execute("\\connect -ma mysql://" + _mysql_uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // FR_EXTRA_SUCCEED_6 : "\c -ma mysql://user@host:3306/db { execute("\\c -ma mysql://" + _mysql_uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // FR_EXTRA_SUCCEED_9 : \connect -ma mysqlx://user@host:33060/db { execute("\\connect -ma mysqlx://" + _uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // FR_EXTRA_SUCCEED_10 : \c -ma mysqlx://user@host:33060/db { execute("\\c -ma mysqlx://" + _uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Session type determined by the URI scheme { execute("\\connect mysqlx://" + _uri); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("(X protocol)"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Using -ma for X protocol session(type determined from connection success) { execute("\\connect -ma " + _uri); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("(X protocol)"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Using -ma for Classic session(type determined from connection success) { execute("\\connect -ma " + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Using -ma for session type determined by the URI scheme(Classic session) { execute("\\connect -ma mysql://" + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Using -ma for session type determined by the URI scheme(X protocol session) { execute("\\connect -ma mysqlx://" + _uri); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("(X protocol)"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } #ifndef _WIN32 { auto data = shcore::get_connection_options(_uri); std::string user = data.get_user(); data.clear_host(); data.clear_port(); // Check for URI syntax with socket path (classic) if (!_mysql_socket.empty()) { data.set_socket(_mysql_socket); // \connect --mc user@/path%2Fto%2Fsocket { execute("\\connect --mc " + data.as_uri(mysqlshdk::db::uri::formats::full())); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to "); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS( "<ClassicSession:" + data.as_uri(mysqlshdk::db::uri::formats::full_no_password()) + ">"); output_handler.wipe_all(); execute("session.close()"); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:disconnected>"); output_handler.wipe_all(); } // \connect user@/path%2Fto%2Fsocket { execute("\\connect " + data.as_uri(mysqlshdk::db::uri::formats::full())); MY_EXPECT_STDOUT_CONTAINS("Creating a session to "); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS( "<ClassicSession:" + data.as_uri(mysqlshdk::db::uri::formats::full_no_password()) + ">"); output_handler.wipe_all(); execute("session.close()"); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:disconnected>"); output_handler.wipe_all(); } // \connect user@/path%2Fto%2Fsocket // using classic socket as X protocol connection must fail { execute("\\connect --mx " + data.as_uri(mysqlshdk::db::uri::formats::full())); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to "); // wrong protocol can manifest as this error or a 2006 'gone away' error if (output_handler.std_err.find("MySQL Error 2006") == std::string::npos) { MY_EXPECT_STDERR_CONTAINS("MySQL Error 2027"); MY_EXPECT_STDERR_CONTAINS( "Requested session assumes MySQL X Protocol but"); MY_EXPECT_STDERR_CONTAINS( "seems to speak the classic MySQL protocol"); output_handler.wipe_all(); } execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:disconnected>"); output_handler.wipe_all(); } } // Check for URI syntax with xsocket path (x protocol) if (!_socket.empty()) { data.clear_socket(); data.set_socket(_socket); // \connect --mx user@/path%2Fto%2Fx_socket { execute("\\connect --mx " + data.as_uri(mysqlshdk::db::uri::formats::full())); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to "); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS( "<Session:" + data.as_uri(mysqlshdk::db::uri::formats::full_no_password()) + ">"); output_handler.wipe_all(); execute("session.close()"); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:disconnected>"); output_handler.wipe_all(); } // \connect user@/path%2Fto%2Fx_socket { execute("\\connect " + data.as_uri(mysqlshdk::db::uri::formats::full())); MY_EXPECT_STDOUT_CONTAINS("Creating a session to "); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS( "<Session:" + data.as_uri(mysqlshdk::db::uri::formats::full_no_password()) + ">"); output_handler.wipe_all(); execute("session.close()"); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:disconnected>"); output_handler.wipe_all(); } // \connect --mc user@/path%2Fto%2Fx_socket // using x socket as classic connection must fail // Test omitted due to long time to discover wrong protocol } } #endif // !_WIN32 } TEST_F(Interactive_shell_test, shell_function_connect_node) { execute("shell.connect('mysqlx://" + _uri + "');"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); execute("session.close()"); execute("shell.connect('mysqlx://" + _uri + "/mysql');"); MY_EXPECT_STDOUT_CONTAINS("Creating an X protocol session to '" + _uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("(X protocol)"); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("db"); MY_EXPECT_STDOUT_CONTAINS("<Schema:mysql>"); output_handler.wipe_all(); execute("session.close()"); } TEST_F(Interactive_shell_test, shell_function_connect_classic) { execute("shell.connect('mysql://" + _mysql_uri + "');"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); execute("session.close()"); execute("shell.connect('mysql://" + _mysql_uri + "/mysql');"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to '" + _mysql_uri_nopasswd + "/mysql'"); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("session.close()"); } TEST_F(Interactive_shell_test, shell_function_connect_auto) { // Session type determined from connection success { execute("shell.connect('" + _uri + "');"); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + _uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<Session:" + _uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } // Session type determined from connection success { execute("shell.connect('" + _mysql_uri + "');"); MY_EXPECT_STDOUT_CONTAINS("Creating a session to '" + _mysql_uri_nopasswd + "'"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("session"); MY_EXPECT_STDOUT_CONTAINS("<ClassicSession:" + _mysql_uri_nopasswd); output_handler.wipe_all(); execute("session.close()"); } } TEST_F(Interactive_shell_test, shell_command_connect_no_parameters) { execute("\\connect"); MY_EXPECT_STDERR_CONTAINS( "\\connect [--mx|--mysqlx|--mc|--mysql] [--ssh <sshuri>] <URI>\n"); output_handler.wipe_all(); execute("\\connect "); MY_EXPECT_STDERR_CONTAINS( "\\connect [--mx|--mysqlx|--mc|--mysql] [--ssh <sshuri>] <URI>\n"); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_connect_conflicts) { execute("\\connect --ssh root@example.com localhost"); MY_EXPECT_STDERR_CONTAINS( "Host and port for database are required in advance when using SSH " "tunneling functionality\n"); output_handler.wipe_all(); execute("\\connect --ssh --mx " + _uri); MY_EXPECT_STDERR_CONTAINS("option --ssh requires an argument"); output_handler.wipe_all(); execute("\\connect --ssh --mysql " + _uri); MY_EXPECT_STDERR_CONTAINS("option --ssh requires an argument"); output_handler.wipe_all(); // No port is specified and default port can not be determined given // the connection parameters execute("\\connect --ssh root@example.com root@example.com"); MY_EXPECT_STDERR_CONTAINS( "Host and port for database are required in advance when using SSH " "tunneling functionality\n"); output_handler.wipe_all(); // No port is specified BUT default port can be determined given // the connection parameters (scheme is given) execute("\\connect --ssh root@example.com --mysql root@example.com"); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic session to 'root@example.com'"); MY_EXPECT_STDOUT_CONTAINS("Opening SSH tunnel to example.com:22..."); // Not validating a specific error since they vary depending on where the test // is executed MY_EXPECT_STDERR_CONTAINS("Cannot open SSH Tunnel:"); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_connect_deprecated_options) { // Deprecated is not the same as removed, so these are supposed to still // work in 8.0 execute("\\connect -mx " + _uri); MY_EXPECT_STDERR_CONTAINS( "WARNING: The -mx option was deprecated, please use --mx instead."); MY_EXPECT_STDOUT_CONTAINS("Creating an X"); output_handler.wipe_all(); execute("\\connect -mc " + _mysql_uri); MY_EXPECT_STDERR_CONTAINS( "WARNING: The -mc option was deprecated, please use --mc instead."); MY_EXPECT_STDOUT_CONTAINS("Creating a Classic"); output_handler.wipe_all(); execute("\\connect -ma " + _mysql_uri); MY_EXPECT_STDERR_CONTAINS("WARNING: The -ma option was deprecated."); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_use) { execute("\\use mysql"); MY_EXPECT_STDERR_CONTAINS("Not connected."); output_handler.wipe_all(); execute("\\connect " + _uri); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("\\use mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("<Schema:mysql>\n", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("\\use unexisting"); MY_EXPECT_STDERR_CONTAINS("Unknown database 'unexisting'"); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("<Schema:mysql>\n", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("session.close()"); execute("\\connect --mx " + _uri); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("\\use mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema `mysql` accessible through db."); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("<Schema:mysql>\n", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("session.close()"); execute("\\connect --mc " + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("\\use mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("", output_handler.std_out.c_str()); output_handler.wipe_all(); execute("session.close()"); execute("\\sql"); execute("\\connect --mc " + _mysql_uri); execute("use mysql; select 'sabracadabra';"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); MY_EXPECT_STDOUT_CONTAINS("sabracadabra"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("USE information_schema;"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `information_schema`."); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("Use mysql;"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("uSE information_schema"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `information_schema`."); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("\\history"); MY_EXPECT_STDOUT_CONTAINS("uSE information_schema"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("use \\w mysql"); MY_EXPECT_STDERR_CONTAINS("Incorrect number of arguments for use command"); output_handler.wipe_all(); execute("create schema `backtick``quote`;"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("use `backtick``quote`;"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `backtick`quote`."); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("drop schema `backtick``quote`;"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("create schema \"double\"\"quote\";"); MY_EXPECT_STDERR_CONTAINS( "You have an error in your SQL syntax; check the manual " "that corresponds to your MySQL server version for the right syntax to " "use near '\"double\"\"quote\"' at line 1"); output_handler.wipe_all(); execute("SET SESSION SQL_MODE=\"ANSI_QUOTES\";"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("create schema \"double\"\"quote\";"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("SET SESSION SQL_MODE=\"\";"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("use \"double\"\"quote\";"); MY_EXPECT_STDERR_CONTAINS("Unknown database '\"double\"\"quote\"'"); EXPECT_FALSE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("SET SESSION SQL_MODE=\"ANSI_QUOTES\";"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("use \"double\"\"quote\";"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `double\"quote`."); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("drop schema \"double\"\"quote\";"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); execute("set SESSION SQL_MODE=@@GLOBAL.SQL_MODE;"); EXPECT_TRUE(output_handler.std_err.empty()); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_sql_use) { _options->interactive = true; _options->db_name_cache = true; reset_shell(); // check that SQL use command is overriden and will trigger // internal actions, like updating the completion cache and setting the db // variable execute("\\sql"); output_handler.wipe_all(); execute("use mysql"); MY_EXPECT_STDERR_CONTAINS("Not connected."); output_handler.wipe_all(); execute("use mysql;"); MY_EXPECT_STDERR_CONTAINS("Not connected."); output_handler.wipe_all(); execute("\\connect " + _uri); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("use mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`"); MY_EXPECT_STDOUT_CONTAINS( "Fetching global names, object names from `mysql` for " "auto-completion..."); output_handler.wipe_all(); execute("use mysql;"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`"); MY_EXPECT_STDOUT_CONTAINS( "Fetching global names, object names from `mysql` for " "auto-completion..."); output_handler.wipe_all(); execute(to_scripting); output_handler.wipe_all(); execute("db"); EXPECT_STREQ("<Schema:mysql>\n", output_handler.std_out.c_str()); execute("\\sql"); output_handler.wipe_all(); execute("use unexisting"); MY_EXPECT_STDERR_CONTAINS("Unknown database 'unexisting'"); output_handler.wipe_all(); execute("use unexisting;"); MY_EXPECT_STDERR_CONTAINS("Unknown database 'unexisting'"); output_handler.wipe_all(); execute("\\connect --mx " + _uri); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("use mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`"); output_handler.wipe_all(); execute("\\connect --mc " + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); execute("use mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`."); MY_EXPECT_STDOUT_CONTAINS( "Fetching global names, object names from `mysql` for " "auto-completion..."); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_warnings) { _options->interactive = true; reset_shell(); execute("\\warnings"); MY_EXPECT_STDOUT_CONTAINS("Show warnings enabled."); output_handler.wipe_all(); execute("\\W"); MY_EXPECT_STDOUT_CONTAINS("Show warnings enabled."); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_no_warnings) { _options->interactive = true; reset_shell(); execute("\\nowarnings"); MY_EXPECT_STDOUT_CONTAINS("Show warnings disabled."); output_handler.wipe_all(); execute("\\w"); MY_EXPECT_STDOUT_CONTAINS("Show warnings disabled."); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_empty_source_command) { _interactive_shell->process_line("\\source"); MY_EXPECT_STDERR_CONTAINS("Filename not specified"); output_handler.wipe_all(); } #ifdef HAVE_JS TEST_F(Interactive_shell_test, shell_command_source_invalid_path_js) { _interactive_shell->process_line("\\js"); // directory std::string tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "."; _interactive_shell->process_line("\\source " + tmpdir); MY_EXPECT_STDERR_CONTAINS("Failed to open file: '" + tmpdir + "' is a directory"); output_handler.wipe_all(); std::string filename = "empty_file_" + random_string(10); // no such file _interactive_shell->process_line("\\source " + filename); MY_EXPECT_STDERR_CONTAINS("Failed to open file '" + filename + "', error: No such file or directory"); output_handler.wipe_all(); } #endif TEST_F(Interactive_shell_test, shell_command_source_invalid_path_py) { _interactive_shell->process_line("\\py"); // directory std::string tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "."; _interactive_shell->process_line("\\source " + tmpdir); MY_EXPECT_STDERR_CONTAINS("Failed to open file: '" + tmpdir + "' is a directory"); output_handler.wipe_all(); std::string filename = "empty_file_" + random_string(10); // no such file _interactive_shell->process_line("\\source " + filename); MY_EXPECT_STDERR_CONTAINS("Failed to open file '" + filename + "', error: No such file or directory"); output_handler.wipe_all(); } TEST_F(Interactive_shell_test, shell_command_source_incomplete_files) { std::vector<std::tuple<std::string, std::string, std::string>> data = { {"\\js", R"*(function sample(data) { print(data); )*", "Expected } but found eof"}, {"\\py", R"*(def sample(data): print(data)*", "SyntaxError: "}, {"\\sql", R"*(select * from)*", "ERROR: 1064: You have an error in your SQL syntax;"}}; execute("\\connect --mx " + _uri); for (const auto &item : data) { const auto &mode = std::get<0>(item); _interactive_shell->process_line(mode); // directory std::string tmpdir = getenv("TMPDIR") ? getenv("TMPDIR") : "."; std::string filename = shcore::path::join_path(tmpdir, "srctest." + mode.substr(2)); shcore::create_file(filename, std::get<1>(item)); _interactive_shell->process_line("\\source " + filename); MY_EXPECT_STDERR_CONTAINS(std::get<2>(item)); shcore::delete_file(filename); output_handler.wipe_all(); } execute("\\disconnect"); } TEST_F(Interactive_shell_test, python_startup_scripts) { if (g_test_parallel_execution) { SKIP_TEST( "This test is writing to the share folder, skipping for parallel " "execution."); } const auto user_path = shcore::path::join_path(shcore::get_user_config_path(), "mysqlshrc.py"); // User Config path is executed last std::string user_backup; bool user_existed = shcore::load_text_file(user_path, user_backup, false); std::ofstream out; out.open(user_path, std::ios_base::trunc); if (!out.fail()) { out << "the_variable = 'Local Value'" << std::endl; out.close(); } const auto home_path = shcore::get_mysqlx_home_path(); const auto bin_path = home_path.empty() ? shcore::path::join_path(shcore::get_binary_folder(), "mysqlshrc.py") : shcore::path::join_path(home_path, "share", "mysqlsh", "mysqlshrc.py"); // Binary Config path is executed first std::string bin_backup; bool bin_existed = shcore::load_text_file(bin_path, user_backup, false); out.open(bin_path, std::ios_base::app); if (!out.fail()) { out << "from mysqlsh import mysqlx" << std::endl; out << "the_variable = 'Global Value'" << std::endl; out.close(); } _options->full_interactive = true; _options->initial_mode = shcore::IShell_core::Mode::Python; reset_shell(); output_handler.wipe_all(); execute("mysqlx"); MY_EXPECT_STDOUT_CONTAINS("<module 'mysqlsh.mysqlx' (built-in)>"); output_handler.wipe_all(); execute("the_variable"); if (bin_path == user_path) { // When running tests, if MYSQLSH_USER_CONFIG_HOME environment variable is // not set, it is explicitly initialized to the binary folder. If // shcore::get_mysqlx_home_path() returns an empty value, we'll end up with // both variables pointing to the same location, with the `bin_path` one // created last. MY_EXPECT_STDOUT_CONTAINS("Global Value"); } else { MY_EXPECT_STDOUT_CONTAINS("Local Value"); } output_handler.wipe_all(); std::remove(user_path.c_str()); std::remove(bin_path.c_str()); // If there startup files on the paths used on this test, they are restored if (user_existed) { out.open(user_path, std::ios_base::trunc); if (!out.fail()) { out.write(user_backup.c_str(), user_backup.size()); out.close(); } } if (bin_existed) { out.open(bin_path, std::ios_base::trunc); if (!out.fail()) { out.write(bin_backup.c_str(), bin_backup.size()); out.close(); } } } #ifdef HAVE_JS TEST_F(Interactive_shell_test, js_startup_scripts) { if (g_test_parallel_execution) { SKIP_TEST( "This test is writing to the share folder, skipping for parallel " "execution."); } const auto user_path = shcore::path::join_path(shcore::get_user_config_path(), "mysqlshrc.js"); // User Config path is executed last std::string user_backup; bool user_existed = shcore::load_text_file(user_path, user_backup); std::ofstream out; out.open(user_path, std::ios_base::trunc); if (!out.fail()) { out << "var the_variable = 'Local Value';" << std::endl; out.close(); } const auto home_path = shcore::get_mysqlx_home_path(); const auto bin_path = home_path.empty() ? shcore::path::join_path(shcore::get_binary_folder(), "mysqlshrc.js") : shcore::path::join_path(home_path, "share", "mysqlsh", "mysqlshrc.js"); // Binary Config path is executed first std::string bin_backup; bool bin_existed = shcore::load_text_file(bin_path, user_backup); out.open(bin_path, std::ios_base::app); if (!out.fail()) { out << "var mysqlx = require('mysqlx');" << std::endl; out << "var the_variable = 'Global Value';" << std::endl; out.close(); } _options->full_interactive = true; reset_shell(); output_handler.wipe_all(); execute("dir(mysqlx)"); MY_EXPECT_STDOUT_CONTAINS("getSession"); output_handler.wipe_all(); execute("the_variable"); if (bin_path == user_path) { // When running tests, if MYSQLSH_USER_CONFIG_HOME environment variable is // not set, it is explicitly initialized to the binary folder. If // shcore::get_mysqlx_home_path() returns an empty value, we'll end up with // both variables pointing to the same location, with the `bin_path` one // created last. MY_EXPECT_STDOUT_CONTAINS("Global Value"); } else { MY_EXPECT_STDOUT_CONTAINS("Local Value"); } output_handler.wipe_all(); std::remove(user_path.c_str()); std::remove(bin_path.c_str()); // If there startup files on the paths used on this test, they are restored if (user_existed) { out.open(user_path, std::ios_base::trunc); if (!out.fail()) { out.write(user_backup.c_str(), user_backup.size()); out.close(); } } if (bin_existed) { out.open(bin_path, std::ios_base::trunc); if (!out.fail()) { out.write(bin_backup.c_str(), bin_backup.size()); out.close(); } } } #endif TEST_F(Interactive_shell_test, expired_account_support_classic) { // Test secure call passing uri with no password (will be prompted) _options->set_uri(_mysql_uri); _options->initial_mode = shcore::IShell_core::Mode::SQL; reset_shell(); // Setup an expired account for the test _interactive_shell->connect(_options->connection_options()); output_handler.wipe_all(); execute("drop user if exists expired;"); output_handler.wipe_all(); execute("create user expired@'%' identified by 'sample';"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute("grant all on *.* to expired@'%';"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute("alter user expired@'%' password expire;"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute(to_scripting); execute("session.close()"); execute("\\sql"); // Connects with the expired account execute("\\c mysql://expired:sample@localhost:" + _mysql_port); MY_EXPECT_STDOUT_CONTAINS( "Creating a Classic session to 'expired@localhost:" + _mysql_port + "'"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); // Tests unable to execute any statement with an expired account execute("select host from mysql.user where user = 'expired';"); MY_EXPECT_STDERR_CONTAINS( "ERROR: 1820 (HY000): You must reset your password using ALTER USER " "statement before executing this statement."); output_handler.wipe_all(); // Tests allow reseting the password on an expired account execute("ALTER USER expired@'%' IDENTIFIED BY 'updated';"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute("select host from mysql.user where user = 'expired';"); MY_EXPECT_STDOUT_CONTAINS("1 row in set"); output_handler.wipe_all(); execute(to_scripting); execute("session.close()"); execute("\\sql"); execute("\\c " + _mysql_uri); execute("drop user if exists expired;"); execute(to_scripting); execute("session.close()"); MY_EXPECT_STDOUT_CONTAINS(""); } TEST_F(Interactive_shell_test, expired_account_support_node) { // Test secure call passing uri with no password (will be prompted) _options->set_uri(_uri); _options->initial_mode = shcore::IShell_core::Mode::SQL; reset_shell(); // Setup an expired account for the test _interactive_shell->connect(_options->connection_options()); output_handler.wipe_all(); execute("drop user if exists expired;"); output_handler.wipe_all(); execute("create user expired@'%' identified by 'sample';"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute("grant all on *.* to expired@'%';"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute("alter user expired@'%' password expire;"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute(to_scripting); execute("session.close()"); execute("\\sql"); // Connects with the expired account execute("\\c mysqlx://expired:sample@localhost:" + _port); MY_EXPECT_STDOUT_CONTAINS( "Creating an X protocol session to 'expired@localhost:" + _port + "'"); MY_EXPECT_STDOUT_CONTAINS( "No default schema selected; type \\use <schema> to set one."); output_handler.wipe_all(); // Tests unable to execute any statement with an expired account execute("select host from mysql.user where user = 'expired';"); MY_EXPECT_STDERR_CONTAINS( "ERROR: 1820: You must reset your password using ALTER USER statement " "before executing this statement."); output_handler.wipe_all(); // Tests allow reseting the password on an expired account execute("ALTER USER expired@'%' IDENTIFIED BY 'updated';"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); output_handler.wipe_all(); execute("select host from mysql.user where user = 'expired';"); MY_EXPECT_STDOUT_CONTAINS("1 row in set"); output_handler.wipe_all(); execute(to_scripting); execute("session.close()"); execute("\\sql"); execute("\\c " + _uri); execute("drop user if exists expired;"); execute(to_scripting); execute("session.close()"); MY_EXPECT_STDOUT_CONTAINS(""); } TEST_F(Interactive_shell_test, classic_sql_result) { execute("\\connect " + _mysql_uri); execute("\\sql"); execute("drop schema if exists itst;"); execute("create schema itst;"); // Regression test for // Bug#26406214 WRONG VALUE FOR SMALLINT ZEROFILL COLUMNS HOLDING NULL VALUE execute( "create table itst.tbl (a int, b varchar(30), c int(10), " " d int(8) unsigned zerofill, e int(2) zerofill, " " f smallint unsigned zerofill, ggggg tinyint unsigned zerofill, " " h bigint unsigned zerofill, i double unsigned zerofill);"); execute( "insert into itst.tbl values (1, 'one', -42, 42, 42, 42, 42, 42, 42), " " (2, 'two', -12345, 12345, 12345, 12345, 123, 12345, 12345)," " (3, 'three', 0, 0, 0, 0, 0, 0, 0)," " (4, 'four', null, null, null, null, null, null, null);"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("select 1, 'two', 3.3, null;"); MY_EXPECT_STDOUT_CONTAINS( "+---+-----+-----+------+\n" "| 1 | two | 3.3 | NULL |\n" "+---+-----+-----+------+\n" "| 1 | two | 3.3 | NULL |\n" "+---+-----+-----+------+\n" "1 row in set ("); wipe_all(); // test zerofill execute("select * from itst.tbl;"); // clang-format off MY_EXPECT_MULTILINE_OUTPUT( "test zerofill", multiline({ "+---+-------+--------+----------+-------+-------+-------+----------------------+------------------------+", "| a | b | c | d | e | f | ggggg | h | i |", "+---+-------+--------+----------+-------+-------+-------+----------------------+------------------------+", "| 1 | one | -42 | 00000042 | 42 | 00042 | 042 | 00000000000000000042 | 0000000000000000000042 |", "| 2 | two | -12345 | 00012345 | 12345 | 12345 | 123 | 00000000000000012345 | 0000000000000000012345 |", "| 3 | three | 0 | 00000000 | 00 | 00000 | 000 | 00000000000000000000 | 0000000000000000000000 |", "| 4 | four | NULL | NULL | NULL | NULL | NULL | NULL | NULL |", // NOLINT "+---+-------+--------+----------+-------+-------+-------+----------------------+------------------------+", // NOLINT }), output_handler.std_out.c_str()); // clang-format on execute(to_scripting); execute("shell.options['resultFormat']='vertical'"); execute("\\sql"); wipe_all(); execute("select * from itst.tbl;"); MY_EXPECT_STDOUT_CONTAINS( "*************************** 1. row ***************************\n" " a: 1\n" " b: one\n" " c: -42\n" " d: 00000042\n" " e: 42\n" " f: 00042\n" "ggggg: 042\n" " h: 00000000000000000042\n" " i: 0000000000000000000042\n" "*************************** 2. row ***************************\n" " a: 2\n" " b: two\n" " c: -12345\n" " d: 00012345\n" " e: 12345\n" " f: 12345\n" "ggggg: 123\n" " h: 00000000000000012345\n" " i: 0000000000000000012345\n" "*************************** 3. row ***************************\n" " a: 3\n" " b: three\n" " c: 0\n" " d: 00000000\n" " e: 00\n" " f: 00000\n" "ggggg: 000\n" " h: 00000000000000000000\n" " i: 0000000000000000000000\n" "*************************** 4. row ***************************\n" " a: 4\n" " b: four\n" " c: NULL\n" " d: NULL\n" " e: NULL\n" " f: NULL\n" "ggggg: NULL\n" " h: NULL\n" " i: NULL\n" "4 rows in set"); execute(to_scripting); execute("shell.options['resultFormat']='json'"); execute("\\sql"); wipe_all(); execute("select * from itst.tbl;"); MY_EXPECT_STDOUT_CONTAINS( "{\n" " \"a\": 1,\n" " \"b\": \"one\",\n" " \"c\": -42,\n" " \"d\": 42,\n" " \"e\": 42,\n" " \"f\": 42,\n" " \"ggggg\": 42,\n" " \"h\": 42,\n" " \"i\": 42\n" "}\n" "{\n" " \"a\": 2,\n" " \"b\": \"two\",\n" " \"c\": -12345,\n" " \"d\": 12345,\n" " \"e\": 12345,\n" " \"f\": 12345,\n" " \"ggggg\": 123,\n" " \"h\": 12345,\n" " \"i\": 12345\n" "}\n" "{\n" " \"a\": 3,\n" " \"b\": \"three\",\n" " \"c\": 0,\n" " \"d\": 0,\n" " \"e\": 0,\n" " \"f\": 0,\n" " \"ggggg\": 0,\n" " \"h\": 0,\n" " \"i\": 0\n" "}\n" "{\n" " \"a\": 4,\n" " \"b\": \"four\",\n" " \"c\": null,\n" " \"d\": null,\n" " \"e\": null,\n" " \"f\": null,\n" " \"ggggg\": null,\n" " \"h\": null,\n" " \"i\": null\n" "}\n"); execute("drop schema itst;"); } TEST_F(Interactive_shell_test, x_sql_result) { execute("\\connect " + _uri); execute("\\sql"); execute("drop schema if exists itst;"); execute("create schema itst;"); execute( "create table itst.tbl (a int, b varchar(30), c int(10), " " d int(8) unsigned zerofill, e int(2) zerofill, " " f smallint unsigned zerofill, ggggg tinyint unsigned zerofill, " " h bigint unsigned zerofill, i double unsigned zerofill);"); execute( "insert into itst.tbl values (1, 'one', -42, 42, 42, 42, 42, 42, 42), " " (2, 'two', -12345, 12345, 12345, 12345, 123, 12345, 12345)," " (3, 'three', 0, 0, 0, 0, 0, 0, 0)," " (4, 'four', null, null, null, null, null, null, null);"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("select 1, 'two', 3.3, null;"); MY_EXPECT_STDOUT_CONTAINS( "+---+-----+-----+------+\n" "| 1 | two | 3.3 | NULL |\n" "+---+-----+-----+------+\n" "| 1 | two | 3.3 | NULL |\n" "+---+-----+-----+------+\n" "1 row in set ("); wipe_all(); // test zerofill execute("select * from itst.tbl;"); // NOTE: X protocol does not support zerofill for double atm // clang-format off MY_EXPECT_STDOUT_CONTAINS( "+---+-------+--------+----------+-------+-------+-------+----------------------+-------+\n" "| a | b | c | d | e | f | ggggg | h | i |\n" "+---+-------+--------+----------+-------+-------+-------+----------------------+-------+\n" "| 1 | one | -42 | 00000042 | 42 | 00042 | 042 | 00000000000000000042 | 42 |\n" "| 2 | two | -12345 | 00012345 | 12345 | 12345 | 123 | 00000000000000012345 | 12345 |\n" "| 3 | three | 0 | 00000000 | 00 | 00000 | 000 | 00000000000000000000 | 0 |\n" "| 4 | four | NULL | NULL | NULL | NULL | NULL | NULL | NULL |\n" "+---+-------+--------+----------+-------+-------+-------+----------------------+-------+\n"); // clang-format on execute(to_scripting); execute("shell.options['resultFormat']='vertical'"); execute("\\sql"); wipe_all(); execute("select * from itst.tbl;"); MY_EXPECT_STDOUT_CONTAINS( "*************************** 1. row ***************************\n" " a: 1\n" " b: one\n" " c: -42\n" " d: 00000042\n" " e: 42\n" " f: 00042\n" "ggggg: 042\n" " h: 00000000000000000042\n" " i: 42\n" "*************************** 2. row ***************************\n" " a: 2\n" " b: two\n" " c: -12345\n" " d: 00012345\n" " e: 12345\n" " f: 12345\n" "ggggg: 123\n" " h: 00000000000000012345\n" " i: 12345\n" "*************************** 3. row ***************************\n" " a: 3\n" " b: three\n" " c: 0\n" " d: 00000000\n" " e: 00\n" " f: 00000\n" "ggggg: 000\n" " h: 00000000000000000000\n" " i: 0\n" "*************************** 4. row ***************************\n" " a: 4\n" " b: four\n" " c: NULL\n" " d: NULL\n" " e: NULL\n" " f: NULL\n" "ggggg: NULL\n" " h: NULL\n" " i: NULL\n" "4 rows in set"); execute(to_scripting); execute("shell.options['resultFormat']='json'"); execute("\\sql"); wipe_all(); execute("select * from itst.tbl;"); MY_EXPECT_STDOUT_CONTAINS( "{\n" " \"a\": 1,\n" " \"b\": \"one\",\n" " \"c\": -42,\n" " \"d\": 42,\n" " \"e\": 42,\n" " \"f\": 42,\n" " \"ggggg\": 42,\n" " \"h\": 42,\n" " \"i\": 42\n" "}\n" "{\n" " \"a\": 2,\n" " \"b\": \"two\",\n" " \"c\": -12345,\n" " \"d\": 12345,\n" " \"e\": 12345,\n" " \"f\": 12345,\n" " \"ggggg\": 123,\n" " \"h\": 12345,\n" " \"i\": 12345\n" "}\n" "{\n" " \"a\": 3,\n" " \"b\": \"three\",\n" " \"c\": 0,\n" " \"d\": 0,\n" " \"e\": 0,\n" " \"f\": 0,\n" " \"ggggg\": 0,\n" " \"h\": 0,\n" " \"i\": 0\n" "}\n" "{\n" " \"a\": 4,\n" " \"b\": \"four\",\n" " \"c\": null,\n" " \"d\": null,\n" " \"e\": null,\n" " \"f\": null,\n" " \"ggggg\": null,\n" " \"h\": null,\n" " \"i\": null\n" "}\n"); execute("drop schema itst;"); } TEST_F(Interactive_shell_test, ssl_status) { if (g_target_server_version == mysqlshdk::utils::Version(8, 0, 4)) { PENDING_BUG_TEST("Caching_sha2 with xproto and no ssl is broken"); } else { wipe_all(); execute("\\connect " + _uri + "?ssl-Mode=DISABLED"); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("Not in use."); EXPECT_EQ("", _interactive_shell->prompt_variables()->at("ssl")); } wipe_all(); execute("\\connect " + _uri + "?ssl-Mode=REQUIRED"); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("Cipher in use: "); MY_EXPECT_STDOUT_CONTAINS("TLSv"); EXPECT_EQ("SSL", _interactive_shell->prompt_variables()->at("ssl")); wipe_all(); execute("\\connect " + _mysql_uri + "?ssl-Mode=DISABLED"); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("Not in use."); EXPECT_EQ("", _interactive_shell->prompt_variables()->at("ssl")); wipe_all(); execute("\\connect " + _mysql_uri + "?ssl-Mode=REQUIRED"); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("Cipher in use: "); MY_EXPECT_STDOUT_CONTAINS("TLSv"); EXPECT_EQ("SSL", _interactive_shell->prompt_variables()->at("ssl")); } TEST_F(Interactive_shell_test, status_x) { if (g_target_server_version == mysqlshdk::utils::Version(8, 0, 4)) { PENDING_BUG_TEST("Caching_sha2 with xproto and no ssl is broken"); return; } execute("\\connect " + _uri + "?ssl-Mode=DISABLED"); wipe_all(); execute("\\status"); char c = 0; int dec = 0; std::stringstream ss(output_handler.std_out); std::string line; ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "Connection Id: %d", &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Default schema")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Current schema")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Current user")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("SSL")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "Using delimiter: %c", &c)); EXPECT_EQ(c, ';'); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Server version")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(line, "Protocol version: X protocol"); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(3, sscanf(line.c_str(), "Client library: %d.%d.%d", &dec, &dec, &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Connection")); EXPECT_NE(std::string::npos, line.find("via TCP")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "TCP port: %d", &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Server characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Schema characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Client characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Conn. characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Result characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Compression")); if (g_target_server_version >= mysqlshdk::utils::Version(8, 0, 19)) EXPECT_NE(std::string::npos, line.find("Enabled")); else EXPECT_NE(std::string::npos, line.find("Disabled")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Uptime")); EXPECT_NE(std::string::npos, line.find("sec")); } TEST_F(Interactive_shell_test, status_classic) { execute("\\connect " + _mysql_uri + "?ssl-Mode=DISABLED&compression=1"); wipe_all(); execute("\\status"); char c = 0; int dec = 0; std::stringstream ss(output_handler.std_out); std::string line; ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "Connection Id: %d", &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Current schema")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Current user")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("SSL")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "Using delimiter: %c", &c)); EXPECT_EQ(c, ';'); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Server version")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "Protocol version: Compressed %d", &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(3, sscanf(line.c_str(), "Client library: %d.%d.%d", &dec, &dec, &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Connection")); EXPECT_NE(std::string::npos, line.find("via TCP")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_EQ(1, sscanf(line.c_str(), "TCP port: %d", &dec)); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Server characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Schema characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Client characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); ASSERT_NE(line.find("Conn. characterset"), std::string::npos); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Result characterset")); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); ASSERT_NE(line.find("Compression"), std::string::npos); ASSERT_NE(line.find("Enabled"), std::string::npos); ASSERT_TRUE(static_cast<bool>(std::getline(ss, line))); EXPECT_NE(std::string::npos, line.find("Uptime")); EXPECT_NE(std::string::npos, line.find("sec")); } TEST_F(Interactive_shell_test, status_ansi_quotes) { execute("\\connect " + _uri); wipe_all(); execute("\\sql"); MY_EXPECT_STDOUT_CONTAINS("Switching to SQL mode... Commands end with ;"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("set SESSION SQL_MODE=\"\";"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("MySQL Shell version"); MY_EXPECT_STDOUT_CONTAINS("Connection Id:"); MY_EXPECT_STDOUT_CONTAINS("TCP port:"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("set SESSION SQL_MODE=\"ANSI_QUOTES\";"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("MySQL Shell version"); MY_EXPECT_STDOUT_CONTAINS("Connection Id:"); MY_EXPECT_STDOUT_CONTAINS("TCP port:"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\connect " + _mysql_uri); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("set SESSION SQL_MODE=\"\";"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("MySQL Shell version"); MY_EXPECT_STDOUT_CONTAINS("Connection Id:"); MY_EXPECT_STDOUT_CONTAINS("TCP port:"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("set SESSION SQL_MODE=\"ANSI_QUOTES\";"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\status"); MY_EXPECT_STDOUT_CONTAINS("MySQL Shell version"); MY_EXPECT_STDOUT_CONTAINS("Connection Id:"); MY_EXPECT_STDOUT_CONTAINS("TCP port:"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); } TEST_F(Interactive_shell_test, reconnect_command) { execute("\\reconnect"); MY_EXPECT_STDERR_CONTAINS("enable reconnection"); wipe_all(); execute("\\reconnect dummy_arg"); MY_EXPECT_STDERR_CONTAINS("not accept"); wipe_all(); execute("\\connect " + _mysql_uri); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\reconnect"); MY_EXPECT_STDOUT_CONTAINS("successfully reconnected"); wipe_all(); execute("\\sql"); execute("kill CONNECTION_ID();"); MY_EXPECT_STDERR_CONTAINS("interrupted"); wipe_all(); execute("\\reconnect"); MY_EXPECT_STDOUT_CONTAINS("successfully reconnected"); wipe_all(); // connect to mysql schema execute("\\connect " + _mysql_uri + "/mysql"); MY_EXPECT_STDOUT_CONTAINS("Default schema set to `mysql`"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\use information_schema"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); // check current schema execute("\\sql select database();"); MY_EXPECT_STDOUT_CONTAINS("information_schema"); wipe_all(); execute("\\reconnect"); MY_EXPECT_STDOUT_CONTAINS("successfully reconnected"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); // check if schema was retained execute("\\sql select database();"); MY_EXPECT_STDOUT_CONTAINS("information_schema"); wipe_all(); // check reconnect when previous default schema was dropped. execute("\\sql"); execute("CREATE DATABASE IF NOT EXISTS dropped_schema_reconnect_test;"); execute("\\use dropped_schema_reconnect_test"); execute("DROP DATABASE dropped_schema_reconnect_test;"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\reconnect"); MY_EXPECT_STDOUT_CONTAINS( "WARNING: Unable to reconnect to default schema " "'dropped_schema_reconnect_test'"); MY_EXPECT_STDOUT_CONTAINS("Ignoring schema, attempting to reconnect to"); MY_EXPECT_STDOUT_CONTAINS("successfully reconnected"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); } TEST_F(Interactive_shell_test, disconnect_command) { execute("\\disconnect"); MY_EXPECT_STDERR_CONTAINS("Already disconnected."); wipe_all(); execute("\\disconnect dummy_arg"); MY_EXPECT_STDERR_CONTAINS( "\\disconnect command does not accept any arguments."); wipe_all(); execute("\\connect " + _mysql_uri); execute("\\use mysql"); ASSERT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("mysql", _interactive_shell->prompt_variables()->at("schema")); wipe_all(); execute("\\disconnect"); ASSERT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("", _interactive_shell->prompt_variables()->at("schema")); execute("\\status"); MY_EXPECT_STDERR_CONTAINS("Not Connected"); wipe_all(); } TEST_F(Interactive_shell_test, mod_shell_options) { execute("\\py"); ASSERT_TRUE(_options->wizards); execute("shell.options[\"useWizards\"]"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_NE(std::string::npos, output_handler.std_out.find("true")); execute("shell.options[\"useWizards\"] = False"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->wizards); _options->wizards = true; wipe_all(); execute("shell.options.set_persist(\"showWarnings\", False)"); EXPECT_EQ("", output_handler.std_err); EXPECT_FALSE(_options->show_warnings); wipe_all(); execute("shell.options.unset(\"showWarnings\");"); EXPECT_EQ("", output_handler.std_err); EXPECT_TRUE(_options->show_warnings); execute("shell.options.unset_persist(\"showWarnings\");"); EXPECT_EQ("", output_handler.std_err); EXPECT_TRUE(_options->show_warnings); #ifdef HAVE_JS execute("\\js"); wipe_all(); execute("shell.options"); auto named_opts = _opts->get_named_options(); for (const auto &name : named_opts) { MY_EXPECT_STDOUT_CONTAINS("\"" + name + "\":"); } wipe_all(); execute("shell.options.resultFormat = 'json'"); execute("shell.options"); for (const auto &name : named_opts) { MY_EXPECT_STDOUT_CONTAINS("\"" + name + "\":"); } reset_options(0, nullptr, false); reset_shell(); wipe_all(); EXPECT_TRUE(_options->show_warnings); execute("shell.options.showWarnings=false"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->show_warnings); execute("shell.options.set(\"showWarnings\", true)"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_TRUE(_options->show_warnings); reset_options(0, nullptr, false); reset_shell(); EXPECT_TRUE(_options->show_warnings); wipe_all(); execute("shell.options.setPersist(\"showWarnings\", false)"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->show_warnings); reset_options(0, nullptr, false); reset_shell(); EXPECT_FALSE(_options->show_warnings); wipe_all(); execute("shell.options.unset(\"showWarnings\");"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_TRUE(_options->show_warnings); reset_options(0, nullptr, false); reset_shell(); EXPECT_FALSE(_options->show_warnings); execute("shell.options.unsetPersist(\"showWarnings\");"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_TRUE(_options->show_warnings); reset_options(0, nullptr, false); reset_shell(); EXPECT_TRUE(_options->show_warnings); wipe_all(); #endif reset_options(); reset_shell(); } TEST_F(Interactive_shell_test, option_command) { wipe_all(); execute("\\option"); MY_EXPECT_STDERR_CONTAINS("Allows working with the available shell options."); wipe_all(); auto named_opts = _opts->get_named_options(); execute("\\option --list"); for (const auto &name : named_opts) { MY_EXPECT_STDOUT_CONTAINS(name); } MY_EXPECT_STDOUT_NOT_CONTAINS( to_string(shcore::opts::Source::Compiled_default)); wipe_all(); execute("\\option -l --show-origin"); for (const auto &name : named_opts) { MY_EXPECT_STDOUT_CONTAINS(name); } MY_EXPECT_STDOUT_CONTAINS(to_string(shcore::opts::Source::Compiled_default)); wipe_all(); execute("\\option -h"); for (const auto &name : named_opts) { MY_EXPECT_STDOUT_CONTAINS(name); } wipe_all(); execute("\\option -h " SHCORE_USE_WIZARDS); MY_EXPECT_STDOUT_CONTAINS(SHCORE_USE_WIZARDS); MY_EXPECT_STDOUT_NOT_CONTAINS(SHCORE_HISTIGNORE); wipe_all(); execute("\\option -h zzzzz"); MY_EXPECT_STDERR_CONTAINS("No help found for filter: zzzzz"); wipe_all(); for (const auto &name : named_opts) { execute("\\option " + name); MY_EXPECT_STDOUT_CONTAINS(_opts->get_value_as_string(name)); wipe_all(); } execute("\\option zzzzz"); MY_EXPECT_STDERR_CONTAINS("Unrecognized option: zzzzz"); wipe_all(); execute("\\option zzzzz=val"); MY_EXPECT_STDERR_CONTAINS("Unrecognized option: zzzzz"); wipe_all(); execute("\\option zzzzz 1"); MY_EXPECT_STDERR_CONTAINS("Unrecognized option: zzzzz"); wipe_all(); ASSERT_TRUE(_options->wizards); execute("\\option " SHCORE_USE_WIZARDS " false"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->wizards); wipe_all(); execute("\\option -l --show-origin"); MY_EXPECT_STDOUT_CONTAINS(to_string(shcore::opts::Source::User)); wipe_all(); execute("\\option --persist " SHCORE_USE_WIZARDS "=true"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_TRUE(_options->wizards); wipe_all(); const char *args[] = {"ut", "--show-warnings=false"}; reset_options(2, args, false); reset_shell(); EXPECT_TRUE(_options->wizards); execute("\\option -l --show-origin"); MY_EXPECT_STDOUT_CONTAINS( to_string(shcore::opts::Source::Configuration_file)); MY_EXPECT_STDOUT_CONTAINS(to_string(shcore::opts::Source::Command_line)); wipe_all(); execute("\\option " SHCORE_USE_WIZARDS " =false"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->wizards); wipe_all(); execute("\\option " SHCORE_USE_WIZARDS "= false"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->wizards); wipe_all(); execute("\\option " SHCORE_USE_WIZARDS " dummy"); EXPECT_FALSE(output_handler.std_err.empty()); EXPECT_FALSE(_options->wizards); wipe_all(); execute("\\option --unset --persist " SHCORE_USE_WIZARDS); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_TRUE(_options->wizards); wipe_all(); execute("\\option " SHCORE_USE_WIZARDS " = false"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_FALSE(_options->wizards); wipe_all(); EXPECT_EQ("", _options->pager); execute("\\option " SHCORE_PAGER " = less -E"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("less -E", _options->pager); wipe_all(); execute("\\option " SHCORE_PAGER " =more -E"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("more -E", _options->pager); wipe_all(); execute("\\option " SHCORE_PAGER "= less -E"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("less -E", _options->pager); wipe_all(); execute("\\option " SHCORE_PAGER "=more -E"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("more -E", _options->pager); wipe_all(); execute("\\option " SHCORE_PAGER " less -E"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("less -E", _options->pager); wipe_all(); execute("\\option --unset " SHCORE_PAGER); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_EQ("", _options->pager); wipe_all(); reset_options(0, nullptr, false); reset_shell(); EXPECT_TRUE(_options->wizards); execute("\\option -l --show-origin"); MY_EXPECT_STDOUT_NOT_CONTAINS( to_string(shcore::opts::Source::Configuration_file)); wipe_all(); execute("\\option dummy_variable=5"); EXPECT_FALSE(output_handler.std_err.empty()); wipe_all(); // value already unsaved execute("\\option -u " SHCORE_USE_WIZARDS); EXPECT_FALSE(output_handler.std_err.empty()); wipe_all(); // Try to assign empty value auto old_wiz = _options->wizards; execute("\\option " SHCORE_USE_WIZARDS "="); MY_EXPECT_STDERR_CONTAINS("Incorrect option value"); EXPECT_EQ(old_wiz, _options->wizards); wipe_all(); execute("\\option " SHCORE_PAGER "="); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_TRUE(_options->pager.empty()); wipe_all(); reset_options(); reset_shell(); } TEST_F(Interactive_shell_test, bug_28240437) { static constexpr auto select_output = R"(+---+ | 1 | +---+ | 1 | +---+ 1 row in set)"; wipe_all(); execute("\\sql"); MY_EXPECT_STDOUT_CONTAINS("Switching to SQL mode... Commands end with ;"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("select 1;"); EXPECT_EQ("", output_handler.std_out); MY_EXPECT_STDERR_CONTAINS("Not connected."); wipe_all(); execute("\\connect " + _mysql_uri); MY_EXPECT_STDOUT_CONTAINS("Your MySQL connection id is "); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("select 1;"); MY_EXPECT_STDOUT_CONTAINS(select_output); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute(to_scripting); #ifdef HAVE_JS MY_EXPECT_STDOUT_CONTAINS("Switching to JavaScript mode..."); #else MY_EXPECT_STDOUT_CONTAINS("Switching to Python mode..."); #endif EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("session.close();"); EXPECT_EQ("", output_handler.std_out); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("\\sql"); MY_EXPECT_STDOUT_CONTAINS("Switching to SQL mode... Commands end with ;"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("select 1;"); EXPECT_EQ("", output_handler.std_out); MY_EXPECT_STDERR_CONTAINS("Not connected."); wipe_all(); } TEST_F(Interactive_shell_test, invalid_command) { wipe_all(); execute("\\js"); wipe_all(); execute("\\invalid"); MY_EXPECT_STDERR_CONTAINS("Unknown command: '\\invalid'"); wipe_all(); execute("\\py"); wipe_all(); execute("\\invalid"); MY_EXPECT_STDERR_CONTAINS("Unknown command: '\\invalid'"); wipe_all(); execute("\\sql"); wipe_all(); execute("\\invalid"); MY_EXPECT_STDERR_CONTAINS("Unknown command: '\\invalid'"); wipe_all(); } TEST_F(Interactive_shell_test, multi_line_command) { #ifdef HAVE_JS wipe_all(); execute("\\js"); wipe_all(); execute("\\"); MY_EXPECT_STDERR_CONTAINS( "Expected an operand but found error (SyntaxError)"); #endif wipe_all(); execute("\\py"); wipe_all(); execute("\\"); EXPECT_TRUE(output_handler.std_err.empty()); execute("print(1 + 2)"); execute(""); MY_EXPECT_STDOUT_CONTAINS("3"); wipe_all(); execute("\\sql"); wipe_all(); execute("\\"); EXPECT_TRUE(output_handler.std_err.empty()); execute("select 1;"); execute(""); MY_EXPECT_STDERR_CONTAINS("ERROR: Not connected."); wipe_all(); } TEST_F(Interactive_shell_test, pager_command) { // get the initial pager execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); const auto default_pager = shcore::str_strip(output_handler.std_out); wipe_all(); // set pager to a command enclosed in quotes, they should be removed execute("\\pager \"more\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more", shcore::str_strip(output_handler.std_out)); wipe_all(); // reset pager to the initial value using command with no arguments execute("\\pager"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ(default_pager, shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command execute("\\pager more"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more", shcore::str_strip(output_handler.std_out)); wipe_all(); // reset pager to the initial value using command with an empty argument execute("\\pager \"\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ(default_pager, shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument, both enclosed in quotes execute("\\pager \"more -10\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument, the first one enclosed in quotes execute("\\pager \"more\" -10"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument, the second one enclosed in quotes execute("\\pager more \"-10\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument without using quotes execute("\\pager more -10"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument which is enclosed in quotes execute("\\pager \"some_cmd print \\\"hello\\\"\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("some_cmd print \"hello\"", shcore::str_strip(output_handler.std_out)); wipe_all(); reset_options(); } TEST_F(Interactive_shell_test, pager_short_command) { // get the initial pager execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); const auto default_pager = shcore::str_strip(output_handler.std_out); wipe_all(); // set pager to a command enclosed in quotes, they should be removed execute("\\P \"more\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more", shcore::str_strip(output_handler.std_out)); wipe_all(); // reset pager to the initial value using command with no arguments execute("\\P"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ(default_pager, shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command execute("\\P more"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more", shcore::str_strip(output_handler.std_out)); wipe_all(); // reset pager to the initial value using command with an empty argument execute("\\P \"\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ(default_pager, shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument, both enclosed in quotes execute("\\P \"more -10\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument, the first one enclosed in quotes execute("\\P \"more\" -10"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument, the second one enclosed in quotes execute("\\P more \"-10\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument without using quotes execute("\\pager more -10"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // set pager to a command with argument which is enclosed in quotes execute("\\P \"some_cmd print \\\"hello\\\"\""); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("some_cmd print \"hello\"", shcore::str_strip(output_handler.std_out)); wipe_all(); reset_options(); } TEST_F(Interactive_shell_test, nopager_command) { // set pager to sample command execute("\\pager more -10"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("more -10", shcore::str_strip(output_handler.std_out)); wipe_all(); // reset pager to an empty string execute("\\nopager"); EXPECT_EQ("", output_handler.std_err); wipe_all(); execute("print(shell.options.pager)"); EXPECT_EQ("", output_handler.std_err); EXPECT_EQ("", shcore::str_strip(output_handler.std_out)); wipe_all(); static constexpr auto expected_error = R"(NAME \nopager - Disables the current pager. SYNTAX \nopager)"; // nopager does not take any arguments, this should be an error execute("\\nopager more"); EXPECT_EQ(expected_error, output_handler.std_err); EXPECT_EQ("", output_handler.std_out); wipe_all(); reset_options(); } TEST_F(Interactive_shell_test, inline_commands) { execute("\\sql"); execute("\\connect " + _uri); wipe_all(); // ensure behaviour of inline \commands match old cli execute(";;"); EXPECT_EQ("", output_handler.std_out); EXPECT_EQ("ERROR: 1065: Query was empty\nERROR: 1065: Query was empty\n", output_handler.std_err); wipe_all(); execute("select 1\\w"); EXPECT_NE("", _interactive_shell->shell_context()->get_continued_input_context()); execute("\\g"); EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); MY_EXPECT_STDOUT_CONTAINS( "Show warnings disabled.\n" "+---+\n" "| 1 |\n" "+---+\n" "| 1 |\n" "+---+\n"); wipe_all(); execute("select 1\\w\\G"); EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); MY_EXPECT_STDOUT_CONTAINS( "Show warnings disabled.\n" "*************************** 1. row ***************************\n" "1: 1\n" "1 row in set"); wipe_all(); execute("select 1\\W\\w\\W;"); MY_EXPECT_STDOUT_CONTAINS( "Show warnings enabled.\n" "Show warnings disabled.\n" "Show warnings enabled.\n" "+---+\n" "| 1 |\n" "+---+\n" "| 1 |\n" "+---+\n" "1 row in set"); EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); wipe_all(); execute("\\W"); EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); MY_EXPECT_STDOUT_CONTAINS("Show warnings enabled."); wipe_all(); // \ at the beginning of stmt means a full line command execute("\\W\\w\\W"); EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); MY_EXPECT_STDERR_CONTAINS("Unknown command: '\\W\\w\\W'"); // \ after stmt means single letter escape commands wipe_all(); execute(";\\W\\w\\W"); EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); MY_EXPECT_STDOUT_CONTAINS( "Show warnings enabled.\n" "Show warnings disabled.\n" "Show warnings enabled."); wipe_all(); execute("\\l"); // invalid cmd EXPECT_EQ("", _interactive_shell->shell_context()->get_continued_input_context()); EXPECT_EQ("", output_handler.std_out); EXPECT_EQ("Unknown command: '\\l'", output_handler.std_err); // invalid delimiters wipe_all(); execute("delimiter"); EXPECT_EQ( "ERROR: DELIMITER must be followed by a 'delimiter' character or " "string\n", output_handler.std_out); wipe_all(); execute("delimiter$"); EXPECT_EQ( "ERROR: DELIMITER must be followed by a 'delimiter' character or " "string\n", output_handler.std_out); wipe_all(); execute("delimiter \\x"); EXPECT_EQ("ERROR: DELIMITER cannot contain a backslash character\n", output_handler.std_out); wipe_all(); execute("select 1;"); EXPECT_EQ("", output_handler.std_err); } // This tests is added for Bug #28894826 // EMPTY RESULTSET TABLES ARE PRINTED WHEN THERE ARE NO RESULTS TO DISPLAY #ifdef HAVE_JS TEST_F(Interactive_shell_test, not_empty_tables_js) { execute("\\connect " + _mysql_uri); wipe_all(); execute("session.runSql('select 0 from dual where 0')"); EXPECT_TRUE( shcore::str_beginswith(output_handler.std_out.c_str(), "Empty set")); execute("session.close()"); } TEST_F(Interactive_shell_test, not_empty_collections_js) { execute("\\connect " + _uri); wipe_all(); execute("var schema = session.createSchema('empty_collection')"); execute("var collection = schema.createCollection('empty')"); execute("collection.find()"); EXPECT_TRUE( shcore::str_beginswith(output_handler.std_out.c_str(), "Empty set")); execute("session.dropSchema('empty_collection')"); execute("session.close()"); } #else TEST_F(Interactive_shell_test, not_empty_tables_py) { execute("\\connect " + _mysql_uri); wipe_all(); execute("session.run_sql('select 0 from dual where 0')"); EXPECT_TRUE( shcore::str_beginswith(output_handler.std_out.c_str(), "Empty set")); execute("session.close()"); } TEST_F(Interactive_shell_test, not_empty_collections_py) { execute("\\connect " + _uri); wipe_all(); execute("schema = session.create_schema('empty_collection')"); execute("collection = schema.create_collection('empty')"); execute("collection.find()"); EXPECT_TRUE( shcore::str_beginswith(output_handler.std_out.c_str(), "Empty set")); execute("session.drop_schema('empty_collection')"); execute("session.close()"); } #endif TEST_F(Interactive_shell_test, compression) { ASSERT_FALSE(_options->default_compress); const auto check_compression = [this](const char *status, const char *algorithm = nullptr, const char *level = nullptr) { wipe_all(); execute("show session status like 'compression%';"); SCOPED_TRACE(output_handler.std_err.c_str()); EXPECT_TRUE(output_handler.std_err.empty()); MY_EXPECT_STDOUT_CONTAINS(status); if (algorithm) MY_EXPECT_STDOUT_CONTAINS(algorithm); if (level) MY_EXPECT_STDOUT_CONTAINS(level); execute("\\s"); if (strcmp(status, "ON") == 0) { MY_EXPECT_STDOUT_CONTAINS("Compression: Enabled"); if (algorithm) MY_EXPECT_STDOUT_CONTAINS(std::string("(") + algorithm + ")"); } else if (output_handler.std_out.find("Compression:") != std::string::npos) { MY_EXPECT_STDOUT_CONTAINS("Compression: Disabled"); } wipe_all(); }; const auto check_x_compression = [this](const char *status, const char *algorithm = nullptr, const char *level = nullptr) { if (_target_server_version >= mysqlshdk::utils::Version(8, 0, 19)) { execute( "select case when variable_value > 0 then 'ON' else 'OFF' end from " "performance_schema.session_status where variable_name = " "'Mysqlx_bytes_sent_compressed_payload';"); SCOPED_TRACE(output_handler.std_err.c_str()); EXPECT_TRUE(output_handler.std_err.empty()); MY_EXPECT_STDOUT_CONTAINS(status); } execute("\\s"); if (strcmp(status, "ON") == 0) MY_EXPECT_STDOUT_CONTAINS("Compression: Enabled"); else if (output_handler.std_out.find("Compression:") != std::string::npos) MY_EXPECT_STDOUT_CONTAINS("Compression: Disabled"); if (algorithm != nullptr) MY_EXPECT_STDOUT_CONTAINS(algorithm); if (level != nullptr) { wipe_all(); execute("show status like 'Mysqlx_compression_level';"); MY_EXPECT_STDOUT_CONTAINS(level); } wipe_all(); }; execute("shell.connect(\"" + _mysql_uri + "?compression=true\");"); execute("\\sql"); check_compression("ON"); execute("\\connect " + _mysql_uri + "?compression=true"); check_compression("ON"); execute("\\connect " + _mysql_uri); check_compression("OFF"); execute("\\option defaultCompress=true"); execute("\\connect " + _mysql_uri); check_compression("ON"); execute(to_scripting); execute("shell.connect(\"" + _mysql_uri + "\");"); execute("\\sql"); check_compression("ON"); execute("\\option defaultCompress=false"); execute("\\connect " + _mysql_uri); check_compression("OFF"); if (_target_server_version <= mysqlshdk::utils::Version(8, 0, 18)) { execute("\\connect " + _uri + "?compression=true"); check_x_compression("OFF"); } else { execute("\\connect " + _uri); check_x_compression("ON"); execute("\\connect " + _uri + "?compression=REQUIRED"); check_x_compression("ON"); execute("\\connect " + _uri + "?compression=DISABLED"); check_x_compression("OFF"); if (_target_server_version >= mysqlshdk::utils::Version(8, 0, 20)) { execute("\\connect " + _uri + "?compression-algorithms=zstd&compression-level=7"); check_x_compression("ON", "ZSTD_STREAM", "7"); execute("\\connect " + _uri + "?compression-algorithms=lz4&compression-level=4"); check_x_compression("ON", "LZ4_MESSAGE", "4"); execute("\\connect " + _uri + "?compression-algorithms=zlib,uncompressed&compression-level=4"); check_x_compression("ON", "DEFLATE_STREAM", "4"); } else { execute("\\connect " + _uri + "?compression-algorithms=zstd"); check_x_compression("ON"); execute("\\connect " + _uri + "?compression-algorithms=lz4"); check_x_compression("ON"); execute("\\connect " + _uri + "?compression-algorithms=zlib,uncompressed"); check_x_compression("ON"); } execute("\\connect " + _uri + "?compression-algorithms=uncompressed"); check_x_compression("OFF"); } if (_target_server_version >= mysqlshdk::utils::Version(8, 0, 18)) { execute("\\connect " + _mysql_uri + "?compression-algorithms=zstd&compression-level=7"); check_compression("ON", "zstd", "7"); execute("\\connect " + _mysql_uri + "?compression-algorithms=zlib,zstd"); check_compression("ON", "zlib"); execute("\\connect " + _mysql_uri + "?compression-algorithms=zstd,uncompressed&compression-level=19"); check_compression("ON", "zstd", "19"); // conflict execute("\\connect " + _mysql_uri + "?compression-algorithms=uncompressed&compression=required"); MY_EXPECT_STDERR_CONTAINS( "Conflicting connection options: compression=REQUIRED, " "compression-algorithms=uncompressed"); wipe_all(); execute(to_scripting); execute("shell.connect({\"user\": \"" + _user + "\", \"host\": \"" + _host + "\", \"port\": " + _mysql_port + ", \"password\": \"" + _pwd + "\", \"compression-algorithms\": \"zstd\", " "\"compression-level\": 14});"); execute("\\sql"); check_compression("ON", "zstd", "14"); wipe_all(); execute(to_scripting); execute("shell.connect({\"user\": \"" + _user + "\", \"host\": \"" + _host + "\", \"port\": " + _mysql_port + ", \"password\": \"" + _pwd + "\", \"compression\": \" 1 \");"); execute("\\sql"); check_compression("ON"); } } TEST_F(Interactive_shell_test, sql_command) { #ifdef HAVE_JS execute("\\js"); #else execute("\\py"); #endif execute("\\sql show databases;"); MY_EXPECT_STDERR_CONTAINS("ERROR: Not connected."); wipe_all(); execute("\\connect " + _uri); execute("\\sql create database if not exists sqlcommandtest"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 1 row affected"); wipe_all(); execute( "\\sql create table if not exists `sqlcommandtest`.t1 (i integer, s " "varchar(20));"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 0 rows affected"); wipe_all(); execute("\\use sqlcommandtest"); execute("\\sql insert into t1 values (0, \"foo\")"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 1 row affected"); wipe_all(); execute("\\sql select * from t1 where s=\"foo\";"); MY_EXPECT_STDOUT_CONTAINS("1 row in set"); wipe_all(); execute("\\sql select * from t1 where s=\"foo\"\\G"); MY_EXPECT_STDOUT_CONTAINS( "*************************** 1. row ***************************"); wipe_all(); execute("\\sql select d from t1;"); MY_EXPECT_STDERR_CONTAINS("Unknown column 'd' in 'field list'"); wipe_all(); execute(" \\sql show tables"); MY_EXPECT_STDOUT_CONTAINS("1 row in set"); wipe_all(); execute("\\sql show tables\\g"); MY_EXPECT_STDOUT_CONTAINS("1 row in set"); wipe_all(); execute("\\sql drop database sqlcommandtest\\G"); MY_EXPECT_STDOUT_CONTAINS("Query OK, 1 row affected"); wipe_all(); execute("\\sql show table"); MY_EXPECT_STDERR_CONTAINS("You have an error in your SQL syntax"); wipe_all(); execute("\\sql show databases; show tables;"); MY_EXPECT_STDERR_CONTAINS("You have an error in your SQL syntax"); wipe_all(); } TEST_F(Interactive_shell_test, ansi_quotes) { execute("\\sql"); execute("\\connect " + _uri); execute("set @old_mode=@@sql_mode;"); execute(R"(select "\"";select version();#";)"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); for (const std::string s : {"set @@session.sql_mode='ANSI_QUOTES';", "set LOCAL sql_mode='ANSI_QUOTES';", "set sql_mode = 'ANSI_QUOTES';"}) { execute(s); EXPECT_TRUE(output_handler.std_err.empty()); execute(R"(select "\"";select version();#";)"); EXPECT_NE(std::string::npos, output_handler.std_err.find( R"(Unknown column '\";select version();#')")); wipe_all(); execute("set sql_mode=@old_mode;"); execute(R"(select "\"";select version();#";)"); EXPECT_TRUE(output_handler.std_err.empty()); } } TEST_F(Interactive_shell_test, sql_source_cmd) { std::string file_name = "f.sql"; if (!std::ifstream(file_name).good()) { std::ofstream f(file_name); f << "select 2;\n"; f << "select 3,\n" " 4,\n" " 5;\n"; f.close(); } execute("\\sql"); execute("\\connect " + _uri); const auto times_in_output = [&](const char *str) { int i = 0; size_t pos = 0; while ((pos = output_handler.std_out.find(str, pos)) != std::string::npos) { i++; pos++; } return i; }; execute("select 1; \\source " + file_name); EXPECT_EQ(3, times_in_output("1 row in set")); EXPECT_TRUE(output_handler.std_err.empty()); execute("\\history clear"); wipe_all(); execute("select 1; \\. " + file_name); EXPECT_EQ(3, times_in_output("1 row in set")); EXPECT_TRUE(output_handler.std_err.empty()); execute("\\history clear"); wipe_all(); execute("source " + file_name + "; select 1;"); EXPECT_EQ(3, times_in_output("1 row in set")); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("SoUrce " + file_name); EXPECT_EQ(2, times_in_output("1 row in set")); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("select 'sabra'; SOURCE " + file_name + ";"); MY_EXPECT_STDOUT_CONTAINS("sabra"); EXPECT_EQ(3, times_in_output("1 row in set")); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("delimiter sr"); execute("SOURCE " + file_name + " sr"); execute("delimiter ;"); EXPECT_EQ(2, times_in_output("1 row in set")); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("\\history"); MY_EXPECT_STDOUT_CONTAINS("source " + file_name + ";"); MY_EXPECT_STDOUT_CONTAINS("SoUrce " + file_name); MY_EXPECT_STDOUT_CONTAINS("SOURCE " + file_name + ";"); MY_EXPECT_STDOUT_NOT_CONTAINS("select 2;"); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("delimiter $$"); execute("SOURCE \"file$$.sql\" $$ select 777 $$"); execute("delimiter ;"); MY_EXPECT_STDERR_CONTAINS("Failed to open file 'file$$.sql'"); MY_EXPECT_STDOUT_CONTAINS("777"); wipe_all(); std::string file_source = "fs.sql"; if (!std::ifstream(file_source).good()) { std::ofstream f(file_source); f << "select 'sabra';\n" << std::endl; f << "source " << file_name << ";" << std::endl; f << "select 'cadabra';\n" << std::endl; f << "\\. " << file_name << std::endl; f.close(); } testutil->call_mysqlsh_c({_uri, "--sql", "-f", file_source}); EXPECT_EQ(4, times_in_output("2")); MY_EXPECT_STDOUT_CONTAINS("sabra"); MY_EXPECT_STDOUT_CONTAINS("cadabra"); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); shcore::delete_file(file_name); shcore::delete_file(file_source); execute( "source " + shcore::path::join_path(g_test_home, "data", "sql", "sakila-schema.sql") + ";"); EXPECT_TRUE(output_handler.std_err.empty()); EXPECT_NO_THROW(execute("drop database sakila")); wipe_all(); } TEST_F(Interactive_shell_test, tls_ciphersuites) { // Feature not yet supported on X protocol execute("shell.connect(\"" + _uri + "?tls-ciphersuites=[TLS_DHE_PSK_WITH_AES_128_GCM_SHA256," "TLS_CHACHA20_POLY1305_SHA256]\");"); MY_EXPECT_STDOUT_CONTAINS("tls-ciphersuites option is not yet supported"); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("shell.connect(\"" + _uri + "?tls-ciphersuites=TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:" "TLS_CHACHA20_POLY1305_SHA256\");"); MY_EXPECT_STDOUT_CONTAINS("tls-ciphersuites option is not yet supported"); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // needed to force classic protocol to use TCP and SSL instead of unix sockets std::string classic_uri = shcore::str_replace(_mysql_uri, "localhost", "127.0.0.1"); // We only check if there is no error since this feature requires TLSv1.3 // which is not available on all the platforms e.g. Ubuntu 16.04 execute("shell.connect(\"" + classic_uri + "?tls-ciphersuites=[TLS_DHE_PSK_WITH_AES_128_GCM_SHA256," "TLS_CHACHA20_POLY1305_SHA256]\");"); EXPECT_TRUE(output_handler.std_err.empty()); MY_EXPECT_STDOUT_NOT_CONTAINS("tls-ciphersuites option is not yet supported"); wipe_all(); execute("shell.connect(\"" + classic_uri + "?tls-ciphersuites=TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:" "TLS_CHACHA20_POLY1305_SHA256\");"); EXPECT_TRUE(output_handler.std_err.empty()); MY_EXPECT_STDOUT_NOT_CONTAINS("tls-ciphersuites option is not yet supported"); wipe_all(); } #ifdef HAVE_JS TEST_F(Interactive_shell_test, js_interactive_multiline_comments) { execute("\\js"); execute("print(1);/*"); execute("print(2);"); execute("print(3); */print(4)"); EXPECT_EQ("14", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("var f = function () { /*"); execute("print(1);"); execute("*/print(7)"); execute("}"); execute(""); execute("f();"); EXPECT_EQ("7", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("print(1); /*/*"); execute("*/print(2);"); EXPECT_EQ("12", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); } #endif #ifdef HAVE_JS TEST_F(Interactive_shell_test, js_interactive_template_literal) { execute("\\js"); execute("var a = `multi"); execute("line`;"); execute(""); execute("print(a);"); EXPECT_EQ("multi\nline", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); } #endif // HAVE_JS TEST_F(Interactive_shell_test, py_multiple_statements) { // BUG#30029568 execute("\\py"); wipe_all(); execute(R"(one = 1 two = 'two' def three(): print(3) def four(): print('four') )"); execute("print(one)"); execute("print(two)"); execute("three()"); execute("four()"); EXPECT_EQ(R"(1 two 3 four )", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); } #if defined(HAVE_JS) && (defined(HAVE_PYTHON)) TEST_F(Interactive_shell_test, file_operations) { // BUG#30538516 METHODS WHICH DEAL WITH FILESYSTEM DO NOT PROPERLY HANDLE // NON-ASCII CHARACTERS const auto tempdir = shcore::path::tmpdir(); // Create non-ascii files using python execute("\\py"); execute("import os"); execute("os.getcwd()"); execute("tempdir = \"\"\"" + shcore::str_replace(tempdir, "\\", "\\\\") + "\"\"\""); execute("print(tempdir)"); wipe_all(); execute("os.mkdir(os.path.join(tempdir, 'zażółć_gęślą_jaźń'))"); execute("os.makedirs(os.path.join(tempdir, 'zażółć/gęślą/jaźń'))"); // clang-format off execute( "for f in ('zażółć_gęślą_jaźń', 'zażółć/gęślą/jaźń','zażółć/gęślą', 'zażółć', '.'):\n" " with open(os.path.join(tempdir, f, 'test.txt'), 'w', encoding='utf-8') as fh:\n" " fh.write('# mysqlsh file operations test\\n')\n" " with open(os.path.join(tempdir, f, 'café.txt'), 'w', encoding='utf-8') as fh:\n" " fh.write('# mysqlsh file operations test\\ncafé arabica\\n')\n"); // clang-format on // Check in javascript mode if we can access them execute("\\js"); execute("const tempdir = '" + shcore::str_replace(tempdir, "\\", "\\\\") + "'"); execute("println(tempdir)"); wipe_all(); execute( "os.path.isfile(os.path.join(tempdir, './zażółć_gęślą_jaźń/test.txt'))"); execute( "os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/jaźń/test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './test.txt'))"); EXPECT_EQ("true\ntrue\ntrue\ntrue\ntrue\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute( "os.path.isfile(os.path.join(tempdir, './zażółć_gęślą_jaźń/café.txt'))"); execute( "os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/jaźń/café.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/café.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/café.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './café.txt'))"); EXPECT_EQ("true\ntrue\ntrue\ntrue\ntrue\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("os.path.isdir(os.path.join(tempdir, './zażółć_gęślą_jaźń'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć/gęślą/jaźń'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć/gęślą'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć'))"); EXPECT_EQ("true\ntrue\ntrue\ntrue\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // cleanup using shcore (functions family that javascipt's os.path is using) shcore::remove_directory(shcore::path::join_path(tempdir, "./zażółć"), true); shcore::remove_directory( shcore::path::join_path(tempdir, "./zażółć_gęślą_jaźń"), true); shcore::delete_file(shcore::path::join_path(tempdir, "./test.txt")); shcore::delete_file(shcore::path::join_path(tempdir, "./café.txt")); execute("os.path.isfile(os.path.join(tempdir, './test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './café.txt'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć_gęślą_jaźń'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć'))"); EXPECT_EQ("false\nfalse\nfalse\nfalse\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // Create non-ascii files using javascript shcore::create_directory( shcore::path::join_path(tempdir, "zażółć_gęślą_jaźń")); shcore::create_directory( shcore::path::join_path(tempdir, "zażółć/gęślą/jaźń"), true); for (const auto &f : {"zażółć_gęślą_jaźń", "zażółć/gęślą/jaźń", "zażółć/gęślą", "zażółć", ""}) { shcore::create_file(shcore::path::join_path(tempdir, f, "test.txt"), "# mysqlsh file operations test\n"); shcore::create_file(shcore::path::join_path(tempdir, f, "café.txt"), "# mysqlsh file operations test\ncafé arabica\n"); } // Check in python mode if we can access them execute("\\py"); wipe_all(); execute( "os.path.isfile(os.path.join(tempdir, './zażółć_gęślą_jaźń/test.txt'))"); execute( "os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/jaźń/test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './test.txt'))"); EXPECT_EQ("true\ntrue\ntrue\ntrue\ntrue\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute( "os.path.isfile(os.path.join(tempdir, './zażółć_gęślą_jaźń/café.txt'))"); execute( "os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/jaźń/café.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/gęślą/café.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './zażółć/café.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './café.txt'))"); EXPECT_EQ("true\ntrue\ntrue\ntrue\ntrue\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("os.path.isdir(os.path.join(tempdir, './zażółć_gęślą_jaźń'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć/gęślą/jaźń'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć/gęślą'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć'))"); EXPECT_EQ("true\ntrue\ntrue\ntrue\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // cleanup using python execute( "for f in ('zażółć_gęślą_jaźń', 'zażółć/gęślą/jaźń', 'zażółć/gęślą', " "'zażółć'):\n" " os.remove(os.path.join(tempdir, f, 'test.txt'))\n" " os.remove(os.path.join(tempdir, f, 'café.txt'))\n" " os.rmdir(os.path.join(tempdir, f))\n"); execute("os.remove(os.path.join(tempdir, 'test.txt'))"); execute("os.remove(os.path.join(tempdir, 'café.txt'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć_gęślą_jaźń'))"); execute("os.path.isdir(os.path.join(tempdir, './zażółć'))"); execute("os.path.isfile(os.path.join(tempdir, './test.txt'))"); execute("os.path.isfile(os.path.join(tempdir, './café.txt'))"); EXPECT_EQ("false\nfalse\nfalse\nfalse\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); } #endif #if defined(HAVE_JS) TEST_F(Interactive_shell_test, Bug30296825) { execute("\\js"); execute("\\connect " + _uri); execute("shell.setCurrentSchema(\"mysql\");"); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("db.schema;"); MY_EXPECT_STDOUT_CONTAINS("<Schema:mysql>"); wipe_all(); execute("db.getSchema();"); MY_EXPECT_STDOUT_CONTAINS("<Schema:mysql>"); wipe_all(); } #endif TEST_F(Interactive_shell_test, show_warnings) { execute("\\sql"); // Enable warnings execute("\\W"); MY_EXPECT_STDOUT_CONTAINS("Show warnings enabled"); const auto test_warnings = [&](const std::string &uri) { execute("\\connect " + uri); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("select cast('abc' as signed);"); MY_EXPECT_STDOUT_CONTAINS("Truncated incorrect INTEGER value: 'abc'"); wipe_all(); }; // X protocol test_warnings(_uri); // Classic test_warnings(_mysql_uri); } #ifdef HAVE_JS TEST_F(Interactive_shell_test, DISABLED_redefine_let) { // BUG#32470621 execute("\\js"); wipe_all(); // use variable before it is defined, variable ends up in TDZ execute("z; let z = 0;"); EXPECT_TRUE(output_handler.std_out.empty()); EXPECT_EQ("ReferenceError: z is not defined\n", output_handler.std_err); wipe_all(); // variable cannot be accessed any more execute("z;"); EXPECT_TRUE(output_handler.std_out.empty()); EXPECT_EQ("ReferenceError: z is not defined\n", output_handler.std_err); wipe_all(); // variable cannot be assigned any more execute("z = 0;"); EXPECT_TRUE(output_handler.std_out.empty()); EXPECT_EQ("ReferenceError: Cannot access 'z' before initialization\n", output_handler.std_err); wipe_all(); // define it once again to fix it execute("let z = 1;"); EXPECT_TRUE(output_handler.std_out.empty()); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // check the value execute("println(z);"); EXPECT_EQ("1\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // define it one more time, overwrites the previous value execute("let z = 2;"); EXPECT_TRUE(output_handler.std_out.empty()); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); // check the value execute("println(z);"); EXPECT_EQ("2\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); wipe_all(); } #endif // HAVE_JS #if defined(HAVE_PYTHON) && defined(_WIN32) TEST_F(Interactive_shell_test, windows_python_locale) { execute("\\py"); execute("import locale"); ASSERT_TRUE(output_handler.std_err.empty()); wipe_all(); execute("locale.getlocale() == (None, None)"); EXPECT_EQ("true\n", output_handler.std_out); EXPECT_TRUE(output_handler.std_err.empty()); } #endif } // namespace mysqlsh