-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Unified Recorder] Call
proxy-tool
through dev-tool (#18322)
* test-proxy starter code for starting * adding in the requirements * test-proxy starter code for starting * clean up * gets RootLocation * os.platform() === "win32" check * cleanup * testProxyUtils.ts * checkpoint * node side looks like it's working ✔️ * readme formatting * same console.log in win and lin * test:node-with-proxy * "sdk-type": "utility", * lock file * "sdk-type": "utility", * lock file * dev-tool test:browser * dotenv.config() call not needed since dev-tool does it by default * run subcommand * Partly switching to "fs-extra" * fsExtra -> fs * test:node-{js|ts}-input * default options * --single-run * remove dev-tool shortcut * runOnlyTestCommand * dedeuplicate with shouldRunProxyTool and runTestsWithProxyTool methods * minor changes * add console.logs * --mocha=\"--whatever\" and refactoring * simplify test scripts * more refactoring * unintended duplication * const sdkType = contents["sdk-type"]; * dead code * removing the if check * use an array instead * lock file * "sdk-type": "utility", * "sdk-type": "utility", * lock file * npm run integration-test:node * js -> ts * lock file * moving commands/run/testUtils.ts -> src/util/testUtils.ts * lock file * lock file * bug fix * PROXY_MANUAL_START * getTestMode * readme * lock file from main * lock file * dump logs * fix windows path * PROXY_MANUAL_START in karma.conf * duplication * clean karma conf * clean package.json * waits for the proxy tool - draft * wait-for-proxy-endpoint finish * Update sdk/test-utils/recorder-new/test/testProxyTests.spec.ts * beautify the tests * fix the test mode log * minor updates to tests * test-info * no need to pass test mode
- Loading branch information
1 parent
df9ad9d
commit 2de7b65
Showing
25 changed files
with
431 additions
and
68 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license | ||
|
||
import { subCommand, makeCommandInfo } from "../../framework/command"; | ||
|
||
export const commandInfo = makeCommandInfo("run", "run scripts such as test:node"); | ||
|
||
export default subCommand(commandInfo, { | ||
"test:node-ts-input": () => import("./testNodeTSInput"), | ||
"test:node-js-input": () => import("./testNodeJSInput"), | ||
"test:browser": () => import("./testBrowser") | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license | ||
|
||
import { leafCommand, makeCommandInfo } from "../../framework/command"; | ||
import { runTestsWithProxyTool } from "../../util/testUtils"; | ||
|
||
export const commandInfo = makeCommandInfo( | ||
"test:browser", | ||
"runs the browser tests using karma with the default and the provided options; starts the proxy-tool in record and playback modes", | ||
{ | ||
karma: { | ||
kind: "string", | ||
description: "Karma options (such as --single-run)", | ||
default: "--single-run" | ||
} | ||
} | ||
); | ||
|
||
export default leafCommand(commandInfo, async (options) => { | ||
return runTestsWithProxyTool({ | ||
command: `karma start ${options.karma}`, | ||
name: "browser-tests" | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license | ||
|
||
import { leafCommand, makeCommandInfo } from "../../framework/command"; | ||
import { runTestsWithProxyTool } from "../../util/testUtils"; | ||
|
||
export const commandInfo = makeCommandInfo( | ||
"test:node-js-input", | ||
"runs the node tests using mocha with the default and the provided options; starts the proxy-tool in record and playback modes", | ||
{ | ||
mocha: { | ||
kind: "string", | ||
description: | ||
"Mocha options along with the bundled test file(JS) with rollup as expected by mocha", | ||
default: '--timeout 5000000 "dist-esm/test/{,!(browser)/**/}/*.spec.js"' | ||
} | ||
} | ||
); | ||
|
||
export default leafCommand(commandInfo, async (options) => { | ||
return runTestsWithProxyTool({ | ||
command: `nyc mocha -r esm --require source-map-support/register --reporter ../../../common/tools/mocha-multi-reporter.js --full-trace ${options.mocha}`, | ||
name: "node-tests" | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license | ||
|
||
import { leafCommand, makeCommandInfo } from "../../framework/command"; | ||
import { runTestsWithProxyTool } from "../../util/testUtils"; | ||
|
||
export const commandInfo = makeCommandInfo( | ||
"test:node-ts-input", | ||
"runs the node tests using mocha with the default and the provided options; starts the proxy-tool in record and playback modes", | ||
{ | ||
mocha: { | ||
kind: "string", | ||
description: | ||
"Mocha options along with the test files(glob pattern) in TS as expected by mocha", | ||
default: '--timeout 1200000 --exclude "test/**/browser/*.spec.ts" "test/**/*.spec.ts"' | ||
} | ||
} | ||
); | ||
|
||
export default leafCommand(commandInfo, async (options) => { | ||
return runTestsWithProxyTool({ | ||
command: `mocha -r esm -r ts-node/register --reporter ../../../common/tools/mocha-multi-reporter.js --full-trace ${options.mocha}`, | ||
name: "node-tests" | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license | ||
|
||
import { subCommand, makeCommandInfo } from "../../framework/command"; | ||
|
||
export const commandInfo = makeCommandInfo( | ||
"test-proxy", | ||
"runs the proxy-tool with the `docker run ...` command" | ||
); | ||
|
||
export default subCommand(commandInfo, { | ||
start: () => import("./start"), | ||
"wait-for-proxy-endpoint": () => import("./waitForProxyEndpoint") | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { leafCommand, makeCommandInfo } from "../../framework/command"; | ||
import { config } from "dotenv"; | ||
import { startProxyTool } from "../../util/testProxyUtils"; | ||
config(); | ||
|
||
export const commandInfo = makeCommandInfo( | ||
"test-proxy", | ||
"runs the proxy-tool with the `docker run ...` command", | ||
{} | ||
); | ||
|
||
export default leafCommand(commandInfo, async (_options) => { | ||
await startProxyTool(); | ||
return true; | ||
}); |
19 changes: 19 additions & 0 deletions
19
common/tools/dev-tool/src/commands/test-proxy/waitForProxyEndpoint.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { leafCommand, makeCommandInfo } from "../../framework/command"; | ||
import { config } from "dotenv"; | ||
import { isProxyToolActive } from "../../util/testProxyUtils"; | ||
import { checkWithTimeout } from "../../util/checkWithTimeout"; | ||
config(); | ||
|
||
export const commandInfo = makeCommandInfo( | ||
"test-proxy", | ||
"waits for the proxy tool to be active or fails in 2 minutes", | ||
{} | ||
); | ||
|
||
export default leafCommand(commandInfo, async (_options) => { | ||
const result = await checkWithTimeout(isProxyToolActive, 1000, 120000); | ||
return result; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license | ||
|
||
import { createPrinter } from "./printer"; | ||
const log = createPrinter("check-with-timeout"); | ||
|
||
/** | ||
* - Maximum wait duration for the expected event to happen = `10000 ms`(default value is 10 seconds)(= maxWaitTimeInMilliseconds) | ||
* - Keep checking whether the predicate is true after every `1000 ms`(default value is 1 second) (= delayBetweenRetriesInMilliseconds) | ||
*/ | ||
export async function checkWithTimeout( | ||
predicate: () => boolean | Promise<boolean>, | ||
delayBetweenRetriesInMilliseconds: number = 1000, | ||
maxWaitTimeInMilliseconds: number = 10000 | ||
): Promise<boolean> { | ||
const maxTime = Date.now() + maxWaitTimeInMilliseconds; | ||
while (Date.now() < maxTime) { | ||
if (await predicate()) { | ||
log.info(`checkWithTimeout condition returned true`); | ||
return true; | ||
} | ||
await delay(delayBetweenRetriesInMilliseconds); | ||
} | ||
return false; | ||
} | ||
|
||
async function delay(timeInMs: number) { | ||
return new Promise((resolve) => { | ||
log.info(`waiting for ${timeInMs}ms`); | ||
setTimeout(resolve, timeInMs); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { spawn } from "child_process"; | ||
import path from "path"; | ||
import { IncomingMessage, request, RequestOptions } from "http"; | ||
import fs from "fs-extra"; | ||
import { createPrinter } from "./printer"; | ||
|
||
const log = createPrinter("test-proxy"); | ||
export async function startProxyTool() { | ||
log.info( | ||
`Attempting to start test proxy at http://localhost:5000 & https://localhost:5001.\n` | ||
); | ||
|
||
const subprocess = spawn(await getDockerRunCommand(), [], { | ||
shell: true | ||
}); | ||
|
||
const outFileName = "test-proxy-output.log"; | ||
const out = fs.createWriteStream(`./${outFileName}`, { flags: 'a' }); | ||
subprocess.stdout.pipe(out); | ||
subprocess.stderr.pipe(out); | ||
|
||
log.info(`Check the output file "${outFileName}" for test-proxy logs.`); | ||
} | ||
|
||
async function getRootLocation(start?: string): Promise<string> { | ||
start ??= process.cwd(); | ||
if (await fs.pathExists(path.join(start, "rush.json"))) { | ||
return start; | ||
} else { | ||
const nextPath = path.resolve(start, ".."); | ||
if (nextPath === start) { | ||
throw new Error("Reached filesystem root, but no rush.json was found."); | ||
} else { | ||
return getRootLocation(nextPath); | ||
} | ||
} | ||
} | ||
|
||
async function getDockerRunCommand() { | ||
const repoRoot = await getRootLocation(); // /workspaces/azure-sdk-for-js/ | ||
const testProxyRecordingsLocation = "/etc/testproxy"; | ||
const allowLocalhostAccess = "--add-host host.docker.internal:host-gateway"; | ||
const imageToLoad = `azsdkengsys.azurecr.io/engsys/testproxy-lin:${await getImageTag()}`; | ||
return `docker run -v ${repoRoot}:${testProxyRecordingsLocation} -p 5001:5001 -p 5000:5000 ${allowLocalhostAccess} ${imageToLoad}`; | ||
} | ||
|
||
export async function isProxyToolActive() { | ||
try { | ||
await makeRequest("http://localhost:5000/info/available", {}); | ||
log.info(`Proxy tool seems to be active at http://localhost:5000\n`); | ||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
async function makeRequest(uri: string, requestOptions: RequestOptions): Promise<IncomingMessage> { | ||
return new Promise<IncomingMessage>((resolve, reject) => { | ||
const req = request(uri, requestOptions, resolve); | ||
req.once("error", reject); | ||
req.end(); | ||
}); | ||
} | ||
|
||
async function getImageTag() { | ||
// Grab the tag from the `/eng/common/testproxy/docker-start-proxy.ps1` file [..is used to run the proxy-tool in the CI] | ||
// | ||
// $SELECTED_IMAGE_TAG = "1147815"; | ||
// (Bot regularly updates the tag in the file above.) | ||
try { | ||
const contentInPWSHScript = await fs.readFile( | ||
`${path.join(await getRootLocation(), "eng/common/testproxy/docker-start-proxy.ps1")}`, | ||
"utf-8" | ||
); | ||
const tag = contentInPWSHScript.match(/\$SELECTED_IMAGE_TAG \= \"(.*)\"/)![1]; | ||
log.info(`Image tag obtained from the powershell script => ${tag}\n`); | ||
return tag; | ||
} catch (_) { | ||
log.warn( | ||
`Unable to get the image tag from the powershell script, trying "latest" tag instead\n` | ||
); | ||
return "latest"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { isProxyToolActive } from "./testProxyUtils"; | ||
import concurrently from "concurrently"; | ||
import { createPrinter } from "./printer"; | ||
|
||
const log = createPrinter("preparing-proxy-tool"); | ||
|
||
async function shouldRunProxyTool(): Promise<boolean> { | ||
const mode = process.env.TEST_MODE; | ||
createPrinter("test-info").info(`===TEST_MODE="${mode}"===`); | ||
if (mode === "live") { | ||
return false; // No need to start the proxy tool in the live mode | ||
} else { | ||
const isActive = await isProxyToolActive(); | ||
if (isActive) { | ||
// No need to run a new one if it is already active | ||
// Especially, CI uses this path | ||
log.info( | ||
`Proxy tool seems to be active, not attempting to start the test proxy at http://localhost:5000 & https://localhost:5001.\n` | ||
); | ||
} | ||
return !isActive; | ||
} | ||
} | ||
|
||
export async function runTestsWithProxyTool(testCommandObj: concurrently.CommandObj) { | ||
if ( | ||
await shouldRunProxyTool() // Boolean to figure out if we need to run just the mocha command or the test-proxy too | ||
) { | ||
const testProxyCMD = "dev-tool test-proxy start"; | ||
const waitForProxyEndpointCMD = "dev-tool test-proxy wait-for-proxy-endpoint"; | ||
await concurrently( | ||
[ | ||
{ command: testProxyCMD }, | ||
{ | ||
command: `${waitForProxyEndpointCMD} && ${testCommandObj.command}`, // Waits for the proxy endpoint to be active and then starts running the tests | ||
name: testCommandObj.name | ||
} | ||
], | ||
{ | ||
killOthers: ["failure", "success"], | ||
successCondition: "first" | ||
} | ||
); | ||
} else { | ||
await concurrently([testCommandObj]); | ||
} | ||
return true; | ||
} |
Oops, something went wrong.