From d5abeb302bce6d031fe728125cb60d354b2ea49d Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Wed, 6 Sep 2017 08:04:35 -0700 Subject: [PATCH 1/2] Add Spanner stale read sample. --- spanner/README.md | 37 ++------- spanner/crud.js | 51 ++++++++++++ spanner/package.json | 4 +- spanner/system-test/spanner.test.js | 122 ++++++++++++++++++---------- 4 files changed, 139 insertions(+), 75 deletions(-) diff --git a/spanner/README.md b/spanner/README.md index 71b177b9ce..596369ff6a 100644 --- a/spanner/README.md +++ b/spanner/README.md @@ -1,8 +1,8 @@ Google Cloud Platform logo -# Google Cloud Spanner Node.js Samples +# Cloud Spanner: Node.js Samples -[![Build](https://storage.googleapis.com/cloud-docs-samples-badges/GoogleCloudPlatform/nodejs-docs-samples/nodejs-docs-samples-spanner.svg)]() +[![Build](https://storage.googleapis.com/.svg)]() [Cloud Spanner](https://cloud.google.com/spanner/docs/) is a fully managed, mission-critical, relational database service that offers transactional consistency at global scale, schemas, SQL (ANSI 2011 with extensions), and automatic, synchronous replication for high availability. @@ -18,19 +18,6 @@ ## Setup -1. Read [Prerequisites][prereq] and [How to run a sample][run] first. -1. Install dependencies: - - With **npm**: - - npm install - - With **yarn**: - - yarn install - -[prereq]: ../README.md#prerequisites -[run]: ../README.md#how-to-run-a-sample ## Samples @@ -70,10 +57,11 @@ __Usage:__ `node crud.js --help` ``` Commands: - update Modifies existing rows of data in an example Cloud Spanner table. - query Executes a read-only SQL query against an example Cloud Spanner table. - insert Inserts new rows of data into an example Cloud Spanner table. - read Reads data in an example Cloud Spanner table. + update Modifies existing rows of data in an example Cloud Spanner table. + query Executes a read-only SQL query against an example Cloud Spanner table. + insert Inserts new rows of data into an example Cloud Spanner table. + read Reads data in an example Cloud Spanner table. + read-stale Reads data in an example Cloud Spanner table. Options: --help Show help [boolean] @@ -151,14 +139,3 @@ For more information, see https://cloud.google.com/spanner/docs ## Running the tests -1. Set the **GCLOUD_PROJECT** and **GOOGLE_APPLICATION_CREDENTIALS** environment variables. - -1. Run the tests: - - With **npm**: - - npm test - - With **yarn**: - - yarn test diff --git a/spanner/crud.js b/spanner/crud.js index 63d51ea6fc..16b06794d5 100644 --- a/spanner/crud.js +++ b/spanner/crud.js @@ -163,6 +163,50 @@ function readData (instanceId, databaseId) { // [END read_data] } +function readStaleData (instanceId, databaseId) { + // [START read_stale_data] + // Imports the Google Cloud client library + const Spanner = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = Spanner(); + + // Uncomment these lines to specify the instance and database to use + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // Read rows from the Albums table + const albumsTable = database.table('Albums'); + + const query = { + columns: ['SingerId', 'AlbumId', 'AlbumTitle', 'MarketingBudget'], + keySet: { + all: true + } + }; + + const options = { + // Guarantees that all writes that have committed more than 10 seconds ago + // are visible + exactStaleness: 10 + }; + + albumsTable.read(query, options) + .then((results) => { + const rows = results[0]; + + rows.forEach((row) => { + const json = row.toJSON(); + console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}, MarketingBudget: ${json.MarketingBudget ? json.MarketingBudget.value : ''}`); + }); + }); + // [END read_stale_data] +} + const cli = require(`yargs`) .demand(1) .command( @@ -189,10 +233,17 @@ const cli = require(`yargs`) {}, (opts) => readData(opts.instanceName, opts.databaseName) ) + .command( + `read-stale `, + `Reads data in an example Cloud Spanner table.`, + {}, + (opts) => readStaleData(opts.instanceName, opts.databaseName) + ) .example(`node $0 update "my-instance" "my-database"`) .example(`node $0 query "my-instance" "my-database"`) .example(`node $0 insert "my-instance" "my-database"`) .example(`node $0 read "my-instance" "my-database"`) + .example(`node $0 read-stale "my-instance" "my-database"`) .wrap(120) .recommendCommands() .epilogue(`For more information, see https://cloud.google.com/spanner/docs`); diff --git a/spanner/package.json b/spanner/package.json index 1fd115fcd5..cc2c3db35f 100644 --- a/spanner/package.json +++ b/spanner/package.json @@ -22,9 +22,9 @@ }, "devDependencies": { "@google-cloud/nodejs-repo-tools": "1.4.17", - "ava": "0.21.0", + "ava": "0.22.0", "proxyquire": "1.8.0", - "sinon": "3.2.0" + "sinon": "3.2.1" }, "cloud-repo-tools": { "requiresKeyFile": true, diff --git a/spanner/system-test/spanner.test.js b/spanner/system-test/spanner.test.js index ddce1e9a8e..4818e4c611 100644 --- a/spanner/system-test/spanner.test.js +++ b/spanner/system-test/spanner.test.js @@ -53,103 +53,139 @@ test.after.always(async (t) => { // create_database test.serial(`should create an example database`, async (t) => { - const output = await tools.runAsync(`${schemaCmd} createDatabase "${INSTANCE_ID}" "${DATABASE_ID}"`, cwd); - t.true(output.includes(`Waiting for operation on ${DATABASE_ID} to complete...`)); - t.true(output.includes(`Created database ${DATABASE_ID} on instance ${INSTANCE_ID}.`)); + const results = await tools.runAsyncWithIO(`${schemaCmd} createDatabase "${INSTANCE_ID}" "${DATABASE_ID}"`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`Waiting for operation on ${DATABASE_ID} to complete...`)); + t.regex(output, new RegExp(`Created database ${DATABASE_ID} on instance ${INSTANCE_ID}.`)); }); // insert_data test.serial(`should insert rows into an example table`, async (t) => { - let output = await tools.runAsync(`${crudCmd} insert ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`Inserted data.`)); + const results = await tools.runAsyncWithIO(`${crudCmd} insert ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`Inserted data.`)); }); // query_data test.serial(`should query an example table and return matching rows`, async (t) => { - const output = await tools.runAsync(`${crudCmd} query ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); + const results = await tools.runAsyncWithIO(`${crudCmd} query ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); }); // read_data test.serial(`should read an example table`, async (t) => { - const output = await tools.runAsync(`${crudCmd} read ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); + const results = await tools.runAsyncWithIO(`${crudCmd} read ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); }); // add_column test.serial(`should add a column to a table`, async (t) => { - const output = await tools.runAsync(`${schemaCmd} addColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`Waiting for operation to complete...`)); - t.true(output.includes(`Added the MarketingBudget column.`)); + const results = await tools.runAsyncWithIO(`${schemaCmd} addColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`Waiting for operation to complete...`)); + t.regex(output, new RegExp(`Added the MarketingBudget column.`)); }); // update_data test.serial(`should update existing rows in an example table`, async (t) => { - let output = await tools.runAsync(`${crudCmd} update ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`Updated data.`)); + const results = await tools.runAsyncWithIO(`${crudCmd} update ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`Updated data.`)); +}); + +// read_stale_data +test.serial(`should read stale data from an example table`, (t) => { + t.plan(2); + // read-stale-data reads data that is exactly 10 seconds old. So, make sure + // 10 seconds have elapsed since the update_data test. + return new Promise((resolve, reject) => { + setTimeout(async () => { + const results = await tools.runAsyncWithIO(`${crudCmd} read-stale ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + try { + t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget: 100000`)); + t.regex(output, new RegExp(`SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace, MarketingBudget: 500000`)); + resolve(); + } catch (err) { + reject(err); + } + }, 11000); + }); }); // query_data_with_new_column test.serial(`should query an example table with an additional column and return matching rows`, async (t) => { - const output = await tools.runAsync(`${schemaCmd} queryNewColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`SingerId: 1, AlbumId: 1, MarketingBudget: 100000`)); - t.true(output.includes(`SingerId: 2, AlbumId: 2, MarketingBudget: 500000`)); + const results = await tools.runAsyncWithIO(`${schemaCmd} queryNewColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, MarketingBudget: 100000`)); + t.regex(output, new RegExp(`SingerId: 2, AlbumId: 2, MarketingBudget: 500000`)); }); // create_index test.serial(`should create an index in an example table`, async (t) => { - let output = await tools.runAsync(`${indexingCmd} createIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`Waiting for operation to complete...`)); - t.true(output.includes(`Added the AlbumsByAlbumTitle index.`)); + const results = await tools.runAsyncWithIO(`${indexingCmd} createIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`Waiting for operation to complete...`)); + t.regex(output, new RegExp(`Added the AlbumsByAlbumTitle index.`)); }); // create_storing_index test.serial(`should create a storing index in an example table`, async (t) => { - const output = await tools.runAsync(`${indexingCmd} createStoringIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`Waiting for operation to complete...`)); - t.true(output.includes(`Added the AlbumsByAlbumTitle2 index.`)); + const results = await tools.runAsyncWithIO(`${indexingCmd} createStoringIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`Waiting for operation to complete...`)); + t.regex(output, new RegExp(`Added the AlbumsByAlbumTitle2 index.`)); }); // query_data_with_index test.serial(`should query an example table with an index and return matching rows`, async (t) => { - const output = await tools.runAsync(`${indexingCmd} queryIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:`)); + const results = await tools.runAsyncWithIO(`${indexingCmd} queryIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:`)); t.false(output.includes(`AlbumId: 2, AlbumTitle: Total Junk, MarketingBudget:`)); }); test.serial(`should respect query boundaries when querying an example table with an index`, async (t) => { - const output = await tools.runAsync(`${indexingCmd} queryIndex ${INSTANCE_ID} ${DATABASE_ID} -s Ardvark -e Zoo`, cwd); - t.true(output.includes(`AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:`)); - t.true(output.includes(`AlbumId: 2, AlbumTitle: Total Junk, MarketingBudget:`)); + const results = await tools.runAsyncWithIO(`${indexingCmd} queryIndex ${INSTANCE_ID} ${DATABASE_ID} -s Ardvark -e Zoo`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:`)); + t.regex(output, new RegExp(`AlbumId: 2, AlbumTitle: Total Junk, MarketingBudget:`)); }); // read_data_with_index test.serial(`should read an example table with an index`, async (t) => { - const output = await tools.runAsync(`${indexingCmd} readIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`AlbumId: 1, AlbumTitle: Go, Go, Go`)); + const results = await tools.runAsyncWithIO(`${indexingCmd} readIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go`)); }); // read_data_with_storing_index test.serial(`should read an example table with a storing index`, async (t) => { - const output = await tools.runAsync(`${indexingCmd} readStoringIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`AlbumId: 1, AlbumTitle: Go, Go, Go`)); + const results = await tools.runAsyncWithIO(`${indexingCmd} readStoringIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go`)); }); // read_only_transaction test.serial(`should read an example table using transactions`, async (t) => { - const output = await tools.runAsync(`${transactionCmd} readOnly ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); - t.true(output.includes(`Successfully executed read-only transaction.`)); + const results = await tools.runAsyncWithIO(`${transactionCmd} readOnly ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + const output = results.stdout + results.stderr; + t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); + t.regex(output, new RegExp(`Successfully executed read-only transaction.`)); }); // read_write_transaction test.serial(`should read from and write to an example table using transactions`, async (t) => { - let output = await tools.runAsync(`${transactionCmd} readWrite ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`The first album's marketing budget: 100000`)); - t.true(output.includes(`The second album's marketing budget: 500000`)); - t.true(output.includes(`Successfully executed read-write transaction to transfer 200000 from Album 2 to Album 1.`)); - - output = await tools.runAsync(`${schemaCmd} queryNewColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); - t.true(output.includes(`SingerId: 1, AlbumId: 1, MarketingBudget: 300000`)); - t.true(output.includes(`SingerId: 2, AlbumId: 2, MarketingBudget: 300000`)); + let results = await tools.runAsyncWithIO(`${transactionCmd} readWrite ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + let output = results.stdout + results.stderr; + t.regex(output, new RegExp(`The first album's marketing budget: 100000`)); + t.regex(output, new RegExp(`The second album's marketing budget: 500000`)); + t.regex(output, new RegExp(`Successfully executed read-write transaction to transfer 200000 from Album 2 to Album 1.`)); + + results = await tools.runAsyncWithIO(`${schemaCmd} queryNewColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); + output = results.stdout + results.stderr; + t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, MarketingBudget: 300000`)); + t.regex(output, new RegExp(`SingerId: 2, AlbumId: 2, MarketingBudget: 300000`)); }); From e5dabb47dc8154ea6e585b1f3043ae9d15725a1c Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Tue, 12 Sep 2017 11:13:32 -0700 Subject: [PATCH 2/2] Address comments. --- spanner/crud.js | 15 ++++--- spanner/system-test/spanner.test.js | 64 +++++++++++++---------------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/spanner/crud.js b/spanner/crud.js index 16b06794d5..696c6cae14 100644 --- a/spanner/crud.js +++ b/spanner/crud.js @@ -141,7 +141,7 @@ function readData (instanceId, databaseId) { const instance = spanner.instance(instanceId); const database = instance.database(databaseId); - // Read rows from the Albums table + // Reads rows from the Albums table const albumsTable = database.table('Albums'); const query = { @@ -179,7 +179,7 @@ function readStaleData (instanceId, databaseId) { const instance = spanner.instance(instanceId); const database = instance.database(databaseId); - // Read rows from the Albums table + // Reads rows from the Albums table const albumsTable = database.table('Albums'); const query = { @@ -190,8 +190,7 @@ function readStaleData (instanceId, databaseId) { }; const options = { - // Guarantees that all writes that have committed more than 10 seconds ago - // are visible + // Guarantees that all writes committed more than 10 seconds ago are visible exactStaleness: 10 }; @@ -201,7 +200,11 @@ function readStaleData (instanceId, databaseId) { rows.forEach((row) => { const json = row.toJSON(); - console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}, MarketingBudget: ${json.MarketingBudget ? json.MarketingBudget.value : ''}`); + const id = json.SingerId.value; + const album = json.AlbumId.value; + const title = json.AlbumTitle; + const budget = json.MarketingBudget ? json.MarketingBudget.value : ''; + console.log(`SingerId: ${id}, AlbumId: ${album}, AlbumTitle: ${title}, MarketingBudget: ${budget}`); }); }); // [END read_stale_data] @@ -235,7 +238,7 @@ const cli = require(`yargs`) ) .command( `read-stale `, - `Reads data in an example Cloud Spanner table.`, + `Reads stale data in an example Cloud Spanner table.`, {}, (opts) => readStaleData(opts.instanceName, opts.databaseName) ) diff --git a/spanner/system-test/spanner.test.js b/spanner/system-test/spanner.test.js index 4818e4c611..ae505e2d37 100644 --- a/spanner/system-test/spanner.test.js +++ b/spanner/system-test/spanner.test.js @@ -63,36 +63,36 @@ test.serial(`should create an example database`, async (t) => { test.serial(`should insert rows into an example table`, async (t) => { const results = await tools.runAsyncWithIO(`${crudCmd} insert ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`Inserted data.`)); + t.regex(output, /Inserted data\./); }); // query_data test.serial(`should query an example table and return matching rows`, async (t) => { const results = await tools.runAsyncWithIO(`${crudCmd} query ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); + t.regex(output, /SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go/); }); // read_data test.serial(`should read an example table`, async (t) => { const results = await tools.runAsyncWithIO(`${crudCmd} read ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); + t.regex(output, /SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go/); }); // add_column test.serial(`should add a column to a table`, async (t) => { const results = await tools.runAsyncWithIO(`${schemaCmd} addColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`Waiting for operation to complete...`)); - t.regex(output, new RegExp(`Added the MarketingBudget column.`)); + t.regex(output, /Waiting for operation to complete\.\.\./); + t.regex(output, /Added the MarketingBudget column\./); }); // update_data test.serial(`should update existing rows in an example table`, async (t) => { const results = await tools.runAsyncWithIO(`${crudCmd} update ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`Updated data.`)); + t.regex(output, /Updated data\./); }); // read_stale_data @@ -100,92 +100,86 @@ test.serial(`should read stale data from an example table`, (t) => { t.plan(2); // read-stale-data reads data that is exactly 10 seconds old. So, make sure // 10 seconds have elapsed since the update_data test. - return new Promise((resolve, reject) => { - setTimeout(async () => { + return (new Promise((resolve) => setTimeout(resolve, 11000))) + .then(async () => { const results = await tools.runAsyncWithIO(`${crudCmd} read-stale ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - try { - t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget: 100000`)); - t.regex(output, new RegExp(`SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace, MarketingBudget: 500000`)); - resolve(); - } catch (err) { - reject(err); - } - }, 11000); - }); + t.regex(output, /SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget: 100000/); + t.regex(output, /SingerId: 2, AlbumId: 2, AlbumTitle: Forever Hold your Peace, MarketingBudget: 500000/); + }); }); // query_data_with_new_column test.serial(`should query an example table with an additional column and return matching rows`, async (t) => { const results = await tools.runAsyncWithIO(`${schemaCmd} queryNewColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, MarketingBudget: 100000`)); - t.regex(output, new RegExp(`SingerId: 2, AlbumId: 2, MarketingBudget: 500000`)); + t.regex(output, /SingerId: 1, AlbumId: 1, MarketingBudget: 100000/); + t.regex(output, /SingerId: 2, AlbumId: 2, MarketingBudget: 500000/); }); // create_index test.serial(`should create an index in an example table`, async (t) => { const results = await tools.runAsyncWithIO(`${indexingCmd} createIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`Waiting for operation to complete...`)); - t.regex(output, new RegExp(`Added the AlbumsByAlbumTitle index.`)); + t.regex(output, /Waiting for operation to complete\.\.\./); + t.regex(output, /Added the AlbumsByAlbumTitle index\./); }); // create_storing_index test.serial(`should create a storing index in an example table`, async (t) => { const results = await tools.runAsyncWithIO(`${indexingCmd} createStoringIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`Waiting for operation to complete...`)); - t.regex(output, new RegExp(`Added the AlbumsByAlbumTitle2 index.`)); + t.regex(output, /Waiting for operation to complete\.\.\./); + t.regex(output, /Added the AlbumsByAlbumTitle2 index\./); }); // query_data_with_index test.serial(`should query an example table with an index and return matching rows`, async (t) => { const results = await tools.runAsyncWithIO(`${indexingCmd} queryIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:`)); + t.regex(output, /AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:/); t.false(output.includes(`AlbumId: 2, AlbumTitle: Total Junk, MarketingBudget:`)); }); test.serial(`should respect query boundaries when querying an example table with an index`, async (t) => { const results = await tools.runAsyncWithIO(`${indexingCmd} queryIndex ${INSTANCE_ID} ${DATABASE_ID} -s Ardvark -e Zoo`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:`)); - t.regex(output, new RegExp(`AlbumId: 2, AlbumTitle: Total Junk, MarketingBudget:`)); + t.regex(output, /AlbumId: 1, AlbumTitle: Go, Go, Go, MarketingBudget:/); + t.regex(output, /AlbumId: 2, AlbumTitle: Total Junk, MarketingBudget:/); }); // read_data_with_index test.serial(`should read an example table with an index`, async (t) => { const results = await tools.runAsyncWithIO(`${indexingCmd} readIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go`)); + t.regex(output, /AlbumId: 1, AlbumTitle: Go, Go, Go/); }); // read_data_with_storing_index test.serial(`should read an example table with a storing index`, async (t) => { const results = await tools.runAsyncWithIO(`${indexingCmd} readStoringIndex ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`AlbumId: 1, AlbumTitle: Go, Go, Go`)); + t.regex(output, /AlbumId: 1, AlbumTitle: Go, Go, Go/); }); // read_only_transaction test.serial(`should read an example table using transactions`, async (t) => { const results = await tools.runAsyncWithIO(`${transactionCmd} readOnly ${INSTANCE_ID} ${DATABASE_ID}`, cwd); const output = results.stdout + results.stderr; - t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go`)); - t.regex(output, new RegExp(`Successfully executed read-only transaction.`)); + t.regex(output, /SingerId: 1, AlbumId: 1, AlbumTitle: Go, Go, Go/); + t.regex(output, /Successfully executed read-only transaction\./); }); // read_write_transaction test.serial(`should read from and write to an example table using transactions`, async (t) => { let results = await tools.runAsyncWithIO(`${transactionCmd} readWrite ${INSTANCE_ID} ${DATABASE_ID}`, cwd); let output = results.stdout + results.stderr; - t.regex(output, new RegExp(`The first album's marketing budget: 100000`)); - t.regex(output, new RegExp(`The second album's marketing budget: 500000`)); - t.regex(output, new RegExp(`Successfully executed read-write transaction to transfer 200000 from Album 2 to Album 1.`)); + t.regex(output, /The first album's marketing budget: 100000/); + t.regex(output, /The second album's marketing budget: 500000/); + t.regex(output, /Successfully executed read-write transaction to transfer 200000 from Album 2 to Album 1./); results = await tools.runAsyncWithIO(`${schemaCmd} queryNewColumn ${INSTANCE_ID} ${DATABASE_ID}`, cwd); output = results.stdout + results.stderr; - t.regex(output, new RegExp(`SingerId: 1, AlbumId: 1, MarketingBudget: 300000`)); - t.regex(output, new RegExp(`SingerId: 2, AlbumId: 2, MarketingBudget: 300000`)); + t.regex(output, /SingerId: 1, AlbumId: 1, MarketingBudget: 300000/); + t.regex(output, /SingerId: 2, AlbumId: 2, MarketingBudget: 300000/); });