From 59919d48a9cb0e7a89ccbd0201574e20a19ff3ad Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Tue, 7 Feb 2023 17:10:00 -0800 Subject: [PATCH] integration-tests/node: Progress on workbench stability tests --- .../mysql-client-tests/node/database.js | 3 +- .../mysql-client-tests/node/helpers.js | 16 ++ .../mysql-client-tests/node/index.js | 26 +--- .../mysql-client-tests/node/package-lock.json | 13 +- .../mysql-client-tests/node/package.json | 3 +- .../mysql-client-tests/node/workbench.js | 26 ++++ .../node/workbenchTests/branches.js | 92 +++++++++++ .../node/workbenchTests/databases.js | 49 ++++++ .../node/workbenchTests/index.js | 48 ++++++ .../node/workbenchTests/logs.js | 53 +++++++ .../node/workbenchTests/matchers.js | 67 ++++++++ .../node/workbenchTests/merge.js | 43 ++++++ .../node/workbenchTests/table.js | 143 ++++++++++++++++++ 13 files changed, 561 insertions(+), 21 deletions(-) create mode 100644 integration-tests/mysql-client-tests/node/workbench.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/branches.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/databases.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/index.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/logs.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/matchers.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/merge.js create mode 100644 integration-tests/mysql-client-tests/node/workbenchTests/table.js diff --git a/integration-tests/mysql-client-tests/node/database.js b/integration-tests/mysql-client-tests/node/database.js index ce93b6f2c2..bc23f907bd 100644 --- a/integration-tests/mysql-client-tests/node/database.js +++ b/integration-tests/mysql-client-tests/node/database.js @@ -1,4 +1,4 @@ -import mysql from "mysql"; +import mysql from "mysql2"; export class Database { constructor(config) { @@ -14,6 +14,7 @@ export class Database { }); }); } + close() { this.connection.end((err) => { if (err) { diff --git a/integration-tests/mysql-client-tests/node/helpers.js b/integration-tests/mysql-client-tests/node/helpers.js index 8fcf0151b4..61d008a32f 100644 --- a/integration-tests/mysql-client-tests/node/helpers.js +++ b/integration-tests/mysql-client-tests/node/helpers.js @@ -1,3 +1,5 @@ +import { mysql as escapeQueryWithParameters } from "yesql"; + const args = process.argv.slice(2); const user = args[0]; const port = args[1]; @@ -16,3 +18,17 @@ export function getConfig() { database: dbName, }; } + +export function assertQueryResult(q, resultStr, expected, rows, matcher) { + if (matcher) { + return matcher(rows, expected); + } + if (q.toLowerCase().includes("dolt_commit")) { + return rows.length === 1 && rows[0].hash.length === 32; + } + return resultStr === JSON.stringify(expected); +} + +export function getQueryWithEscapedParameters(q, parameters) { + return escapeQueryWithParameters(q)(parameters || {}); +} diff --git a/integration-tests/mysql-client-tests/node/index.js b/integration-tests/mysql-client-tests/node/index.js index 413b352e7b..a19e96bbf3 100644 --- a/integration-tests/mysql-client-tests/node/index.js +++ b/integration-tests/mysql-client-tests/node/index.js @@ -1,5 +1,5 @@ import { Database } from "./database.js"; -import { getConfig } from "./helpers.js"; +import { assertQueryResult, getConfig } from "./helpers.js"; const tests = [ { @@ -8,11 +8,9 @@ const tests = [ fieldCount: 0, affectedRows: 0, insertId: 0, + info: "", serverStatus: 2, - warningCount: 0, - message: "", - protocol41: true, - changedRows: 0, + warningStatus: 0, }, }, { @@ -43,11 +41,9 @@ const tests = [ fieldCount: 0, affectedRows: 1, insertId: 0, + info: "", serverStatus: 2, - warningCount: 0, - message: "", - protocol41: true, - changedRows: 0, + warningStatus: 0, }, }, { q: "select * from test", res: [{ pk: 0, value: 0 }] }, @@ -61,11 +57,9 @@ const tests = [ fieldCount: 0, affectedRows: 1, insertId: 0, + info: "", serverStatus: 2, - warningCount: 0, - message: "", - protocol41: true, - changedRows: 0, + warningStatus: 0, }, }, { q: "call dolt_commit('-a', '-m', 'my commit2')", res: [] }, @@ -88,11 +82,7 @@ async function main() { .then((rows) => { const resultStr = JSON.stringify(rows); const result = JSON.parse(resultStr); - if ( - resultStr !== JSON.stringify(expected) && - test.q.includes("dolt_commit") && - !(rows.length === 1 && rows[0].hash.length > 0) - ) { + if (!assertQueryResult(test.q, resultStr, expected, rows)) { console.log("Query:", test.q); console.log("Results:", result); console.log("Expected:", expected); diff --git a/integration-tests/mysql-client-tests/node/package-lock.json b/integration-tests/mysql-client-tests/node/package-lock.json index daaa50c802..774cba332b 100644 --- a/integration-tests/mysql-client-tests/node/package-lock.json +++ b/integration-tests/mysql-client-tests/node/package-lock.json @@ -12,7 +12,8 @@ "knex": "^2.4.0", "mysql": "^2.18.1", "mysql2": "^2.3.3", - "wtfnode": "^0.9.1" + "wtfnode": "^0.9.1", + "yesql": "^5.0.0" } }, "node_modules/bignumber.js": { @@ -448,6 +449,11 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yesql": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yesql/-/yesql-5.0.0.tgz", + "integrity": "sha512-ua4Ok0hv6Q+KPn2oJrGNRfsubYnKPLJAt6kvawG6Aqb6yg4Cm1T2JIBveFNx0aDhCIansNVq/9MFzuvmNdnJnw==" } }, "dependencies": { @@ -776,6 +782,11 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yesql": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yesql/-/yesql-5.0.0.tgz", + "integrity": "sha512-ua4Ok0hv6Q+KPn2oJrGNRfsubYnKPLJAt6kvawG6Aqb6yg4Cm1T2JIBveFNx0aDhCIansNVq/9MFzuvmNdnJnw==" } } } diff --git a/integration-tests/mysql-client-tests/node/package.json b/integration-tests/mysql-client-tests/node/package.json index f1edcc04d1..1e0a21947a 100644 --- a/integration-tests/mysql-client-tests/node/package.json +++ b/integration-tests/mysql-client-tests/node/package.json @@ -13,6 +13,7 @@ "knex": "^2.4.0", "mysql": "^2.18.1", "mysql2": "^2.3.3", - "wtfnode": "^0.9.1" + "wtfnode": "^0.9.1", + "yesql": "^5.0.0" } } diff --git a/integration-tests/mysql-client-tests/node/workbench.js b/integration-tests/mysql-client-tests/node/workbench.js new file mode 100644 index 0000000000..177fc89546 --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbench.js @@ -0,0 +1,26 @@ +import { Database } from "./database.js"; +import { getConfig } from "./helpers.js"; +import runWorkbenchTests from "./workbenchTests/index.js"; + +// Table + +// Workspaces + +// Diffs + +// Docs + +// Views + +// Tags + +async function workbench() { + const database = new Database(getConfig()); + + await runWorkbenchTests(database); + + database.close(); + process.exit(0); +} + +workbench(); diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/branches.js b/integration-tests/mysql-client-tests/node/workbenchTests/branches.js new file mode 100644 index 0000000000..a867343f6c --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/branches.js @@ -0,0 +1,92 @@ +import { branchesMatcher } from "./matchers.js"; +import { getArgs } from "../helpers.js"; + +const args = getArgs(); + +export const branchTests = [ + { + q: `CALL DOLT_BRANCH(:newBranchName, :fromRefName)`, + p: { newBranchName: "mybranch", fromRefName: "main" }, + res: [{ status: 0 }], + }, + { + q: `USE ::dbName`, + p: { dbName: `${args.dbName}/mybranch` }, + res: { + fieldCount: 0, + affectedRows: 0, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: "create table test (pk int, `value` int, primary key(pk))", + res: { + fieldCount: 0, + affectedRows: 0, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `CALL DOLT_COMMIT("-A", "-m", :commitMsg, "--author", :authorName)`, + p: { + commitMsg: "Create table test", + authorName: "Dolt ", + }, + res: [{ hash: "" }], + }, + { + q: `SELECT * FROM dolt_branches WHERE name NOT LIKE "workspaces/%" LIMIT 200`, + res: [ + { + name: "main", + latest_committer: "Taylor Bantle", + latest_committer_email: "taylor@liquidata.co", + // TODO: uncomment + // latest_committer: "mysql-test-runner", + // latest_committer_email: "mysql-test-runner@liquidata.co", + }, + { + name: "mybranch", + latest_committer: "Dolt", + latest_committer_email: "dolt@dolthub.com", + }, + ], + matcher: branchesMatcher, + }, + { + q: `CALL DOLT_CHECKOUT("-b", :branchName)`, + p: { branchName: "branch-to-delete" }, + res: [{ status: 0 }], + }, + { + q: `SELECT COUNT(*) FROM dolt_branches WHERE name NOT LIKE "workspaces/%" LIMIT 200`, + res: [{ ["COUNT(*)"]: 3 }], + }, + { + q: `USE ::dbName`, + p: { dbName: `${args.dbName}/main` }, + res: { + fieldCount: 0, + affectedRows: 0, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `CALL DOLT_BRANCH("-D", :branchName)`, + p: { branchName: "branch-to-delete" }, + res: [{ status: 0 }], + }, + { + q: `SELECT COUNT(*) FROM dolt_branches WHERE name NOT LIKE "workspaces/%" LIMIT 200`, + res: [{ ["COUNT(*)"]: 2 }], + }, +]; diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/databases.js b/integration-tests/mysql-client-tests/node/workbenchTests/databases.js new file mode 100644 index 0000000000..6fd29a2934 --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/databases.js @@ -0,0 +1,49 @@ +import { getArgs } from "../helpers.js"; + +const args = getArgs(); + +export const databaseTests = [ + { + q: `USE ::dbName`, + p: { dbName: `${args.dbName}/main` }, + res: { + fieldCount: 0, + affectedRows: 0, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `SHOW DATABASES`, + res: [ + { Database: "information_schema" }, + { Database: "mysql" }, + { Database: "mysql_client" }, + { Database: "mysql_client/main" }, + ], + }, + { + q: `CREATE DATABASE ::dbName`, + p: { dbName: "new_db" }, + res: { + fieldCount: 0, + affectedRows: 1, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `SHOW DATABASES`, + res: [ + { Database: "information_schema" }, + { Database: "mysql" }, + { Database: "mysql_client" }, + { Database: "mysql_client/main" }, + { Database: "new_db" }, + ], + }, +]; diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/index.js b/integration-tests/mysql-client-tests/node/workbenchTests/index.js new file mode 100644 index 0000000000..b2d45e634c --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/index.js @@ -0,0 +1,48 @@ +import { branchTests } from "./branches.js"; +import { databaseTests } from "./databases.js"; +import { logTests } from "./logs.js"; +import { mergeTests } from "./merge.js"; +import { tableTests } from "./table.js"; +import { + assertQueryResult, + getQueryWithEscapedParameters, +} from "../helpers.js"; + +export default async function runWorkbenchTests(database) { + await runTests(database, databaseTests); + await runTests(database, branchTests); + await runTests(database, logTests); + await runTests(database, mergeTests); + await runTests(database, tableTests); +} + +async function runTests(database, tests) { + await Promise.all( + tests.map((test) => { + const expected = test.res; + const { sql, values } = getQueryWithEscapedParameters(test.q, test.p); + return database + .query(sql, values) + .then((rows) => { + const resultStr = JSON.stringify(rows); + const result = JSON.parse(resultStr); + if ( + !assertQueryResult(test.q, resultStr, expected, rows, test.matcher) + ) { + console.log("Query:", test.q); + console.log("Results:", result); + console.log("Expected:", expected); + throw new Error("Query failed"); + } else { + console.log("Query succeeded:", test.q); + // console.log("RES", resultStr); + // console.log("EXP", expected); + } + }) + .catch((err) => { + console.error(err); + process.exit(1); + }); + }) + ); +} diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/logs.js b/integration-tests/mysql-client-tests/node/workbenchTests/logs.js new file mode 100644 index 0000000000..902e2724f8 --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/logs.js @@ -0,0 +1,53 @@ +import { logsMatcher } from "./matchers.js"; + +export const logTests = [ + { + q: `SELECT * FROM DOLT_LOG(:refName, '--parents') LIMIT :limit OFFSET :offset`, + p: { refName: "main", limit: 10, offset: 0 }, + res: [ + { + message: "Initialize data repository", + parentsLength: 0, + committer: "Taylor Bantle", + email: "taylor@liquidata.co", + // TODO: uncomment + // committer: "mysql-test-runner", + // email: "mysql-test-runner@liquidata.co", + }, + ], + matcher: logsMatcher, + }, + { + q: `SELECT * FROM dolt_log AS OF :refName`, + p: { refName: "mybranch" }, + res: [ + { + message: "Create table test", + committer: "Dolt", + email: "dolt@dolthub.com", + }, + { + message: "Initialize data repository", + committer: "Taylor Bantle", + email: "taylor@liquidata.co", + // TODO: uncomment + // committer: "mysql-test-runner", + // email: "mysql-test-runner@liquidata.co", + }, + ], + matcher: logsMatcher, + }, + { + q: `SELECT * FROM DOLT_LOG(:refRange, '--parents')`, + p: { refRange: "main..mybranch" }, + res: [ + { + message: "Create table test", + parentsLength: 1, + committer: "Dolt", + email: "dolt@dolthub.com", + }, + ], + matcher: logsMatcher, + }, +]; diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/matchers.js b/integration-tests/mysql-client-tests/node/workbenchTests/matchers.js new file mode 100644 index 0000000000..5441ecc35c --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/matchers.js @@ -0,0 +1,67 @@ +export function branchesMatcher(rows, exp) { + if (rows.length !== exp.length) { + return false; + } + for (let i = 0; i < rows.length; i++) { + if (rows[i].name !== exp[i].name) { + return false; + } + if (rows[i].hash.length !== 32) { + return false; + } + if (rows[i].latest_commit_date.length === 0) { + return false; + } + if (rows[i].latest_committer !== exp[i].latest_committer) { + return false; + } + if (rows[i].latest_committer_email !== exp[i].latest_committer_email) { + return false; + } + } + return true; +} + +export function logsMatcher(rows, exp) { + if (rows.length !== exp.length) { + return false; + } + for (let i = 0; i < rows.length; i++) { + if (rows[i].message !== exp[i].message) { + return false; + } + if (rows[i].commit_hash.length !== 32) { + return false; + } + if (rows[i].date.length === 0) { + return false; + } + if (rows[i].committer !== exp[i].committer) { + return false; + } + if (rows[i].email !== exp[i].email) { + return false; + } + if (exp[i].parentsLength !== undefined) { + if ( + rows[i].parents.split(", ").filter((v) => !!v.length).length !== + exp[i].parentsLength + ) { + return false; + } + } + } + return true; +} + +export function mergeBaseMatcher(rows, exp) { + if (rows.length !== 1 || exp.length !== 1) { + return false; + } + Object.keys(exp).forEach((key) => { + if (rows[key].length !== 32) { + return false; + } + }); + return true; +} diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/merge.js b/integration-tests/mysql-client-tests/node/workbenchTests/merge.js new file mode 100644 index 0000000000..4b6f654e51 --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/merge.js @@ -0,0 +1,43 @@ +import { logsMatcher, mergeBaseMatcher } from "./matchers.js"; + +export const mergeTests = [ + { + q: `SELECT DOLT_MERGE_BASE(:fromBranchName, :toBranchName)`, + p: { fromBranchName: "mybranch", toBranchName: "main" }, + res: [{ "DOLT_MERGE_BASE('mybranch', 'main')": "" }], + matcher: mergeBaseMatcher, + }, + { + q: `CALL DOLT_MERGE(:branchName, "--no-ff", "-m", :commitMsg)`, + p: { branchName: "mybranch", commitMsg: "Merge mybranch into main" }, + res: [{ fast_forward: 1, conflicts: 0 }], + }, + { + q: `SELECT * FROM DOLT_LOG(:refName, '--parents') LIMIT :limit OFFSET :offset`, + p: { refName: "main", limit: 10, offset: 0 }, + res: [ + { + message: "Merge mybranch into main", + parentsLength: 2, + committer: "mysql-test-runner", + email: "mysql-test-runner@liquidata.co", + }, + { + message: "Create table test", + parentsLength: 1, + committer: "Dolt", + email: "dolt@dolthub.com", + }, + { + message: "Initialize data repository", + parentsLength: 0, + committer: "Taylor Bantle", + email: "taylor@liquidata.co", + // TODO: uncomment + // committer: "mysql-test-runner", + // email: "mysql-test-runner@liquidata.co", + }, + ], + matcher: logsMatcher, + }, +]; diff --git a/integration-tests/mysql-client-tests/node/workbenchTests/table.js b/integration-tests/mysql-client-tests/node/workbenchTests/table.js new file mode 100644 index 0000000000..54c2f92279 --- /dev/null +++ b/integration-tests/mysql-client-tests/node/workbenchTests/table.js @@ -0,0 +1,143 @@ +import { getArgs } from "../helpers.js"; + +const args = getArgs(); + +export const tableTests = [ + { + q: "INSERT INTO test VALUES (0, 0), (1, 1), (2,2)", + res: { + fieldCount: 0, + affectedRows: 3, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `CREATE UNIQUE INDEX test_idx ON test (pk, value)`, + res: { + fieldCount: 0, + affectedRows: 0, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `SHOW CREATE TABLE ::tableName AS OF :refName`, + p: { tableName: "test", refName: "main" }, + res: [ + // Should not show new index in working set + { + Table: "test", + "Create Table": + "CREATE TABLE `test` (\n" + + " `pk` int NOT NULL,\n" + + " `value` int,\n" + + " PRIMARY KEY (`pk`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin", + }, + ], + }, + { + q: `CALL DOLT_COMMIT("-A", "-m", :commitMsg)`, + p: { commitMsg: "Add some rows and a column index" }, + res: [{ hash: "" }], + }, + { + q: `DESCRIBE ::tableName AS OF :refName`, + p: { tableName: "test", refName: "main" }, + res: [ + { + Field: "pk", + Type: "int", + Null: "NO", + Key: "PRI", + Default: "NULL", + Extra: "", + }, + { + Field: "value", + Type: "int", + Null: "YES", + Key: "", + Default: "NULL", + Extra: "", + }, + ], + }, + { + q: `SELECT + table_name, index_name, comment, non_unique, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS COLUMNS + FROM information_schema.statistics + WHERE table_schema=:tableSchema AND table_name=:tableName AND index_name!="PRIMARY" + GROUP BY index_name;`, + p: { tableSchema: `${args.dbName}/main`, tableName: "test" }, + res: [ + { + TABLE_NAME: "test", + INDEX_NAME: "test_idx", + COMMENT: "", + NON_UNIQUE: 0, + COLUMNS: "pk,value", + }, + ], + }, + { + q: "CREATE TABLE test_info (id int, info varchar(255), test_pk int, primary key(id), foreign key (test_pk) references test(pk))", + res: { + fieldCount: 0, + affectedRows: 0, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: "INSERT INTO test_info VALUES (1, 'info about test pk 0', 0)", + res: { + fieldCount: 0, + affectedRows: 1, + insertId: 0, + info: "", + serverStatus: 2, + warningStatus: 0, + }, + }, + { + q: `CALL DOLT_COMMIT("-A", "-m", :commitMsg)`, + p: { commitMsg: "Add test_info with foreign key" }, + res: [{ hash: "" }], + }, + { + q: `SHOW FULL TABLES AS OF :refName WHERE table_type = 'BASE TABLE'`, + p: { refName: "main" }, + res: [ + { "Tables_in_mysql_client/main": "test", Table_type: "BASE TABLE" }, + { "Tables_in_mysql_client/main": "test_info", Table_type: "BASE TABLE" }, + ], + }, + { + q: `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_name=:tableName AND table_schema=:tableSchema AND referenced_table_schema IS NOT NULL`, + p: { tableName: "test_info", tableSchema: `${args.dbName}/main` }, + res: [ + { + CONSTRAINT_CATALOG: "def", + CONSTRAINT_SCHEMA: "mysql_client/main", + CONSTRAINT_NAME: "s7utamh8", + TABLE_CATALOG: "def", + TABLE_SCHEMA: "mysql_client/main", + TABLE_NAME: "test_info", + COLUMN_NAME: "test_pk", + ORDINAL_POSITION: 1, + POSITION_IN_UNIQUE_CONSTRAINT: 1, + REFERENCED_TABLE_SCHEMA: "mysql_client/main", + REFERENCED_TABLE_NAME: "test", + REFERENCED_COLUMN_NAME: "pk", + }, + ], + }, +];