Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit cf6906b

Browse files
danieljbruceleahecolegcf-owl-bot[bot]
authored
test: Make the ReadRows service in tests more modular (#1462)
* test: begin to refactor the mock service (#1447) * Use prettyPrintRequest from readRowsImpl1 * Define interfaces for creating a Bigtable service * Remove keyFrom and keyTo from each test * Define the service inline for standard keys errors * Add a comment about the refactor steps I am doing * Add a header to the service parameters file * Use the ChunkGeneratorParameters interface * Simplify the diff by creating local variables * Remove comment * Eliminate chunks per response constant * Change imports * Replace with as ServerImplementationInterface * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Add a second test for an error at a random pos * Add some comments for documentation * Chunk generation - add the parameter * Added comments to all the interfaces * Fix a regression bug from the merge --------- Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * test: Eliminate the second ReadRows Service and use a generalized version of the first ReadRows (#1457) * Use prettyPrintRequest from readRowsImpl1 * Define interfaces for creating a Bigtable service * Remove keyFrom and keyTo from each test * Define the service inline for standard keys errors * Add a comment about the refactor steps I am doing * Add a header to the service parameters file * Use the ChunkGeneratorParameters interface * Simplify the diff by creating local variables * Remove comment * Eliminate chunks per response constant * Change imports * Set up the second ReadRows service to use params * Remove duplicate copy of generate chunks from serv * Remove second copy of isKeyInRowSet * Eliminate duplicate copies of the debug log * Fix a bug for the to and from service parameters Only ignore keyFrom and keyTo when they are undefined and not just when they are Falsy. * Add cancel back into the mock service for testing * Add variables to match service 1 * Add one more check to match the other service * Remove usages of ReadRows2Impl * Remove the new service The old service has been generalized enough to mock correct server behaviour. * Moved the position of the comment for 150 rows * Eliminate setting keyTo and keyFrom they are undefined anyway. * Add a second test for an error at a random pos * Add some comments for documentation * Chunk generation - add the parameter * Added comments to all the interfaces * Delete file * Change splitted to split * Remove export * Don’t rename the interfaces There is no point * Increase Windows timeout * Provide functions to eliminate if statements * Eliminate the if statements Make a direct call to generateChunksFromRequest * Revert "Eliminate the if statements" This reverts commit 0996e89. * Revert "Provide functions to eliminate if statements" This reverts commit 4a4761f. * Change any’s to string | undefined * Eliminate duplicate code for setting timeouts * remove only * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * Update test/readrows.ts Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> --------- Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> * test: Eliminate repeated if statements in the ReadRows mock service reducing the size of it significantly (#1460) * Use prettyPrintRequest from readRowsImpl1 * Define interfaces for creating a Bigtable service * Remove keyFrom and keyTo from each test * Define the service inline for standard keys errors * Add a comment about the refactor steps I am doing * Add a header to the service parameters file * Use the ChunkGeneratorParameters interface * Simplify the diff by creating local variables * Remove comment * Eliminate chunks per response constant * Change imports * Set up the second ReadRows service to use params * Remove duplicate copy of generate chunks from serv * Remove second copy of isKeyInRowSet * Eliminate duplicate copies of the debug log * Fix a bug for the to and from service parameters Only ignore keyFrom and keyTo when they are undefined and not just when they are Falsy. * Add cancel back into the mock service for testing * Add variables to match service 1 * Add one more check to match the other service * Remove usages of ReadRows2Impl * Remove the new service The old service has been generalized enough to mock correct server behaviour. * Moved the position of the comment for 150 rows * Eliminate setting keyTo and keyFrom they are undefined anyway. * Add a second test for an error at a random pos * Add some comments for documentation * Chunk generation - add the parameter * Added comments to all the interfaces * Group the property getter into a function * Group key selection into functions * Fix typo: default key * Documentation for isInRowSet * Eliminate variable - move function inline * Omit optional selector on stream * Create ReadRowsWritableStream interface * Use new interface, remove ServerWritableStream * Don’t pass the whole stream into helpers * Add a function for generating the chunks * The debug log should be a pass through parameter * Solve compiler errors resulting immediately from merge * Add debugLog parameter to various functions * Add return type * Remove exports - functions are only used in this module * Revise merge correction * Remove TODO for the stream type * Update the getKeyValue function * Eliminate place where debug log is needed * Run linter * test: Break the ReadRows service down into classes instead of a single long function (#1461) * Use prettyPrintRequest from readRowsImpl1 * Define interfaces for creating a Bigtable service * Remove keyFrom and keyTo from each test * Define the service inline for standard keys errors * Add a comment about the refactor steps I am doing * Add a header to the service parameters file * Use the ChunkGeneratorParameters interface * Simplify the diff by creating local variables * Remove comment * Eliminate chunks per response constant * Change imports * Set up the second ReadRows service to use params * Remove duplicate copy of generate chunks from serv * Remove second copy of isKeyInRowSet * Eliminate duplicate copies of the debug log * Fix a bug for the to and from service parameters Only ignore keyFrom and keyTo when they are undefined and not just when they are Falsy. * Add cancel back into the mock service for testing * Add variables to match service 1 * Add one more check to match the other service * Remove usages of ReadRows2Impl * Remove the new service The old service has been generalized enough to mock correct server behaviour. * Moved the position of the comment for 150 rows * Eliminate setting keyTo and keyFrom they are undefined anyway. * Add a second test for an error at a random pos * Add some comments for documentation * Chunk generation - add the parameter * Added comments to all the interfaces * Group the property getter into a function * Group key selection into functions * Fix typo: default key * Documentation for isInRowSet * Eliminate variable - move function inline * Omit optional selector on stream * Create ReadRowsWritableStream interface * Use new interface, remove ServerWritableStream * Don’t pass the whole stream into helpers * Add a function for generating the chunks * The debug log should be a pass through parameter * Add some TODOs about how to address this next * Pull send response into a class * Create a dedicated class for defining the service * Change name to readRowsRequestHandler * Pull the function that generates the chunks into Separate module * Generate documentation * Add more documentation to the Service class * Add debugLog as a method parameter * Solve compiler errors resulting immediately from merge * Add debugLog parameter to various functions * Add return type * Remove exports - functions are only used in this module * Revise merge correction * Remove TODO for the stream type * Update the getKeyValue function * Eliminate place where debug log is needed * Run linter * Eliminate the function that creates a service Add a factory method instead * Add documentation for the constructor * Remove the TODO * Set the timeout for the test * Increase the timeout * Eliminate ternary statement * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Add a couple of comments * re-write the ternary expression --------- Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 433a8e3 commit cf6906b

4 files changed

Lines changed: 409 additions & 507 deletions

File tree

test/readrows.ts

Lines changed: 125 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,50 @@
1313
// limitations under the License.
1414

1515
import {before, describe, it} from 'mocha';
16-
import {Bigtable, protos, Row, Table} from '../src';
16+
import {Bigtable, Row, Table} from '../src';
1717
import * as assert from 'assert';
1818
import {Transform, PassThrough, pipeline} from 'stream';
1919

2020
import {GoogleError} from 'google-gax';
2121
import {MockServer} from '../src/util/mock-servers/mock-server';
2222
import {BigtableClientMockService} from '../src/util/mock-servers/service-implementations/bigtable-client-mock-service';
2323
import {MockService} from '../src/util/mock-servers/mock-service';
24-
import {debugLog, readRowsImpl} from './utils/readRowsImpl';
25-
import {ServerWritableStream, UntypedHandleCall} from '@grpc/grpc-js';
26-
import {readRowsImpl2} from './utils/readRowsImpl2';
24+
import {ReadRowsImpl} from './utils/readRowsImpl';
25+
26+
import {
27+
ReadRowsServiceParameters,
28+
ReadRowsWritableStream,
29+
} from '../test/utils/readRowsServiceParameters';
30+
import * as mocha from 'mocha';
31+
32+
const DEBUG = process.env.BIGTABLE_TEST_DEBUG === 'true';
33+
34+
function debugLog(text: string) {
35+
if (DEBUG) {
36+
console.log(text);
37+
}
38+
}
39+
40+
// Define parameters for a standard Bigtable Mock service
41+
const VALUE_SIZE = 1024 * 1024;
42+
// we want each row to be split into 2 chunks of different sizes
43+
const CHUNK_SIZE = 1023 * 1024 - 1;
44+
const CHUNKS_PER_RESPONSE = 10;
45+
const STANDARD_KEY_FROM = 0;
46+
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
47+
const STANDARD_KEY_TO = 1000;
48+
const STANDARD_SERVICE_WITHOUT_ERRORS: ReadRowsServiceParameters = {
49+
keyFrom: STANDARD_KEY_FROM,
50+
keyTo: STANDARD_KEY_TO,
51+
valueSize: VALUE_SIZE,
52+
chunkSize: CHUNK_SIZE,
53+
chunksPerResponse: CHUNKS_PER_RESPONSE,
54+
debugLog,
55+
};
2756

2857
type PromiseVoid = Promise<void>;
2958
interface ServerImplementationInterface {
30-
(
31-
server: ServerWritableStream<
32-
protos.google.bigtable.v2.IReadRowsRequest,
33-
protos.google.bigtable.v2.IReadRowsResponse
34-
>
35-
): PromiseVoid;
59+
(server: ReadRowsWritableStream): PromiseVoid;
3660
}
3761

3862
describe('Bigtable/ReadRows', () => {
@@ -53,15 +77,21 @@ describe('Bigtable/ReadRows', () => {
5377
service = new BigtableClientMockService(server);
5478
});
5579

56-
it('should create read stream and read synchronously', function (done) {
57-
this.timeout(60000);
80+
// helper function because some tests run slower
81+
// on Windows and need a longer timeout
82+
function setWindowsTestTimeout(test: mocha.Context) {
83+
if (process.platform === 'win32') {
84+
test.timeout(60000); // it runs much slower on Windows!
85+
}
86+
}
5887

59-
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
60-
const keyFrom = 0;
61-
const keyTo = 1000;
88+
it('should create read stream and read synchronously', function (done) {
89+
setWindowsTestTimeout(this);
6290

6391
service.setService({
64-
ReadRows: readRowsImpl(keyFrom, keyTo) as any,
92+
ReadRows: ReadRowsImpl.createService(
93+
STANDARD_SERVICE_WITHOUT_ERRORS
94+
) as ServerImplementationInterface,
6595
});
6696

6797
let receivedRowCount = 0;
@@ -81,19 +111,17 @@ describe('Bigtable/ReadRows', () => {
81111
debugLog(`received row key ${key}`);
82112
});
83113
readStream.on('end', () => {
84-
assert.strictEqual(receivedRowCount, keyTo - keyFrom);
85-
assert.strictEqual(lastKeyReceived, keyTo - 1);
114+
assert.strictEqual(receivedRowCount, STANDARD_KEY_TO - STANDARD_KEY_FROM);
115+
assert.strictEqual(lastKeyReceived, STANDARD_KEY_TO - 1);
86116
done();
87117
});
88118
});
89119

90120
it('should create read stream and read synchronously using Transform stream', done => {
91-
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
92-
const keyFrom = 0;
93-
const keyTo = 1000;
94-
95121
service.setService({
96-
ReadRows: readRowsImpl(keyFrom, keyTo) as any,
122+
ReadRows: ReadRowsImpl.createService(
123+
STANDARD_SERVICE_WITHOUT_ERRORS
124+
) as ServerImplementationInterface,
97125
});
98126

99127
let receivedRowCount = 0;
@@ -128,25 +156,20 @@ describe('Bigtable/ReadRows', () => {
128156
debugLog(`received row key ${key}`);
129157
});
130158
passThrough.on('end', () => {
131-
assert.strictEqual(receivedRowCount, keyTo - keyFrom);
132-
assert.strictEqual(lastKeyReceived, keyTo - 1);
159+
assert.strictEqual(receivedRowCount, STANDARD_KEY_TO - STANDARD_KEY_FROM);
160+
assert.strictEqual(lastKeyReceived, STANDARD_KEY_TO - 1);
133161
done();
134162
});
135163

136164
pipeline(readStream, transform, passThrough, () => {});
137165
});
138166

139167
it('should create read stream and read asynchronously using Transform stream', function (done) {
140-
if (process.platform === 'win32') {
141-
this.timeout(60000); // it runs much slower on Windows!
142-
}
143-
144-
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
145-
const keyFrom = 0;
146-
const keyTo = 1000;
147-
168+
setWindowsTestTimeout(this);
148169
service.setService({
149-
ReadRows: readRowsImpl(keyFrom, keyTo) as any,
170+
ReadRows: ReadRowsImpl.createService(
171+
STANDARD_SERVICE_WITHOUT_ERRORS
172+
) as ServerImplementationInterface,
150173
});
151174

152175
let receivedRowCount = 0;
@@ -183,23 +206,22 @@ describe('Bigtable/ReadRows', () => {
183206
debugLog(`received row key ${key}`);
184207
});
185208
passThrough.on('end', () => {
186-
assert.strictEqual(receivedRowCount, keyTo - keyFrom);
187-
assert.strictEqual(lastKeyReceived, keyTo - 1);
209+
assert.strictEqual(receivedRowCount, STANDARD_KEY_TO - STANDARD_KEY_FROM);
210+
assert.strictEqual(lastKeyReceived, STANDARD_KEY_TO - 1);
188211
done();
189212
});
190213

191214
pipeline(readStream, transform, passThrough, () => {});
192215
});
193216

194217
it('should be able to stop reading from the read stream', done => {
195-
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
196-
const keyFrom = 0;
197-
const keyTo = 1000;
198218
// pick any key to stop after
199219
const stopAfter = 42;
200220

201221
service.setService({
202-
ReadRows: readRowsImpl(keyFrom, keyTo) as any,
222+
ReadRows: ReadRowsImpl.createService(
223+
STANDARD_SERVICE_WITHOUT_ERRORS
224+
) as ServerImplementationInterface,
203225
});
204226

205227
let receivedRowCount = 0;
@@ -232,18 +254,14 @@ describe('Bigtable/ReadRows', () => {
232254

233255
// TODO: enable after https://github.com/googleapis/nodejs-bigtable/issues/1286 is fixed
234256
it('should be able to stop reading from the read stream when reading asynchronously', function (done) {
235-
if (process.platform === 'win32') {
236-
this.timeout(600000); // it runs much slower on Windows!
237-
}
238-
239-
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
240-
const keyFrom = 0;
241-
const keyTo = 1000;
257+
setWindowsTestTimeout(this);
242258
// pick any key to stop after
243259
const stopAfter = 420;
244260

245261
service.setService({
246-
ReadRows: readRowsImpl(keyFrom, keyTo) as any,
262+
ReadRows: ReadRowsImpl.createService(
263+
STANDARD_SERVICE_WITHOUT_ERRORS
264+
) as ServerImplementationInterface,
247265
});
248266

249267
let receivedRowCount = 0;
@@ -294,61 +312,77 @@ describe('Bigtable/ReadRows', () => {
294312
pipeline(readStream, transform, passThrough, () => {});
295313
});
296314

297-
it('should silently resume after server or network error', done => {
298-
// 1000 rows must be enough to reproduce issues with losing the data and to create backpressure
299-
const keyFrom = 0;
300-
const keyTo = 1000;
301-
// the server will error after sending this chunk (not row)
302-
const errorAfterChunkNo = 423;
303-
304-
service.setService({
305-
ReadRows: readRowsImpl(keyFrom, keyTo, errorAfterChunkNo) as any,
306-
});
307-
308-
let receivedRowCount = 0;
309-
let lastKeyReceived: number | undefined;
310-
311-
const readStream = table.createReadStream();
312-
readStream.on('error', (err: GoogleError) => {
313-
done(err);
314-
});
315-
readStream.on('data', (row: Row) => {
316-
++receivedRowCount;
317-
const key = parseInt(row.id);
318-
if (lastKeyReceived && key <= lastKeyReceived) {
319-
done(new Error('Test error: keys are not in order'));
320-
}
321-
lastKeyReceived = key;
322-
debugLog(`received row key ${key}`);
315+
describe('should silently resume after server or network error', () => {
316+
function runTest(done: Mocha.Done, errorAfterChunkNo: number) {
317+
service.setService({
318+
ReadRows: ReadRowsImpl.createService({
319+
keyFrom: STANDARD_KEY_FROM,
320+
keyTo: STANDARD_KEY_TO,
321+
valueSize: VALUE_SIZE,
322+
chunkSize: CHUNK_SIZE,
323+
chunksPerResponse: CHUNKS_PER_RESPONSE,
324+
errorAfterChunkNo,
325+
debugLog,
326+
}) as ServerImplementationInterface,
327+
});
328+
let receivedRowCount = 0;
329+
let lastKeyReceived: number | undefined;
330+
331+
const readStream = table.createReadStream();
332+
readStream.on('error', (err: GoogleError) => {
333+
done(err);
334+
});
335+
readStream.on('data', (row: Row) => {
336+
++receivedRowCount;
337+
const key = parseInt(row.id);
338+
if (lastKeyReceived && key <= lastKeyReceived) {
339+
done(new Error('Test error: keys are not in order'));
340+
}
341+
lastKeyReceived = key;
342+
debugLog(`received row key ${key}`);
343+
});
344+
readStream.on('end', () => {
345+
assert.strictEqual(
346+
receivedRowCount,
347+
STANDARD_KEY_TO - STANDARD_KEY_FROM
348+
);
349+
assert.strictEqual(lastKeyReceived, STANDARD_KEY_TO - 1);
350+
done();
351+
});
352+
}
353+
it('with an error at a fixed position', function (done) {
354+
setWindowsTestTimeout(this);
355+
// Emits an error after enough chunks have been pushed to create back pressure
356+
runTest(done, 423);
323357
});
324-
readStream.on('end', () => {
325-
assert.strictEqual(receivedRowCount, keyTo - keyFrom);
326-
assert.strictEqual(lastKeyReceived, keyTo - 1);
327-
done();
358+
it('with an error at a random position', function (done) {
359+
this.timeout(200000);
360+
// Emits an error after a random number of chunks.
361+
const errorAfterChunkNo = Math.floor(Math.random() * 1000);
362+
runTest(done, errorAfterChunkNo);
328363
});
329364
});
330-
331-
it('should return row data in the right order', done => {
332-
// 150 rows must be enough to reproduce issues with losing the data and to create backpressure
333-
const keyFrom = undefined;
334-
const keyTo = undefined;
335-
// the server will error after sending this chunk (not row)
336-
const errorAfterChunkNo = 100;
365+
it('should return row data in the right order', function (done) {
366+
setWindowsTestTimeout(this);
337367
const dataResults = [];
338368

339-
// TODO: Do not use `any` here, make it a more specific type and address downstream implications on the mock server.
369+
// keyTo and keyFrom are not provided so they will be determined from
370+
// the request that is passed in.
340371
service.setService({
341-
ReadRows: readRowsImpl2(
342-
keyFrom,
343-
keyTo,
344-
errorAfterChunkNo
345-
) as ServerImplementationInterface,
372+
ReadRows: ReadRowsImpl.createService({
373+
errorAfterChunkNo: 100, // the server will error after sending this chunk (not row)
374+
valueSize: 1,
375+
chunkSize: 1,
376+
chunksPerResponse: 1,
377+
debugLog,
378+
}) as ServerImplementationInterface,
346379
});
347380
const sleep = (ms: number) => {
348381
return new Promise(resolve => setTimeout(resolve, ms));
349382
};
350383
(async () => {
351384
try {
385+
// 150 rows must be enough to reproduce issues with losing the data and to create backpressure
352386
const stream = table.createReadStream({
353387
start: '00000000',
354388
end: '00000150',

0 commit comments

Comments
 (0)