Skip to content

Commit cd43c9f

Browse files
committed
fix(storage): unit test
1 parent baf084f commit cd43c9f

6 files changed

Lines changed: 78 additions & 9 deletions

File tree

handwritten/storage/conformance-test/conformanceCommon.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1716
import * as jsonToNodeApiMapping from './test-data/retryInvocationMap.json';
1817
import * as libraryMethods from './libraryMethods';
1918
import {Bucket, File, Gaxios, HmacKey, Notification, Storage} from '../src';
@@ -138,6 +137,19 @@ export function executeScenario(testCase: RetryTestCase) {
138137
throw new Error('Failed to get a valid test ID from test bench.');
139138
}
140139

140+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
141+
const internalGaxios = (storageTransport as any).authClient
142+
?.gaxiosInstance;
143+
144+
if (internalGaxios) {
145+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
146+
internalGaxios.interceptors.request.use((config: any) => {
147+
config.headers = config.headers || {};
148+
config.headers['x-retry-test-id'] = creationResult.id;
149+
return config;
150+
});
151+
}
152+
141153
// Create a Proxy around rawStorageTransport to intercept makeRequest
142154
storageTransport = new Proxy(rawStorageTransport, {
143155
get(target, prop, receiver) {

handwritten/storage/conformance-test/libraryMethods.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,15 @@ export async function create(options: ConformanceTestOptions) {
121121
method: 'GET',
122122
url: `storage/v1/b/${bucketName}`,
123123
};
124-
await options.storageTransport.makeRequest(existsReq);
125-
bucketExists = true;
124+
try {
125+
await options.storageTransport.makeRequest(existsReq);
126+
bucketExists = true;
127+
} catch (e) {
128+
const err = e as GaxiosError;
129+
if (err.response?.status !== 404) {
130+
throw e;
131+
}
132+
}
126133

127134
if (bucketExists) {
128135
let pageToken: string | undefined = undefined;

handwritten/storage/src/resumable-upload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,8 @@ export class Upload extends Writable {
12081208
code: resp.status.toString(),
12091209
message: resp.statusText,
12101210
name: resp.statusText,
1211+
config: resp.config,
1212+
response: resp,
12111213
} as GaxiosError)
12121214
) {
12131215
void this.attemptDelayedRetry(resp);

handwritten/storage/src/storage-transport.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ export class StorageTransport {
174174
noResponseRetries: this.retryOptions.maxRetries,
175175
maxRetryDelay: this.retryOptions.maxRetryDelay,
176176
retryDelayMultiplier: this.retryOptions.retryDelayMultiplier,
177-
shouldRetry: this.retryOptions.retryableErrorFn,
177+
shouldRetry: err => {
178+
return !!this.retryOptions.retryableErrorFn?.(err);
179+
},
178180
totalTimeout: this.retryOptions.totalTimeout,
179181
},
180182
...reqOpts,

handwritten/storage/src/storage.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,25 +306,55 @@ export const RETRYABLE_ERR_FN_DEFAULT = function (err?: GaxiosError) {
306306
if (!err || !err.config) return false;
307307

308308
const method = (err.config.method || 'GET').toUpperCase();
309+
const url = err.config.url || '';
309310
const params = err.config.params || {};
310311

312+
const status = err.response?.status;
313+
if (status && [401, 405, 412].includes(status)) return false;
314+
311315
const hasPrecondition = !!(
312316
params.ifGenerationMatch !== undefined ||
313317
params.ifMetagenerationMatch !== undefined ||
314318
params.ifSourceGenerationMatch !== undefined
315319
);
316320

317-
const isIdempotent =
318-
['GET', 'HEAD', 'OPTIONS', 'TRACE'].includes(method) || !!hasPrecondition;
321+
const isGet = method === 'GET';
322+
const isHead = method === 'HEAD';
323+
const isPut = method === 'PUT';
324+
const isDelete = method === 'DELETE';
325+
const isPost = method === 'POST';
326+
327+
let isIdempotent = false;
328+
if (isGet || isHead) {
329+
isIdempotent = true;
330+
} else if (hasPrecondition) {
331+
isIdempotent = true;
332+
} else if (isDelete) {
333+
if (!url.toString().includes('/o/')) {
334+
isIdempotent = true;
335+
}
336+
} else if (isPut) {
337+
const isSpecialMutation =
338+
url.toString().includes('/iam') || url.toString().includes('/hmacKeys/');
339+
if (!isSpecialMutation) {
340+
isIdempotent = true;
341+
}
342+
} else if (isPost) {
343+
if (url.toString().includes('/o') || url.toString().includes('/v1/b')) {
344+
isIdempotent = true;
345+
}
346+
}
319347
if (!isIdempotent) return false;
320348

321349
const isConnectionProblem = (reason: string) => {
322350
return (
323-
reason.includes('eai_again') || // DNS lookup error
351+
reason.includes('eai_again') ||
324352
reason === 'econnreset' ||
325353
reason === 'unexpected connection closure' ||
326354
reason === 'epipe' ||
327-
reason === 'socket connection timeout'
355+
reason === 'socket connection timeout' ||
356+
reason === 'econnrefused' ||
357+
reason === 'etimedout'
328358
);
329359
};
330360

@@ -349,6 +379,14 @@ export const RETRYABLE_ERR_FN_DEFAULT = function (err?: GaxiosError) {
349379
return true;
350380
}
351381
}
382+
383+
const message = (err.message || '').toLowerCase();
384+
if (
385+
message.includes('unexpected end of json input') ||
386+
message.includes('the operation was aborted')
387+
) {
388+
return true;
389+
}
352390
}
353391
return false;
354392
};

handwritten/storage/test/resumable-upload.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1931,7 +1931,15 @@ describe('resumable-upload', () => {
19311931
});
19321932

19331933
describe('500s', () => {
1934-
const RESP = {status: 500, data: 'error message from server'};
1934+
const RESP = {
1935+
status: 500,
1936+
statusText: 'Internal Server Error',
1937+
data: 'error message from server',
1938+
config: {
1939+
method: 'POST',
1940+
url: `${BASE_URI}/${BUCKET}/o`,
1941+
},
1942+
};
19351943

19361944
it('should increase the retry count if less than limit', () => {
19371945
up.getRetryDelay = () => 1;

0 commit comments

Comments
 (0)