unittest/scripts/js_devapi/setup/setup.js (470 lines of code) (raw):

if (__mysqluripwd) { var s = mysql.getSession(__mysqluripwd); var r = s.runSql("SELECT @@hostname, @@report_host").fetchOne(); s.close(); var __mysql_hostname = r[0]; var __mysql_report_host = r[1]; var __mysql_host = __mysql_report_host ? __mysql_report_host : __mysql_hostname; // Address that appear in pre-configured sandboxes that set report_host to 127.0.0.1 var __address1 = "127.0.0.1:" + __mysql_sandbox_port1; var __address2 = "127.0.0.1:" + __mysql_sandbox_port2; var __address3 = "127.0.0.1:" + __mysql_sandbox_port3; // Address that appear in raw sandboxes, that show the real hostname var __address1r = __mysql_host + ":" + __mysql_sandbox_port1; var __address2r = __mysql_host + ":" + __mysql_sandbox_port2; var __address3r = __mysql_host + ":" + __mysql_sandbox_port3; } function validate_crud_functions(crud, expected) { var actual = dir(crud); // Ensures expected functions are on the actual list var missing = []; for (exp_funct of expected) { var pos = actual.indexOf(exp_funct); if (pos == -1) { missing.push(exp_funct); } else { actual.splice(pos, 1); } } if (missing.length == 0) { print("All expected functions are available\n"); } else { print("Missing Functions:", missing); } // help is ignored cuz it's always available if (actual.length == 0 || (actual.length == 1 && actual[0] == "help")) { print("No additional functions are available\n") } else { print("Extra Functions:", actual); } } function validateMembers(obj, expected) { var actual = dir(obj); // Ensures expected members are on the actual list var missing = []; for (exp of expected) { var pos = actual.indexOf(exp); if (pos == -1) { missing.push(exp); } else { actual.splice(pos, 1); } } var errors = [] if (missing.length) { errors.push("Missing Members: " + missing.join(", ")); } // help is ignored cuz it's always available if (actual.length > 1 || (actual.length == 1 && actual[0] != 'help')) { errors.push("Extra Members: " + actual.join(", ")); } if (errors.length) { testutil.fail(errors.join("\n")) } } function ensure_schema_does_not_exist(session, name) { try { var schema = session.getSchema(name); session.dropSchema(name); } catch (err) { // Nothing happens, it means the schema did not exist } } function getSchemaFromList(schemas, name) { for (index in schemas) { if (schemas[index].name == name) return schemas[index]; } return null; } function wait(timeout, wait_interval, condition) { waiting = 0; res = condition(); while (!res && waiting < timeout) { os.sleep(wait_interval); waiting = waiting + 1; res = condition(); } return res; } // cb_params is an array with whatever parameters required by the // called callback, this way we can pass params to that function // right from the caller of retry function retry(attempts, wait_interval, callback, cb_params) { attempt = 1; res = callback(cb_params); while (!res && attempt < attempts) { println("Attempt " + attempt + " of " + attempts + " failed, retrying..."); os.sleep(wait_interval); attempt = attempt + 1; res = callback(cb_params); } return res; } var ro_session; var ro_module = require('mysql'); function wait_super_read_only_done() { var super_read_only = ro_session.runSql('select @@super_read_only').fetchOne()[0]; println("---> Super Read Only = " + super_read_only); return super_read_only == "0"; } function check_super_read_only_done(connection) { ro_session = ro_module.getClassicSession(connection); wait(60, 1, wait_super_read_only_done); ro_session.close(); } function connect_to_sandbox(params) { var port = params[0]; var connected = false; try { shell.connect({ scheme: 'mysql', user: 'root', password: 'root', host: 'localhost', port: port }) connected = true; println('connected to sandbox at ' + port); } catch (err) { println('failed connecting to sandbox at ' + port + ': ' + err.message); } return connected; } //Function to delete sandbox (only succeed after full server shutdown). function try_delete_sandbox(port, sandbox_dir) { var deleted = false; options = {} if (sandbox_dir != '') options['sandboxDir'] = sandbox_dir; print('Try deleting sandbox at: ' + port); started = wait(10, 1, function () { try { dba.deleteSandboxInstance(port, options); println(' succeeded'); return true; } catch (err) { println(' failed: ' + err.message); return false; } }); if (deleted) { println('Delete succeeded at: ' + port); } else { println('Delete failed at: ' + port); } } function set_sysvar(session, variable, value) { session.runSql("SET GLOBAL " + variable + " = ?", [value]); } function get_sysvar(session, variable, type) { var close_session = false; var pos = 0; var query = ""; // Check if the variable session is an int, if so it's the member_port and we need to establish a session if (typeof session == "number") { session = shell.connect("mysql://root:root@localhost:" + session); close_session = true; } if (type == undefined) type = "GLOBAL"; if (type == "GLOBAL") { query = "SELECT @@GLOBAL." + variable; } else if (type == "PERSISTED") { query = "SELECT * from performance_schema.persisted_variables WHERE Variable_name like '%" + variable + "%'"; pos = 1; } var row = session.runSql(query).fetchOne(); // Close the session if established in this function if (close_session) session.close(); if (row == null) return ""; return row[pos]; } function enable_auto_rejoin(session, port) { var close_session = false; // Check if the variable session is an int, if so it's the member_port and we need to establish a session if (typeof session == "number") { var port = session; session = shell.connect("mysql://root:root@localhost:" + session); close_session = true; } testutil.changeSandboxConf(port, "group_replication_start_on_boot", "ON"); if (__version_num > 80011) session.runSql("RESET PERSIST group_replication_start_on_boot"); // Close the session if established in this function if (close_session) session.close(); } function disable_auto_rejoin(session, port) { var close_session = false; // Check if the variable session is an int, if so it's the member_port and we need to establish a session if (typeof session == "number") { var port = session; session = shell.connect("mysql://root:root@localhost:" + session); close_session = true; } testutil.changeSandboxConf(port, "group_replication_start_on_boot", "OFF"); if (__version_num > 80011) session.runSql("RESET PERSIST group_replication_start_on_boot"); // Close the session if established in this function if (close_session) session.close(); } function disable_expel_timeout(session) { // in 8.0.21, default expel_timeout was set to 5 seconds from 0 if (__version_num >= 80021) return; var close_session = false; // Check if the variable session is an int, if so it's the member_port and we need to establish a session if (typeof session == "number") { var port = session; session = shell.connect("mysql://root:root@localhost:" + session); close_session = true; } session.runSql("SET PERSIST group_replication_expel_timeout=0"); // Close the session if established in this function if (close_session) session.close(); } function create_root_from_anywhere(session, clear_super_read_only) { var super_read_only = get_sysvar(session, "super_read_only"); var super_read_only_enabled = super_read_only === "ON"; session.runSql("SET SQL_LOG_BIN=0"); if (clear_super_read_only && super_read_only_enabled) set_sysvar(session, "super_read_only", 0); session.runSql("CREATE USER root@'%' IDENTIFIED BY 'root'"); session.runSql("GRANT ALL ON *.* to root@'%' WITH GRANT OPTION"); if (clear_super_read_only && super_read_only_enabled) set_sysvar(session, "super_read_only", 1); session.runSql("SET SQL_LOG_BIN=1"); } function ensure_plugin_enabled(plugin_name, session, plugin_soname) { // For function signature simplicity I use `plugin_name` also as shared library name. var sess = session === undefined ? mysql.getClassicSession(__uripwd) : session; var rs = sess.runSql("SELECT COUNT(1) FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME like '" + plugin_name + "';"); var is_installed = rs.fetchOne()[0]; if (plugin_soname === undefined) plugin_soname = plugin_name; if (!is_installed) { var os = sess.runSql('select @@version_compile_os').fetchOne()[0]; if (os == "Win32" || os == "Win64") { sess.runSql("INSTALL PLUGIN " + plugin_name + " SONAME '" + plugin_soname + ".dll';"); } else { sess.runSql("INSTALL PLUGIN " + plugin_name + " SONAME '" + plugin_soname + ".so';"); } } if (session === undefined) sess.close(); } function ensure_plugin_disabled(plugin_name) { var sess = mysql.getClassicSession(__uripwd); var rs = sess.runSql("SELECT COUNT(1) FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME like '" + plugin_name + "';"); var is_installed = rs.fetchOne()[0]; if (is_installed) { sess.runSql("UNINSTALL PLUGIN " + plugin_name + ";"); } sess.close(); } // Check the server compatibility with the specified version numbers. // Return true if the server version is >= than the one specified one, otherwise // false. function check_server_version(major, minor, patch) { var result = session.runSql("SELECT sys.version_major(), sys.version_minor(), sys.version_patch()"); var row = result.fetchOne(); var srv_major = row[0]; var srv_minor = row[1]; var srv_patch = row[2]; return (srv_major > major || (srv_major == major && (srv_minor > minor || (srv_minor == minor && srv_patch >= patch)))); } // Check if the instance exists in the Metadata schema function exist_in_metadata_schema() { var result = session.runSql( 'SELECT COUNT(*) FROM mysql_innodb_cluster_metadata.instances where CAST(mysql_server_uuid AS CHAR CHARACTER SET ASCII) = CAST(@@server_uuid AS CHAR CHARACTER SET ASCII);'); var row = result.fetchOne(); return row[0] != 0; } // -------- Test Expectations function EXPECT_EQ(expected, actual, note) { if (note == undefined) note = ""; if (repr(expected) != repr(actual)) { var context = "<b>Context:</b> " + __test_context + "\n<red>Tested values don't match as expected:</red> " + note + "\n\t<yellow>Actual:</yellow> " + repr(actual) + "\n\t<yellow>Expected:</yellow> " + repr(expected); testutil.fail(context); } } function EXPECT_STDOUT_CONTAINS(text) { var out = testutil.fetchCapturedStdout(false); var err = testutil.fetchCapturedStderr(false); if (out.indexOf(text) < 0) { var context = "<b>Context:</b> " + __test_context + "\n<red>Missing output:</red> " + text + "\n<yellow>Actual stdout:</yellow> " + out + "\n<yellow>Actual stderr:</yellow> " + err; testutil.fail(context); } } function WARNING_SKIPPED_TEST(reason) { return false; } function get_exception_message(exception) { if (exception.cause != undefined) { if (exception.cause.message != undefined) { return exception.cause.message; } } return exception.message } function EXPECT_THROWS_TYPE(func, etext, type) { try { func(); testutil.fail("<b>Context:</b> " + __test_context + "\n<red>Missing expected exception throw like " + etext + "</red>"); } catch (err) { testutil.dprint("Caught exception: " + JSON.stringify(err)); if (err.message.indexOf(etext) < 0) { testutil.fail("<b>Context:</b> " + __test_context + "\n<red>Exception expected:</red> " + etext + "\n\t<yellow>Actual:</yellow> " + err.message); } if (err.cause.type !== type) { testutil.fail("<b>Context:</b> " + __test_context + "\n<red>Exception type expected:</red> " + type + "\n\t<yellow>Actual:</yellow> " + err.type); } } } // Starting 8.0.24 the client lib started reporting connection error using // host:port format, previous versions used just the host. // // This function is used to format the host description accordingly. function libmysql_host_description(hostname, port) { if (testutil.versionCheck(__libmysql_version_id, ">", "8.0.23")) { return hostname + ":" + port; } return hostname; } function EXPECT_THROWS_LIKE(func, pattern) { try { func(); testutil.fail("<b>Context:</b> " + __test_context + "\n<red>Missing expected exception throw like " + String(etext) + "</red>"); } catch (err) { msg = get_exception_message(err); testutil.dprint("Caught exception: " + msg); if (!pattern.test(msg)) { testutil.fail("<b>Context:</b> " + __test_context + "\n<red>Exception expected:</red> " + String(pattern) + "\n\t<yellow>Actual:</yellow> " + err.message); } } } function EXPECT_NO_THROWS(func, context) { try { func(); } catch (err) { testutil.dprint("Unexpected exception: " + JSON.stringify(err)); testutil.fail("<b>Context:</b> " + __test_context + "\n<red>Unexpected exception thrown (" + context + "): " + err.message + "</red>"); } return hostname; } function EXPECT_OUTPUT_CONTAINS(text) { var out = testutil.fetchCapturedStdout(false); var err = testutil.fetchCapturedStderr(false); if (out.indexOf(text) < 0 && err.indexOf(text) < 0) { var context = "<b>Context:</b> " + __test_context + "\n<red>Missing output:</red> " + text + "\n<yellow>Actual stdout:</yellow> " + out + "\n<yellow>Actual stderr:</yellow> " + err; testutil.fail(context); } } function __split_trim_join(text) { const needle = '\n'; const s = text.split(needle); s.forEach(function (item, index) { this[index] = item.trimEnd(); }, s); return { 'str': s.join(needle), 'array': s }; } function __check_wildcard_match(expected, actual) { if (0 === expected.length) { return expected == actual; } const needle = '[[*]]'; const strings = expected.split(needle); var start = 0; for (const str of strings) { const idx = actual.indexOf(str, start); if (idx < 0) { return false; } start = idx + str.length; } if (strings[strings.length - 1] === '') { // expected ends with wildcard start = actual.length; } return start === actual.length; } function __multi_value_compare(expected, actual) { const start = expected.indexOf('{{'); if (start < 0) { return __check_wildcard_match(expected, actual); } else { const end = expected.indexOf('}}'); const pre = expected.substring(0, start); const post = expected.substring(end + 2); const opts = expected.substring(start + 2, end); for (const item of opts.split('|')) { if (__check_wildcard_match(pre + item + post, actual)) { return true; } } return false; } } function __find_line_matching(expected, actual_lines, start_at = 0) { var actual_index = start_at; while (actual_index < actual_lines.length) { if (__multi_value_compare(expected, actual_lines[actual_index])) { break; } else { ++actual_index; } } return actual_index; } function __diff_with_error(arr, error, idx) { const needle = '\n'; const copy = [...arr]; copy[idx] += error; return copy.join(needle); } function __check_multiline_expect(expected_lines, actual_lines) { var diff = ''; var matches = true; const needle = '\n'; while ('' === expected_lines[0] || '[[*]]' === expected_lines[0]) { expected_lines.shift(); } var actual_index = __find_line_matching(expected_lines[0], actual_lines); if (actual_index < actual_lines.length) { for (var expected_index = 0; expected_index < expected_lines.length; ++expected_index) { if ('[[*]]' === expected_lines[expected_index]) { if (expected_index == expected_lines.length - 1) { continue; // Ignores [[*]] if at the end of the expectation } else { ++expected_index; actual_index = __find_line_matching(expected_lines[expected_index], actual_lines, actual_index + expected_index - 1); if (actual_index < actual_lines.length) { actual_index -= expected_index; } else { matches = false; diff = __diff_with_error(expected_lines, '<yellow><------ INCONSISTENCY</yellow>', expected_index); break; } } } if ((actual_index + expected_index) >= actual_lines.length) { matches = false; diff = __diff_with_error(expected_lines, '<yellow><------ MISSING</yellow>', expected_index); break; } if (!__multi_value_compare(expected_lines[expected_index], actual_lines[actual_index + expected_index])) { matches = false; diff = __diff_with_error(expected_lines, '<yellow><------ INCONSISTENCY</yellow>', expected_index); break; } } } else { matches = false; diff = __diff_with_error(expected_lines, '<yellow><------ INCONSISTENCY</yellow>', 0); } return { 'matches': matches, 'diff': diff }; } function EXPECT_OUTPUT_CONTAINS_MULTILINE(t) { const out = __split_trim_join(testutil.fetchCapturedStdout(false)); const err = __split_trim_join(testutil.fetchCapturedStderr(false)); const text = __split_trim_join(t); const out_result = __check_multiline_expect(text.array, out.array); const err_result = __check_multiline_expect(text.array, err.array); if (!out_result.matches && !err_result.matches) { const context = "<b>Context:</b> " + __test_context + "\n<red>Missing output:</red> " + text.str + "\n<yellow>Actual stdout:</yellow> " + out.str + "\n<yellow>Actual stderr:</yellow> " + err.str + "\n<yellow>Diff with stdout:</yellow>\n" + out_result.diff + "\n<yellow>Diff with stderr:</yellow>\n" + err_result.diff; testutil.fail(context); } } function EXPECT_STDOUT_CONTAINS_MULTILINE(t) { const out = __split_trim_join(testutil.fetchCapturedStdout(false)); const err = __split_trim_join(testutil.fetchCapturedStderr(false)); const text = __split_trim_join(t); const out_result = __check_multiline_expect(text.array, out.array); if (!out_result.matches) { const context = "<b>Context:</b> " + __test_context + "\n<red>Missing output:</red> " + text.str + "\n<yellow>Actual stdout:</yellow> " + out.str + "\n<yellow>Actual stderr:</yellow> " + err.str + "\n<yellow>Diff with stdout:</yellow>\n" + out_result.diff; testutil.fail(context); } } function EXPECT_STDERR_CONTAINS_MULTILINE(t) { const out = __split_trim_join(testutil.fetchCapturedStdout(false)); const err = __split_trim_join(testutil.fetchCapturedStderr(false)); const text = __split_trim_join(t); const err_result = __check_multiline_expect(text.array, err.array); if (!err_result.matches) { const context = "<b>Context:</b> " + __test_context + "\n<red>Missing output:</red> " + text.str + "\n<yellow>Actual stdout:</yellow> " + out.str + "\n<yellow>Actual stderr:</yellow> " + err.str + "\n<yellow>Diff with stderr:</yellow>\n" + err_result.diff; testutil.fail(context); } }