Skip to content
Merged
9 changes: 9 additions & 0 deletions sdk/test-utils/recorder-new/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## 1.0.0 (Unreleased)

## 2021-12-27

- Allows passing `undefined` as keys in the sanitizer options so that devs don't have to add additional checks if a certain env variable exists in playback.
- Exports `delay`
- waits for expected time in record/live modes
- no-op in playback

[#19561](https://github.com/Azure/azure-sdk-for-js/pull/19561)

## 2021-12-17

- Refactoring the test proxy http clients for better clarity for the end users [#19446](https://github.com/Azure/azure-sdk-for-js/pull/19446)
Expand Down
1 change: 1 addition & 0 deletions sdk/test-utils/recorder-new/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export {
isRecordMode
} from "./utils/utils";
export { env } from "./utils/env";
export { delay } from "./utils/delay";
36 changes: 31 additions & 5 deletions sdk/test-utils/recorder-new/src/sanitizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { getRealAndFakePairs } from "./utils/connectionStringHelpers";
import { paths } from "./utils/paths";
import {
getTestMode,
isRecordMode,
ProxyToolSanitizers,
RecorderError,
RegexSanitizer,
sanitizerKeywordMapping,
SanitizerOptions
} from "./utils/utils";
Expand Down Expand Up @@ -74,12 +76,27 @@ export class Sanitizer {
const replacers = options[prop];
if (replacers) {
return Promise.all(
replacers.map((replacer: unknown) =>
this.addSanitizer({
replacers.map((replacer: RegexSanitizer) => {
if (
// sanitizers where the "regex" is a required attribute
[
"bodyKeySanitizers",
"bodyRegexSanitizers",
"generalRegexSanitizers",
"uriRegexSanitizers"
].includes(prop) &&
!replacer.regex
) {
if (!isRecordMode()) return;
throw new RecorderError(
`Attempted to add an invalid sanitizer - ${JSON.stringify(replacer)}`
);
}
return this.addSanitizer({
sanitizer: sanitizerKeywordMapping[prop],
body: JSON.stringify(replacer)
})
)
});
})
);
} else return;
})
Expand Down Expand Up @@ -140,9 +157,18 @@ export class Sanitizer {
* - generalRegexSanitizer is applied for each of the parts with the real and fake values that are parsed
*/
async addConnectionStringSanitizer(
actualConnString: string,
actualConnString: string | undefined,
fakeConnString: string
): Promise<void> {
if (!actualConnString) {
if (!isRecordMode()) return;
throw new RecorderError(
`Attempted to add an invalid sanitizer - ${JSON.stringify({
actualConnString: actualConnString,
fakeConnString: fakeConnString
})}`
);
}
// extract connection string parts and match call
const pairsMatched = getRealAndFakePairs(actualConnString, fakeConnString);
await this.addSanitizers({
Expand Down
18 changes: 18 additions & 0 deletions sdk/test-utils/recorder-new/src/utils/delay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { isPlaybackMode } from "./utils";

/**
* Usage - `await delay(<milliseconds>)`
* This `delay` has no effect if the `TEST_MODE` is `"playback"`.
* If the `TEST_MODE` is not `"playback"`, `delay` is a wrapper for setTimeout that resolves a promise after t milliseconds.
*
* @param {number} milliseconds The number of milliseconds to be delayed.
*/
export function delay(milliseconds: number): Promise<void> | void {
if (isPlaybackMode()) {
return;
}
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
24 changes: 15 additions & 9 deletions sdk/test-utils/recorder-new/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ export interface RegexSanitizer {
*/
value: string;
/**
* A regex. Can be defined as a simple regex replace OR if groupForReplace is set, a subsitution operation.
* A regex. Can be defined as a simple regex replace OR if groupForReplace is set, a substitution operation.
*/
regex: string;
regex?: string;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it optional because it is not used in playback mode? if regex is optional, is value optional too?

Copy link
Copy Markdown
Contributor Author

@HarshaNalluru HarshaNalluru Dec 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only meant for convenience while writing the tests. If regex is undefined, the sanitizer won't be added.

For value, typically it is hard-coded and is a non-null value.
If it turned out to be undefined, it is most likely that the user is doing something wrong/unexpected, hence I didn't think of allowing that.

/**
* The capture group that needs to be operated upon. Do not set if you're invoking a simple replacement operation.
*/
Expand Down Expand Up @@ -129,15 +129,11 @@ interface BodyKeySanitizer extends RegexSanitizer {
* 2) To do a simple regex replace operation, define arguments "key", "value", and "regex"
* 3) To do a targeted substitution of a specific group, define all arguments "key", "value", and "regex"
*/
interface HeaderRegexSanitizer extends Omit<RegexSanitizer, "regex"> {
interface HeaderRegexSanitizer extends RegexSanitizer {
/**
* The name of the header we're operating against.
*/
key: string;
/**
* A regex. Can be defined as a simple regex replace OR if groupForReplace is set, a subsitution operation.
*/
regex?: string;
}
/**
* Internally,
Expand All @@ -149,7 +145,7 @@ interface ConnectionStringSanitizer {
/**
* Real connection string with all the secrets
*/
actualConnString: string;
actualConnString?: string;
/**
* Fake connection string - with all the parts of the connection string mapped to fake values
*/
Expand Down Expand Up @@ -181,7 +177,17 @@ export interface SanitizerOptions {
* Regardless, there are examples present in `recorder-new/test/testProxyTests.spec.ts`.
*/
bodyRegexSanitizers?: RegexSanitizer[];

/**
* This sanitizer offers regex update of a specific JTokenPath.
*
* EG: "TableName" within a json response body having its value replaced by whatever substitution is offered.
* This simply means that if you are attempting to replace a specific key wholesale, this sanitizer will be simpler
* than configuring a BodyRegexSanitizer that has to match against the full "KeyName": "Value" that is part of the json structure.
*
* Further reading is available [here](https://www.newtonsoft.com/json/help/html/SelectToken.htm#SelectTokenJSONPath).
*
* If the body is NOT a JSON object, this sanitizer will NOT be applied.
*/
bodyKeySanitizers?: BodyKeySanitizer[];
/**
* TODO
Expand Down
Loading