-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Generate end to end tests for retries #1117
base: main
Are you sure you want to change the base?
Changes from 57 commits
e368301
eb94439
95a281c
cdf8149
091b08c
7d5fa9c
1698902
47eb8d8
b9d82f8
3256cdc
b25068a
4bdd4bb
6ef1bd4
8ae117a
e8cd776
148bd42
d3d9fb6
7153399
8db378b
3247820
1bb0c10
22314fe
a9d94b6
d52f504
e23127c
05f9910
dcf21eb
0150022
93bfea3
7a4dbca
7ee6ab8
df82957
05a2996
516b5c1
54f67cd
32b3da5
1230ee7
85f592b
46cb7f2
1e33d95
ec39fd7
1db4368
d379b45
629c3d5
a45675e
0611455
0e61aab
0f01291
054bdf8
f97d4db
f6240ff
f1ae2bf
0c445c4
35bc274
0ddc3d7
5dca0f7
61df10f
f28813d
5714dd6
776e692
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// ** This file is automatically generated by gapic-generator-typescript. ** | ||
// ** https://github.com/googleapis/gapic-generator-typescript ** | ||
// ** All changes to this file may be overwritten. ** | ||
|
||
import {SameCallHandler} from './same-call-handler'; | ||
import {MockService} from '../../../mock-service'; | ||
import {Mutation} from '../../../../../mutation'; | ||
|
||
function rowResponse(rowKey: {}) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there going to be a different "implementation" file for different test cases, e.g, ensuring that streaming errors are gracefully recovered from? If so, I would be tempted to name the implementation file after the specific behaviour it's a test for. Then you'll have a bunch of implementation files for a bunch of different regression tests. |
||
return { | ||
rowKey: Mutation.convertToBytes(rowKey), | ||
familyName: {value: 'family'}, | ||
qualifier: {value: Mutation.convertToBytes('qualifier')}, | ||
valueSize: 0, | ||
timestampMicros: 0, | ||
labels: [], | ||
commitRow: true, | ||
value: Mutation.convertToBytes('value'), | ||
}; | ||
} | ||
|
||
export interface ReadRowsResponse { | ||
row_keys?: string[]; | ||
last_row_key?: string; | ||
end_with_error?: number; | ||
} | ||
|
||
export class ReadRowsHandler extends SameCallHandler { | ||
responses: ReadRowsResponse[]; | ||
request: any = null; | ||
callCount = 0; | ||
message: any; | ||
|
||
// TODO: service and endpoint should be bundled into one object. | ||
constructor( | ||
service: MockService, | ||
endpoint: string, | ||
responses: ReadRowsResponse[], | ||
message?: any | ||
) { | ||
super(service, endpoint); | ||
this.responses = responses; | ||
this.message = Object.assign({}, message); | ||
} | ||
|
||
// TODO: Create interface for this. | ||
callHandler(call: any) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would b good to use a better type for interface {
emit: () => {}
} But I wouldn't be surprised if there's a type available in the |
||
const lastResponse = this.responses[this.callCount - 1]; | ||
if (lastResponse && lastResponse.row_keys) { | ||
const grpcResponse = { | ||
chunks: lastResponse.row_keys.map(rowResponse), | ||
lastScannedRowKey: Mutation.convertToBytes(lastResponse.last_row_key), | ||
}; | ||
call.write(grpcResponse); | ||
} | ||
// Set a timer and send an error if we are confident that all data has been sent back to the user | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const self = this; | ||
const endRequest = (lastResponse: any) => { | ||
const errorCode = lastResponse.end_with_error; | ||
if (errorCode) { | ||
call.emit('error', { | ||
code: errorCode, | ||
details: 'Details for a particular type of error', | ||
}); | ||
} else { | ||
call.end(); | ||
} | ||
}; | ||
const checkCollected = () => { | ||
// Send the error if all data was collected | ||
const lastIndex = self.data.length - 1; | ||
const lastResponse = self.responses[self.callCount - 1]; | ||
if (lastResponse) { | ||
if (lastResponse.row_keys) { | ||
if (self.data[lastIndex].length === lastResponse.row_keys.length) { | ||
endRequest(lastResponse); | ||
} else { | ||
startTimer(); | ||
} | ||
} else { | ||
endRequest(lastResponse); | ||
} | ||
} else { | ||
throw Error('Ran out of requests to send'); | ||
} | ||
}; | ||
const startTimer = () => { | ||
setTimeout(checkCollected, 2500); | ||
}; | ||
startTimer(); | ||
} | ||
|
||
addData(data: string) { | ||
// Add data collected from the stream | ||
const lastIndex = this.data.length - 1; | ||
this.data[lastIndex].push(data); | ||
} | ||
|
||
snapshot(results: any): any { | ||
return { | ||
input: Object.assign( | ||
{responses: this.responses}, | ||
this.message ? {message: this.message} : null | ||
), | ||
output: { | ||
results, | ||
requestData: this.requests(), | ||
}, | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// ** This file is automatically generated by gapic-generator-typescript. ** | ||
// ** https://github.com/googleapis/gapic-generator-typescript ** | ||
// ** All changes to this file may be overwritten. ** | ||
|
||
import {MockService} from '../../../mock-service'; | ||
import {ServiceHandler} from '../service-handler'; | ||
|
||
const equal = require('deep-equal'); | ||
|
||
export abstract class SameCallHandler extends ServiceHandler { | ||
service: MockService; | ||
request: any = null; | ||
requestList: any[] = []; | ||
requestOrder: number[] = []; | ||
callCount = 0; | ||
endpoint: string; | ||
data: string[][] = []; | ||
|
||
protected constructor(service: MockService, endpoint: string) { | ||
super(); | ||
this.endpoint = endpoint; | ||
this.service = service; | ||
} | ||
|
||
setupService(): void { | ||
const handleRpcCall = (call: any) => { | ||
// TODO: Make an abstraction of this | ||
const callRequest = call.request; | ||
const requestIndex = this.requestList.findIndex(request => { | ||
return equal(request, callRequest); | ||
}); | ||
if (requestIndex === -1) { | ||
this.requestList.push(callRequest); | ||
this.requestOrder.push(this.requestList.length - 1); | ||
} else { | ||
this.requestOrder.push(requestIndex); | ||
} | ||
this.request = callRequest; | ||
this.callCount++; | ||
this.data.push([]); | ||
this.callHandler(call); | ||
}; | ||
this.service.setService({ | ||
// Abstraction: Always emit error | ||
[this.endpoint]: handleRpcCall, | ||
}); | ||
} | ||
|
||
getData() { | ||
return this.data; | ||
} | ||
|
||
requests() { | ||
return { | ||
requests: Object.assign({}, this.requestList), | ||
requestOrder: this.requestOrder, | ||
callCount: this.callCount, | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// ** This file is automatically generated by gapic-generator-typescript. ** | ||
// ** https://github.com/googleapis/gapic-generator-typescript ** | ||
// ** All changes to this file may be overwritten. ** | ||
|
||
import {grpc} from 'google-gax'; | ||
import {SameCallHandler} from './same-call-handler'; | ||
import {MockService} from '../../../mock-service'; | ||
import {Row} from '../../../../../row'; | ||
|
||
export class SendErrorHandler extends SameCallHandler { | ||
code: grpc.status; | ||
request: any = null; | ||
callCount = 0; | ||
message: any; | ||
|
||
// TODO: service and endpoint should be bundled into one object. | ||
constructor( | ||
service: MockService, | ||
endpoint: string, | ||
code: grpc.status, | ||
message?: any | ||
) { | ||
super(service, endpoint); | ||
this.code = code; | ||
this.message = Object.assign({}, message); | ||
} | ||
|
||
callHandler(call: any) { | ||
call.emit('error', { | ||
code: this.code, | ||
details: 'Details for a particular type of error', | ||
}); | ||
} | ||
|
||
addData(data: string) { | ||
const lastIndex = this.data.length - 1; | ||
this.data[lastIndex].push(data); | ||
} | ||
|
||
snapshot(results: any): any { | ||
return { | ||
input: Object.assign( | ||
{code: this.code}, | ||
this.message ? {message: this.message} : null | ||
), | ||
output: { | ||
results, | ||
requestData: this.requests(), | ||
}, | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// ** This file is automatically generated by gapic-generator-typescript. ** | ||
// ** https://github.com/googleapis/gapic-generator-typescript ** | ||
// ** All changes to this file may be overwritten. ** | ||
|
||
export abstract class ServiceHandler { | ||
/* | ||
callHandler accepts a grpc call and provides behaviour for that grpc call | ||
which may involve sending errors or data back to the client for | ||
example. | ||
*/ | ||
abstract callHandler(call: any): void; | ||
|
||
/* | ||
snapshotOutput is used to provide a custom json object that represents the | ||
results of the test that was run with this service handler. | ||
*/ | ||
abstract snapshot(results: any): any; | ||
|
||
/* | ||
setupService is called to setup the service we use for collecting data about | ||
a running test. | ||
*/ | ||
abstract setupService(): void; | ||
|
||
/* | ||
addData is called to add data which will be reported in the snapshot later on. | ||
*/ | ||
abstract addData(data: string): void; | ||
|
||
/* | ||
getData is called to get all data which was collected from requests. | ||
*/ | ||
abstract getData(): string[][]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// ** This file is automatically generated by gapic-generator-typescript. ** | ||
// ** https://github.com/googleapis/gapic-generator-typescript ** | ||
// ** All changes to this file may be overwritten. ** | ||
|
||
import {GetRowsOptions, Table} from '../../../../../table'; | ||
import internal = require('stream'); | ||
import {StreamFetcher} from '../stream-fetcher'; | ||
|
||
export class ReadRowsFetcher extends StreamFetcher { | ||
table: Table; | ||
opts: GetRowsOptions; | ||
|
||
constructor(table: Table, opts?: GetRowsOptions) { | ||
super(); | ||
this.opts = opts ?? {}; | ||
this.table = table; | ||
} | ||
|
||
fetchStream(): internal.PassThrough { | ||
return this.table.createReadStream(this.opts); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// ** This file is automatically generated by gapic-generator-typescript. ** | ||
// ** https://github.com/googleapis/gapic-generator-typescript ** | ||
// ** All changes to this file may be overwritten. ** | ||
|
||
import internal = require('stream'); | ||
|
||
export abstract class StreamFetcher { | ||
abstract fetchStream(): internal.PassThrough; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have caught this in the last review, since these servers (I believe) will only be used in testing, I'd be tempted to have them in
test/util
, rather thansrc/util
. I think ofsrc
as being components of the library itself, vs., components used during testing.