integration-tests/node: Progress on workbench stability tests

This commit is contained in:
Taylor Bantle
2023-02-07 17:10:00 -08:00
parent ba67297d3c
commit 59919d48a9
13 changed files with 561 additions and 21 deletions

View File

@@ -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) {

View File

@@ -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 || {});
}

View File

@@ -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);

View File

@@ -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=="
}
}
}

View File

@@ -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"
}
}

View File

@@ -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();

View File

@@ -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 <dolt@dolthub.com>",
},
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 }],
},
];

View File

@@ -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" },
],
},
];

View File

@@ -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);
});
})
);
}

View File

@@ -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,
},
];

View File

@@ -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;
}

View File

@@ -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,
},
];

View File

@@ -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",
},
],
},
];