From 8a25edcdc3ca07e6e45f12236f486f9bd7dcd57e Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 8 Jun 2024 19:53:53 +0530 Subject: [PATCH 01/47] feat: added basic functionality for env var replacement in values of existing variables --- src/variableParser.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index 69f88f9..ce83450 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -38,17 +38,33 @@ export function getEnvironments(bundleContent: string | undefined, varFileConten return [...uniqueNames]; } +function replaceEnvironmentVariables(vars: Variables): Variables { + const PREFIX = "$env."; + + const getVal = (val: any): any => { + if (typeof val !== "string" || !val.startsWith(PREFIX)) return val; + + const envVarName = val.slice(PREFIX.length); + return envVarName in process.env ? process.env[envVarName] : val; + }; + + const replacedVars: Variables = {}; + for (const key in vars) replacedVars[key] = getVal(vars[key]); +} + export function loadVariables( envName: string | undefined, bundleContent: string | undefined, - varFileContents: string[], + varFileContents: string[] ): Variables { if (!envName) return {}; const allBundleVariables = getBundleVariables(bundleContent); - const bundleVars = allBundleVariables.hasOwnProperty(envName) ? allBundleVariables[envName] : {}; + const bundleVars: Variables = allBundleVariables.hasOwnProperty(envName) + ? allBundleVariables[envName] + : {}; - let envVars = {}; + let envVars: Variables = {}; varFileContents.forEach((fileContents) => { const parsedData = YAML.parse(fileContents); if (parsedData && isDict(parsedData[envName])) { @@ -56,5 +72,7 @@ export function loadVariables( } }); - return Object.assign({}, envVars, bundleVars); + const basicVars = Object.assign({}, envVars, bundleVars); + const envReplaced = replaceEnvironmentVariables(basicVars); + return envReplaced; } From 64a99bbb7c5e80e4e83d28a391f93e168ff2eefc Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 8 Jun 2024 19:54:28 +0530 Subject: [PATCH 02/47] refactor: prettier --- src/variableParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index ce83450..c10857f 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -55,7 +55,7 @@ function replaceEnvironmentVariables(vars: Variables): Variables { export function loadVariables( envName: string | undefined, bundleContent: string | undefined, - varFileContents: string[] + varFileContents: string[], ): Variables { if (!envName) return {}; From 5fbe9aad28ca43d1d272cee1eec009493fb87288 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 8 Jun 2024 19:55:58 +0530 Subject: [PATCH 03/47] bug: returning replaced vars in helper --- src/variableParser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index c10857f..e10cfa3 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -50,12 +50,14 @@ function replaceEnvironmentVariables(vars: Variables): Variables { const replacedVars: Variables = {}; for (const key in vars) replacedVars[key] = getVal(vars[key]); + + return replacedVars; } export function loadVariables( envName: string | undefined, bundleContent: string | undefined, - varFileContents: string[], + varFileContents: string[] ): Variables { if (!envName) return {}; From a641c3a52e05a23f353db47aa0aeb373b291291a Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 8 Jun 2024 19:58:09 +0530 Subject: [PATCH 04/47] refactor: prettier --- src/variableParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index e10cfa3..1743d5f 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -57,7 +57,7 @@ function replaceEnvironmentVariables(vars: Variables): Variables { export function loadVariables( envName: string | undefined, bundleContent: string | undefined, - varFileContents: string[] + varFileContents: string[], ): Variables { if (!envName) return {}; From 98ae00d3247f6a6c3f38e83ff909709d8b1650ba Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 8 Jun 2024 21:40:38 +0530 Subject: [PATCH 05/47] refactor: simplified bundle variables code --- src/variableParser.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index 1743d5f..e113aed 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -62,16 +62,12 @@ export function loadVariables( if (!envName) return {}; const allBundleVariables = getBundleVariables(bundleContent); - const bundleVars: Variables = allBundleVariables.hasOwnProperty(envName) - ? allBundleVariables[envName] - : {}; + const bundleVars: Variables = allBundleVariables[envName] ?? {}; - let envVars: Variables = {}; + const envVars: Variables = {}; varFileContents.forEach((fileContents) => { const parsedData = YAML.parse(fileContents); - if (parsedData && isDict(parsedData[envName])) { - Object.assign(envVars, parsedData[envName]); - } + if (parsedData && isDict(parsedData[envName])) Object.assign(envVars, parsedData[envName]); }); const basicVars = Object.assign({}, envVars, bundleVars); From c5416550b508e7437cfab389d37ba6bc50dcac9c Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Mon, 10 Jun 2024 12:17:02 +0530 Subject: [PATCH 06/47] feat: accounting for undefined variables in environment variable replacement --- src/variableParser.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index e113aed..1163849 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -38,28 +38,34 @@ export function getEnvironments(bundleContent: string | undefined, varFileConten return [...uniqueNames]; } -function replaceEnvironmentVariables(vars: Variables): Variables { +function replaceEnvironmentVariables(vars: Variables): { + replacedVars: Variables; + undefinedVars: string[]; +} { const PREFIX = "$env."; + const undefinedVars: string[] = []; const getVal = (val: any): any => { if (typeof val !== "string" || !val.startsWith(PREFIX)) return val; const envVarName = val.slice(PREFIX.length); - return envVarName in process.env ? process.env[envVarName] : val; + if (envVarName in process.env) return process.env[envVarName]; + + undefinedVars.push(envVarName); }; const replacedVars: Variables = {}; for (const key in vars) replacedVars[key] = getVal(vars[key]); - return replacedVars; + return { replacedVars, undefinedVars }; } export function loadVariables( envName: string | undefined, bundleContent: string | undefined, varFileContents: string[], -): Variables { - if (!envName) return {}; +): { vars: Variables; undefinedVars: string[] } { + if (!envName) return { vars: {}, undefinedVars: [] }; const allBundleVariables = getBundleVariables(bundleContent); const bundleVars: Variables = allBundleVariables[envName] ?? {}; @@ -71,6 +77,7 @@ export function loadVariables( }); const basicVars = Object.assign({}, envVars, bundleVars); - const envReplaced = replaceEnvironmentVariables(basicVars); - return envReplaced; + const { replacedVars: vars, undefinedVars } = replaceEnvironmentVariables(basicVars); + + return { vars, undefinedVars }; } From 28b91db73999a2544153f740c5cf0e62412c2327 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Mon, 10 Jun 2024 12:19:06 +0530 Subject: [PATCH 07/47] bug: returning original string in case of undefined variable --- src/variableParser.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index 1163849..f78da04 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -52,6 +52,7 @@ function replaceEnvironmentVariables(vars: Variables): { if (envVarName in process.env) return process.env[envVarName]; undefinedVars.push(envVarName); + return val; }; const replacedVars: Variables = {}; @@ -63,7 +64,7 @@ function replaceEnvironmentVariables(vars: Variables): { export function loadVariables( envName: string | undefined, bundleContent: string | undefined, - varFileContents: string[], + varFileContents: string[] ): { vars: Variables; undefinedVars: string[] } { if (!envName) return { vars: {}, undefinedVars: [] }; From 5431fa9b88da21775aac2b172f9127e53e37c517 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Mon, 10 Jun 2024 12:19:28 +0530 Subject: [PATCH 08/47] refactor: prettier --- src/variableParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index f78da04..786dab9 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -64,7 +64,7 @@ function replaceEnvironmentVariables(vars: Variables): { export function loadVariables( envName: string | undefined, bundleContent: string | undefined, - varFileContents: string[] + varFileContents: string[], ): { vars: Variables; undefinedVars: string[] } { if (!envName) return { vars: {}, undefinedVars: [] }; From 2ecc883566332685add06fe55fb39e087b7b8ad7 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Mon, 10 Jun 2024 12:42:13 +0530 Subject: [PATCH 09/47] refactor: reducing repetition in httpRequest's key --- src/replaceVars.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/replaceVars.ts b/src/replaceVars.ts index 3e78449..1accb4a 100644 --- a/src/replaceVars.ts +++ b/src/replaceVars.ts @@ -137,9 +137,10 @@ export function replaceVariablesInRequest(request: RequestSpec, variables: Varia type keyOfHttp = Exclude; const httpPropertiesToReplace: string[] = ["baseUrl", "url", "params", "headers", "body"]; - httpPropertiesToReplace.forEach((key) => { - const replacedData = replaceVariables(request.httpRequest[key as keyOfHttp], variables); - request.httpRequest[key as keyOfHttp] = replacedData.data; + httpPropertiesToReplace.forEach((prop) => { + const httpKey = prop as keyOfHttp; + const replacedData = replaceVariables(request.httpRequest[httpKey], variables); + request.httpRequest[httpKey] = replacedData.data; undefs.push(...replacedData.undefinedVars); }); From ae7918f96b9099ac6d1f9d5d5a36e77fd2dcea47 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Mon, 10 Jun 2024 23:08:35 +0530 Subject: [PATCH 10/47] feat: showing entire val in undefined instead of just $env. for clarity --- src/variableParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index 786dab9..035c2f8 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -51,7 +51,7 @@ function replaceEnvironmentVariables(vars: Variables): { const envVarName = val.slice(PREFIX.length); if (envVarName in process.env) return process.env[envVarName]; - undefinedVars.push(envVarName); + undefinedVars.push(val); return val; }; From 2963ffe9056f5f08c0df10338f80c238008366ff Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Fri, 14 Jun 2024 18:03:50 +0530 Subject: [PATCH 11/47] feat: removing user-agent header from constructed curl --- src/constructCurl.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/constructCurl.ts b/src/constructCurl.ts index 594b4a5..cb64c38 100644 --- a/src/constructCurl.ts +++ b/src/constructCurl.ts @@ -11,8 +11,10 @@ export function getCurlRequest(request: RequestSpec): string { // headers if (request.httpRequest.headers !== undefined) { - for (const header in request.httpRequest.headers) - curl += ` -H '${header}: ${request.httpRequest.headers[header]}'`; + for (const header in request.httpRequest.headers) { + if (header === "user-agent") continue; + curl += ` -H \"${header}: ${request.httpRequest.headers[header]}\"`; + } } // body From cf753fe611efe49807b290fb8556b49b11f90382 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Fri, 14 Jun 2024 18:04:34 +0530 Subject: [PATCH 12/47] misc: reverting to old single quotes for now --- src/constructCurl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constructCurl.ts b/src/constructCurl.ts index cb64c38..f27691a 100644 --- a/src/constructCurl.ts +++ b/src/constructCurl.ts @@ -13,7 +13,7 @@ export function getCurlRequest(request: RequestSpec): string { if (request.httpRequest.headers !== undefined) { for (const header in request.httpRequest.headers) { if (header === "user-agent") continue; - curl += ` -H \"${header}: ${request.httpRequest.headers[header]}\"`; + curl += ` -H '${header}: ${request.httpRequest.headers[header]}'`; } } From f199be4dfc6e986516f22ebd375fc5aece9f69e7 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sun, 16 Jun 2024 21:01:22 +0530 Subject: [PATCH 13/47] feat: replacing single quotes with '%27' --- src/constructCurl.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/constructCurl.ts b/src/constructCurl.ts index f27691a..ff4798b 100644 --- a/src/constructCurl.ts +++ b/src/constructCurl.ts @@ -3,6 +3,11 @@ import { getStringValueIfDefined } from "./utils/typeUtils"; import { getParamsForUrl, getURL } from "./executeRequest"; import { RequestSpec } from "./models"; +function replaceSingleQuotes(value: T): T { + if (typeof value !== "string") return value; + return value.replace(/'/g, "%27") as T & string; +} + export function getCurlRequest(request: RequestSpec): string { let curl: string = "curl"; @@ -13,13 +18,13 @@ export function getCurlRequest(request: RequestSpec): string { if (request.httpRequest.headers !== undefined) { for (const header in request.httpRequest.headers) { if (header === "user-agent") continue; - curl += ` -H '${header}: ${request.httpRequest.headers[header]}'`; + curl += ` -H '${replaceSingleQuotes(`${header}: ${request.httpRequest.headers[header]}`)}'`; } } // body if (request.httpRequest.body !== undefined) - curl += ` -d '${getStringValueIfDefined(request.httpRequest.body)}'`; + curl += ` -d '${replaceSingleQuotes(getStringValueIfDefined(request.httpRequest.body))}'`; // options.follow if (request.options.follow) curl += " -L"; @@ -31,10 +36,12 @@ export function getCurlRequest(request: RequestSpec): string { if (request.options.showHeaders) curl += " -i"; // url w/ params - curl += ` '${getURL( - request.httpRequest.baseUrl, - request.httpRequest.url, - getParamsForUrl(request.httpRequest.params, request.options.rawParams), + curl += ` '${replaceSingleQuotes( + getURL( + request.httpRequest.baseUrl, + request.httpRequest.url, + getParamsForUrl(request.httpRequest.params, request.options.rawParams), + ), )}'`; return curl; From bfd4b6ba04476adfe43cc50109ce7ac5dd9c344b Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sun, 16 Jun 2024 21:18:43 +0530 Subject: [PATCH 14/47] feat: allowing user-agent header from node-module, removing from caller instead --- src/constructCurl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/constructCurl.ts b/src/constructCurl.ts index ff4798b..a35d381 100644 --- a/src/constructCurl.ts +++ b/src/constructCurl.ts @@ -17,7 +17,6 @@ export function getCurlRequest(request: RequestSpec): string { // headers if (request.httpRequest.headers !== undefined) { for (const header in request.httpRequest.headers) { - if (header === "user-agent") continue; curl += ` -H '${replaceSingleQuotes(`${header}: ${request.httpRequest.headers[header]}`)}'`; } } From ebdffad85a54a5981797cb227d93add3d2ed69c1 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Mon, 17 Jun 2024 01:27:27 +0530 Subject: [PATCH 15/47] feat: pre-processing of test requests --- tests/callRequests.ts | 58 +++++++++++++++++++++++++++++++++++++ tests/getResponses.ts | 9 ++++++ tests/utils/bundleUtils.ts | 26 +++++++++++++++++ tests/utils/requestUtils.ts | 21 ++++++++++++++ tests/utils/varUtils.ts | 25 ++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 tests/callRequests.ts create mode 100644 tests/getResponses.ts create mode 100644 tests/utils/bundleUtils.ts create mode 100644 tests/utils/requestUtils.ts create mode 100644 tests/utils/varUtils.ts diff --git a/tests/callRequests.ts b/tests/callRequests.ts new file mode 100644 index 0000000..f53d5d4 --- /dev/null +++ b/tests/callRequests.ts @@ -0,0 +1,58 @@ +import * as path from "path"; + +import { RequestSpec, Variables } from "../src/index"; +import { getAllRequestSpecs, getRequestSpec } from "../src/index"; +import { loadVariables } from "../src/index"; + +import { RawRequest } from "./utils/requestUtils"; +import { getVarFileContents } from "./utils/varUtils"; + +import { runRequestTests } from "./getResponses"; + +async function runRequestSpecs( + requests: { [name: string]: RequestSpec }, + rawRequest: RawRequest, +): Promise { + for (const name in requests) { + const request = requests[name]; + + const autoHeaders: { [key: string]: string } = { "user-agent": "zzAPI-cli/" + CLI_VERSION }; + if (request.httpRequest.body && typeof request.httpRequest.body == "object") + autoHeaders["content-type"] = "application/json"; + + request.httpRequest.headers = Object.assign(autoHeaders, request.httpRequest.headers); + } + + await runRequestTests(requests, rawRequest); +} + +export async function callRequests(request: RawRequest): Promise { + try { + // load the variables + const env = request.envName; + const loadedVariables: Variables = loadVariables( + env, + request.bundle.bundleContents, + getVarFileContents(path.dirname(request.bundle.bundlePath)), + ); + if (env && Object.keys(loadedVariables).length < 1) + console.log(`warning: no variables added from env: ${env}`); + request.variables.setLoadedVariables(loadedVariables); + } catch (err: any) { + throw err; + } + + // create the request specs + const name = request.requestName, + content = request.bundle.bundleContents; + + let allRequests: { [name: string]: RequestSpec }; + try { + allRequests = name ? { [name]: getRequestSpec(content, name) } : getAllRequestSpecs(content); + } catch (err: any) { + throw err; + } + + // finally, run the request specs + await runRequestSpecs(allRequests, request); +} diff --git a/tests/getResponses.ts b/tests/getResponses.ts new file mode 100644 index 0000000..224368f --- /dev/null +++ b/tests/getResponses.ts @@ -0,0 +1,9 @@ +import { RequestSpec, ResponseData } from "../src"; +import { RawRequest } from "./utils/requestUtils"; + +export async function runRequestTests( + requests: { [name: string]: RequestSpec }, + rawReq: RawRequest, +): Promise { + +} diff --git a/tests/utils/bundleUtils.ts b/tests/utils/bundleUtils.ts new file mode 100644 index 0000000..282e114 --- /dev/null +++ b/tests/utils/bundleUtils.ts @@ -0,0 +1,26 @@ +import path from "path"; +import * as fs from "fs"; + +export class Bundle { + public bundlePath: string = __dirname; + public bundleContents: string = ""; + + constructor(relPath: string) { + try { + this.setBundlePath(relPath); + this.readContents(); + } catch (e) { + throw e; + } + } + + setBundlePath(relPath: string) { + this.bundlePath = path.resolve(relPath); + if (!fs.existsSync(this.bundlePath)) throw `error: ${this.bundlePath} does not exist`; + if (!fs.lstatSync(this.bundlePath).isFile()) throw `error: ${this.bundlePath} is not a file`; + } + + readContents() { + this.bundleContents = fs.readFileSync(this.bundlePath, "utf-8"); + } +} diff --git a/tests/utils/requestUtils.ts b/tests/utils/requestUtils.ts new file mode 100644 index 0000000..9f52ac2 --- /dev/null +++ b/tests/utils/requestUtils.ts @@ -0,0 +1,21 @@ +import { VarStore } from "../../src/index"; + +import { Bundle } from "./bundleUtils"; + +export class RawRequest { + public requestName: string | undefined = undefined; + public envName: string | undefined = undefined; + public bundle: Bundle; + public variables: VarStore; + + constructor(relPath: string, envName: string | undefined, reqName?: string) { + try { + this.bundle = new Bundle(relPath); + this.requestName = reqName; + this.envName = envName; + this.variables = new VarStore(); + } catch (e) { + throw e; + } + } +} diff --git a/tests/utils/varUtils.ts b/tests/utils/varUtils.ts new file mode 100644 index 0000000..c81dab0 --- /dev/null +++ b/tests/utils/varUtils.ts @@ -0,0 +1,25 @@ +import * as fs from "fs"; +import path from "path"; + +const VARFILE_EXTENSION = ".zzv"; + +function getVarFilePaths(dirPath: string): string[] { + const dirContents = fs.readdirSync(dirPath, { recursive: false, encoding: "utf-8" }); + const varFiles = dirContents.filter((file) => path.extname(file) == VARFILE_EXTENSION); + const varFilePaths = varFiles.map((file) => path.join(dirPath, file)); + + return varFilePaths; +} + +export function getVarFileContents(dirPath: string): string[] { + if (!dirPath) return []; + + const varFilePaths = getVarFilePaths(dirPath); + const fileContents: string[] = []; + varFilePaths.forEach((varFilePath) => { + const fileData = fs.readFileSync(varFilePath, "utf-8"); + fileContents.push(fileData); + }); + + return fileContents; +} From 62012ad0a5c6f8947e916df86f168b3ec9741640 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 21:48:26 +0530 Subject: [PATCH 16/47] feat: wrote basic skeleton of running requests in testing workflow --- tests/execute_got_request.test.ts | 8 +-- tests/getResponses.ts | 107 +++++++++++++++++++++++++++++- tests/utils/fileContents.ts | 18 +++++ 3 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 tests/utils/fileContents.ts diff --git a/tests/execute_got_request.test.ts b/tests/execute_got_request.test.ts index 8eb40bb..e618a64 100644 --- a/tests/execute_got_request.test.ts +++ b/tests/execute_got_request.test.ts @@ -3,8 +3,8 @@ import got from "got"; import { executeGotRequest } from "../src"; test("execute simple-get GOT request", async () => { - const response = await executeGotRequest(got("https://postman-echo.com/get", {method: "GET"})); - expect(response.byteLength).toBeGreaterThan(0); - expect(response.executionTime).toBeGreaterThan(0); - expect(response.error.length).toBeLessThan(1); + const response = await executeGotRequest(got("https://postman-echo.com/get", { method: "GET" })); + expect(response.byteLength).toBeGreaterThan(0); + expect(response.executionTime).toBeGreaterThan(0); + expect(response.error.length).toBeLessThan(1); }); diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 224368f..223d6cc 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -1,9 +1,112 @@ -import { RequestSpec, ResponseData } from "../src"; +import path from "path"; + +import { + RequestSpec, + ResponseData, + captureVariables, + constructGotRequest, + executeGotRequest, + replaceVariablesInRequest, + runAllTests, +} from "../src/index"; + import { RawRequest } from "./utils/requestUtils"; +import { replaceFileContents } from "./utils/fileContents"; + +function getStatusCode(): number { + return process.exitCode ?? 0; +} + +function attemptParse(response: ResponseData, expectJson?: boolean): string | undefined { + if (!expectJson || !response.status) return undefined; + if (!response.body) return "No response body"; + + try { + response.json = JSON.parse(response.body); + } catch (err) { + if (err instanceof Error && err.message) { + return err.message; + } else { + return `Error parsing the response body: ${err}`; + } + } + + return undefined; +} + +function getStrictStringValue(value: any): string { + if (value === undefined) return "undefined"; + if (typeof value === "object") return JSON.stringify(value); + return value.toString(); +} + +function getHeadersAsString(rawHeaders: string[] | undefined): string { + let formattedString = "\n"; + if (rawHeaders === undefined) return formattedString; + + const maxPairInd = rawHeaders.length - 1; + for (let i = 0; i < maxPairInd; i += 2) + formattedString += ` ${rawHeaders[i]} : ${getStrictStringValue(rawHeaders[i + 1])}\n`; + + return `\n ${formattedString.trim()}`; +} export async function runRequestTests( requests: { [name: string]: RequestSpec }, rawReq: RawRequest, ): Promise { - + const bundlePath = rawReq.bundle.bundlePath; + const bundleName = bundlePath.substring(bundlePath.lastIndexOf(path.sep) + 1); + + console.log(`running ${bundleName}`); + + for (const name in requests) { + const req: RequestSpec = requests[name]; + req.httpRequest.body = replaceFileContents(req.httpRequest.body, bundlePath); + + const undefs = replaceVariablesInRequest(req, rawReq.variables.getAllVariables()); + + const httpRequest = constructGotRequest(req); + const { + response: httpResponse, + executionTime, + byteLength: size, + error, + } = await executeGotRequest(httpRequest); + + if (error) { + // fail + process.exitCode = getStatusCode() + 1; + continue; + } + + const response: ResponseData = { + executionTime: `${executionTime} ms`, + status: httpResponse.statusCode, + body: httpResponse.body, + rawHeaders: getHeadersAsString(httpResponse.rawHeaders), + headers: httpResponse.headers, + json: null, + }; + + const parseError = attemptParse(response, req.expectJson); + if (parseError) { + // fail + process.exitCode = getStatusCode() + 1; + continue; + } + + const results = runAllTests(req.tests, response, req.options.stopOnFailure); + // compare the given tests with the results + + const { capturedVars, captureErrors } = captureVariables(req, response); + if (captureErrors) { + // warn + } + if (undefs.length > 0) { + // warn + } + + // write final message + } } diff --git a/tests/utils/fileContents.ts b/tests/utils/fileContents.ts new file mode 100644 index 0000000..c9360be --- /dev/null +++ b/tests/utils/fileContents.ts @@ -0,0 +1,18 @@ +import * as fs from "fs"; +import * as path from "path"; + +export function replaceFileContents(body: T, bundlePath: string): T { + if (typeof body !== "string") return body; + + /* + finds all file:// instances with atleast 1 succeeding word character + matches the file-name referred to by this instance + */ + const fileRegex = /file:\/\/([^\s]+)/g; + return body.replace(fileRegex, (match, givenFilePath) => { + if (match !== body) return match; // we only perform a replacement if file:// is the ENTIRE body + + const filePath = path.resolve(path.dirname(bundlePath), givenFilePath); + return fs.readFileSync(filePath, "utf-8"); + }) as T & string; +} From 4390602d691bb1f27ae89df0ab94243357f086e4 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 22:35:14 +0530 Subject: [PATCH 17/47] feat: created functionality to compare requests to obtained test results --- src/index.ts | 1 + tests/getResponses.ts | 9 ++-- tests/runTests.ts | 105 ++++++++++++++++++++++++++++++++++++++++++ tests/utils/errors.ts | 3 ++ 4 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 tests/runTests.ts create mode 100644 tests/utils/errors.ts diff --git a/src/index.ts b/src/index.ts index 422471a..c8a8416 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export { GotRequest, TestResult, SpecResult, + Tests, } from "./models"; export { getRequestPositions, getAllRequestSpecs, getRequestSpec } from "./parseBundle"; diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 223d6cc..df1c44e 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -12,12 +12,9 @@ import { import { RawRequest } from "./utils/requestUtils"; import { replaceFileContents } from "./utils/fileContents"; +import { getStatusCode } from "./utils/errors"; -function getStatusCode(): number { - return process.exitCode ?? 0; -} - -function attemptParse(response: ResponseData, expectJson?: boolean): string | undefined { +function parseBody(response: ResponseData, expectJson?: boolean): string | undefined { if (!expectJson || !response.status) return undefined; if (!response.body) return "No response body"; @@ -89,7 +86,7 @@ export async function runRequestTests( json: null, }; - const parseError = attemptParse(response, req.expectJson); + const parseError = parseBody(response, req.expectJson); if (parseError) { // fail process.exitCode = getStatusCode() + 1; diff --git a/tests/runTests.ts b/tests/runTests.ts new file mode 100644 index 0000000..1966dc2 --- /dev/null +++ b/tests/runTests.ts @@ -0,0 +1,105 @@ +import { RequestSpec, SpecResult, TestResult } from "../src/index"; +import { Tests } from "../src/index"; + +function formatTestResult(res: TestResult, spec: string, skip?: boolean): string { + const status = + (skip || res.pass ? " ✓ " : " ✗ ") + + ("test " + spec + " ") + + (res.op === ":" ? "$eq" : res.op) + + " " + + res.expected; + if (res.pass) return status; + if (skip) return status + " (skipped)"; + + return status + " | actual " + res.received; +} + +function getResultData(res: SpecResult): [number, number] { + if (res.skipped) return [0, 0]; + + const rootResults = res.results; + let passed = rootResults.filter((r) => r.pass).length; + let all = rootResults.length; + + for (const s of res.subResults) { + const [subPassed, subAll] = getResultData(s); + passed += subPassed; + all += subAll; + } + + return [passed, all]; +} + +function allNegative(res: SpecResult, numTests: number): string[] { + const errors: string[] = []; + + const [passed, all] = getResultData(res); + if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); + + if (passed === 0) return errors; + + function getPassingTests(res: SpecResult, spec: string): string[] { + const passingTests: string[] = []; + if (res.skipped) return passingTests; + + const rootPassing = res.results.filter((r) => r.pass); + passingTests.push(...rootPassing.map((r) => formatTestResult(r, spec, false))); + + spec += " > " + res.spec ?? ""; + for (const s of res.subResults) + passingTests.push(...getPassingTests(s, spec)); + + return passingTests; + } + + errors.push(...getPassingTests(res, res.spec ?? "")); + return errors; +} + +function allPositive(res: SpecResult, numTests: number): string[] { + const errors: string[] = []; + + const [passed, all] = getResultData(res); + if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); + + if (passed === all) return errors; + + function getFailingTests(res: SpecResult, spec: string): string[] { + const failingTests: string[] = []; + if (res.skipped) return failingTests; + + const rootFailing = res.results.filter((r) => !r.pass); + failingTests.push(...rootFailing.map((r) => formatTestResult(r, spec, false))); + + spec += " > " + res.spec ?? ""; + for (const s of res.subResults) + failingTests.push(...getFailingTests(s, spec)); + + return failingTests; + } + + errors.push(...getFailingTests(res, res.spec ?? "")); + return errors; +} + +function getNumTests(tests: Tests) { + let numTests = 0; + if (tests.body) numTests += 1; + if(tests.status) numTests += 1; + if (tests.headers) + numTests += Object.keys(tests.headers).length; + if (tests.json) numTests += Object.keys(tests.json).length; + + return numTests; + +} + +export function compareReqAndResp(req: RequestSpec, res: SpecResult) { + const numTests = getNumTests(req.tests); + if (req.name.includes("positive")) + return allPositive(res, numTests); + else if (req.name.includes("negative")) + return allNegative(res, numTests); + + return ["not a valid test type for automated tests"]; +} diff --git a/tests/utils/errors.ts b/tests/utils/errors.ts new file mode 100644 index 0000000..9d5c969 --- /dev/null +++ b/tests/utils/errors.ts @@ -0,0 +1,3 @@ +export function getStatusCode(): number { + return process.exitCode ?? 0; +} From ddf189d5ea811b164a110e855ded307b29ebab49 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 22:45:55 +0530 Subject: [PATCH 18/47] feat: added captured variables, added error messages --- tests/getResponses.ts | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/getResponses.ts b/tests/getResponses.ts index df1c44e..831c003 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -13,6 +13,7 @@ import { import { RawRequest } from "./utils/requestUtils"; import { replaceFileContents } from "./utils/fileContents"; import { getStatusCode } from "./utils/errors"; +import { compareReqAndResp } from "./runTests"; function parseBody(response: ResponseData, expectJson?: boolean): string | undefined { if (!expectJson || !response.status) return undefined; @@ -48,6 +49,8 @@ function getHeadersAsString(rawHeaders: string[] | undefined): string { return `\n ${formattedString.trim()}`; } +const BULLET = "-->"; + export async function runRequestTests( requests: { [name: string]: RequestSpec }, rawReq: RawRequest, @@ -58,6 +61,8 @@ export async function runRequestTests( console.log(`running ${bundleName}`); for (const name in requests) { + let message = `${name}: `; + const req: RequestSpec = requests[name]; req.httpRequest.body = replaceFileContents(req.httpRequest.body, bundlePath); @@ -72,7 +77,8 @@ export async function runRequestTests( } = await executeGotRequest(httpRequest); if (error) { - // fail + message += `FAIL\n${BULLET} error executing request: ${error}`; + console.log(message + "\n"); process.exitCode = getStatusCode() + 1; continue; } @@ -88,22 +94,31 @@ export async function runRequestTests( const parseError = parseBody(response, req.expectJson); if (parseError) { - // fail + message += `FAIL\n${BULLET} unable to parse body: ${parseError}`; + console.log(message + "\n"); process.exitCode = getStatusCode() + 1; continue; } const results = runAllTests(req.tests, response, req.options.stopOnFailure); - // compare the given tests with the results + const errors = compareReqAndResp(req, results); + if (errors.length > 0) { + message += " FAIL"; + process.exitCode = getStatusCode() + errors.length; - const { capturedVars, captureErrors } = captureVariables(req, response); - if (captureErrors) { - // warn - } - if (undefs.length > 0) { - // warn + message = [message, ...errors].join(`\n${BULLET}`); + } else { + message += " SUCCESS"; } - // write final message + const { capturedVars, captureErrors } = captureVariables(req, response); + rawReq.variables.mergeCapturedVariables(capturedVars); + if (captureErrors) + message = message + "\nWARNING: " + captureErrors; + + if (undefs.length > 0) + message = message + "\nWARNING: undefined vars - " + undefs.join(","); + + console.log(message + "\n"); } } From 300c421ea002f67b4e2bea64df661ee88f251390 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 23:14:02 +0530 Subject: [PATCH 19/47] test: updating jest config file to increase time-out, show console outputs --- jest.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jest.config.js b/jest.config.js index f7475c3..887ba0d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,6 @@ module.exports = { preset: "ts-jest", testEnvironment: "node", + silent: false, + testTimeout: 60*1000, }; \ No newline at end of file From 17c7c64b29ea434c956021929b8b163e6f7a47db Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 23:14:14 +0530 Subject: [PATCH 20/47] test: adding testing file --- examples/auto-tests.zzb | 363 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 examples/auto-tests.zzb diff --git a/examples/auto-tests.zzb b/examples/auto-tests.zzb new file mode 100644 index 0000000..3d331e0 --- /dev/null +++ b/examples/auto-tests.zzb @@ -0,0 +1,363 @@ +# yaml-language-server: $schema=../schemas/zzapi-bundle.schema.json + +# This bundle contains a series of test requests against various endpoints, +# most of them being the postman echo service. We use this to both serve as +# an example of how to create test bundles as well as testing zzapi itself. + +common: + baseUrl: https://postman-echo.com + headers: + Content-type: application/json + tests: + status: 200 + $h.Content-type: application/json; charset=utf-8 + +variables: + default: + username: tom + addressVar: + streetAddress: 7th street + city: Nara + postalCode: "560001" # string. This will make a difference when used in the body + getUrl: /get + fooVar: bar + agrostar: https://test-farmerapp.agrostar.in + pi: 3.14 + +requests: + simple-get-positive: { method: GET, url: /get } + + get-with-params-positive: + method: GET + url: /get + params: + foo1: bar1 + foo2: bar2 + tests: # old way of specifying json tests + json: + $.args.foo1: bar1 + $.args.foo2: bar2 + + get-with-params-no-value-positive: + method: GET + url: /get + params: + foo1: bar1 + foo2: + tests: # new way of specifying json + $.url: "https://postman-echo.com/get?foo1=bar1&foo2" + + get-with-params-as-array-positive: + method: GET + url: /get + params: + - { name: foo1, value: bar1 } + - { name: foo2, value: multi-1 } + - { name: foo2, value: multi-2 } + tests: + $.args.foo1: bar1 + $.args.foo2: { $eq: ["multi-1", "multi-2"] } + + get-with-params-values-as-array-positive: + method: GET + url: /get + params: + foo1: bar1 + foo2: + - multi-1 + - multi-2 + tests: + $.args.foo1: bar + $.args.foo2: { $eq: ["multi-1", "multi-2"], $skip: true } + + post-header-merge-positive: + url: /post + method: POST + headers: { X-Custom-Header: Custom Value } + body: + foo1: bar1 + foo2: 42 + tests: + $.data.foo1: bar1 + $.data.foo2: { $type: number, $gt: 41, $lt: 43 } + + post-header-override-positive: + url: /post + method: POST + headers: + Content-Type: text/plain + body: { foo: bar } + tests: + # if the server didn't parse this as a JSON, then, our content-type override is successful + $.data: '{"foo":"bar"}' + + status-404-positive: + url: /notfound + method: GET + tests: + status: 404 + $h.content-type: { $exists: false } + + status-401-positive: + url: /status/401 + method: GET + tests: { status: 401, $.status: 401 } + + encoding-positive: + method: GET + url: /get + params: + - { name: foo, value: 30% of 200 is 60 } + tests: + $.url: https://postman-echo.com/get?foo=30%25%20of%20200%20is%2060 + $.args.foo: 30% of 200 is 60 + + no-encoding-positive: + method: GET + url: /get + params: + foo: 30%25+of+200 + options: + rawParams: true + tests: + $.url: https://postman-echo.com/get?foo=30%25+of+200 + $.args.foo: 30% of 200 + + # The cookies endpoint does a redirect (no clue why), let's use that. + no-redirects-positive: + method: GET + url: /cookies/set + tests: + status: 302 + headers: { Location: /cookies, Content-Type: { $ne: application/json } } + + redirects-positive: + options: + follow: true + method: GET + url: /cookies/set + tests: + # code: 200 - this is inherited from common + $.cookies: { $exists: true } # Header names are case insensitive + + non-json-positive: + method: GET + url: /encoding/utf8 + tests: + body: { $regex: unicode demo, $options: i } + headers: + content-type: text/html; charset=utf-8 + + response-headers-positive: + method: GET + url: /response-headers + params: { foo: bar } + tests: { $h.foo: bar } + + override-base-url-positive: + method: GET + url: https://postman-echo.com/get + + variables-in-params-positive: + method: GET + url: $(getUrl) + params: + - { name: replaced1, value: $username } + - { name: replaced2, value: some$username } + - { name: replaced3, value: some$(username)else } + - { name: verbatim1, value: $usernameelse } + - { name: verbatim2, value: \$(username) } + tests: + $.args.replaced1: tom + $.args.replaced2: some$username + $.args.replaced3: sometomelse + $.args.verbatim1: { $ne: tomelse, $eq: $usernameelse } + $.args.verbatim2: { $ne: \tom, $eq: \$(username) } + + variables-in-headers-positive: + method: GET + url: $(getUrl) + headers: + - { name: foo, value: $(fooVar) } + tests: + $.headers.foo: bar + + variables-in-body-positive: + method: POST + url: /post + body: | # Alternate way of supplying the JSON string or any raw content + { + "foo1": "$(fooVar)", + "foo2": $pi + } + tests: + $.data.foo1: bar + $.data.foo2: { $type: number, $eq: 3.14 } + + object-variables-in-body-positive: + method: POST + url: /post + body: + name: Tom + address: $addressVar + tests: + $.data.name: Tom + $.data.address.postalCode: { $type: string, $eq: "560001" } + + # This returns headers in capital case. Ensure we match it. + header-case-positive: + method: GET + url: $agrostar/userservice/ping/ + tests: + headers: + content-type: application/json + + tests-positive: + method: POST + url: /post + body: + firstName: John + lastName: Doe + middleName: null + age: 26 + address: + streetAddress: naist street + city: Nara + postalCode: "560002" #string + phoneNumbers: + - type: mobile + number: 0123-4567-8888 + available: [7, 22] + - type: home + number: 0123-4567-8910 + available: [18, 22] + tests: + $.data.firstName: Joh + $.data.age: { $type: number, $eq: 26, $ne: 30, $gt: 25, $lt: 28 } + $.data.address: { $type: object, $size: 3, $exists: true } + $.data.address.city: Nara + $.data.address.postalCode: { $type: string, $size: 6, $ne: 560034 } + $.data.phoneNumbers: { $type: array, $size: 2, $exists: true } + $.data.phoneNumbers[0].type: mobile + $.data.phoneNumbers.1.type: home + $.data.phoneNumbers[?(@.type=="home")].number: 0123-4567-8910 + $.data.phoneNumbers[*].type: mobile + $.data.phoneNumbers[*].available: { $eq: [7, 22] } + $.data.phoneNumbers.0.available: { $eq: [7, 22], $type: array } + $.data.phoneNumbers.1.available: { $eq: "[18,22]", $type: array } + $.data.phoneNumbers.0: + $skip: true + $tests: + $.type: mobile + $.number: { $ne: 0123-4567-8910 } + $.available: { $eq: [7, 22] } + $.data.lastName: { $exists: true } + # $.data.middleName: { $exists: true, $type: "null" } + $.data.middleName: null + $.data.otherName: { $exists: false, $type: undefined } + # stress: ensure corner cases don't crash + $.data.otherName.value: { $exists: false } # don't recurse down undefined + $.data.middleName.value: { $exists: false } # don't recurse down null + + # All these tests should fail + tests-negative-response: + method: POST + url: /post + body: + age: 26 + name: John + address: 1, example street + numbers: [444, 222] + object: { foo: bar } + tests: + status: { $ne: 200 } + $h.content-type: { $exists: false } + # regular things that should fail + $.data.name: { $type: array } + $.data.missing: { $size: 0, $exists: true } # should report 2 failures + $.data.missing.missing.missing: { $exists: true } + $.data.numbers.0: "444" # 444 is not same as "444". We use === for $eq and !== for $neq + + # stress: ensure corner cases don't crash + $.data.age: { $size: 2 } # .length not supported for type: number + $.data.numbers[?(.@)]: 4 # invalid path + $.data.age.something: 55 # jsonpath should take care of this. + $.data.numbers[5]: 0 # jsonpath should take care of this + + # This request tests should all fail due to bad tests schema + # Ensure we don't crash on these. + tests-negative-schema: + method: POST + url: /post + body: + address: 1, example street + numbers: [444, 222] + object: { foo: bar } + tests: + status: { $ne: 200 } + headers: + content-type: { $exists: false } + # Uncomment the following to run tests. Schema validation makes these invalid. + # $.data.operator: { badop: any } # invalid operator badop. If you want to match an entire object/array, use it as the value of the $eq operator. + # $.data.numbers: [444, 222] + # $.data.address: { $type: invalid } + # $.data.object: { $exists: 4 } + + capture-response-positive: + method: POST + url: /post + body: + name: Tom + address: { city: Bangalore, pincode: 560002 } + setvars: + nameVar: $.data.name + addressVarNum: $.data.address + cityVar: $.data.address.city + pincodeVar: $.data.address.pincode + + capture-header-positive: + method: GET + url: /response-headers + params: + - { name: X-Custom-Header, value: Custom Header Value } + setvars: + customHeaderVar: $h.X-Custom-Header + + capture-checks-scalar-positive: + method: POST + url: /post + body: + name: $nameVar + city: $cityVar + pincode: $pincodeVar + customHeader: $customHeaderVar + tests: + $.data.name: Tom + $.data.city: Bangalore + $.data.pincode: 560002 + $.data.customHeader: Custom Header Value + + capture-checks-object-option-1-positive: + method: POST + url: /post + # The body is a string, so that we can use the JSON as is in the replacement + # Therefore, it is NOT "$addressVar" + body: | + { + "name": "Tom", + "address": $addressVarNum + } + tests: + $.data.name: Tom + $.data.address.city: Bangalore + $.data.address.pincode: 560002 # this time it is NOT a string. + + capture-checks-object-option-2-positive: + method: POST + url: /post + body: + name: Tom + address: $addressVarNum + tests: + $.data.name: Tom + $.data.address: { $eq: { city: Bangalore, pincode: 560002 } } From 4cb19aea5a8ef367354e91bde42f72442bb558bf Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 23:14:25 +0530 Subject: [PATCH 21/47] refactor: cleaning up output --- tests/callRequests.ts | 2 +- tests/execute_got_request.test.ts | 19 ++++++++++++++----- tests/getResponses.ts | 17 +++++++++-------- tests/runTests.ts | 4 ++-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tests/callRequests.ts b/tests/callRequests.ts index f53d5d4..5c14484 100644 --- a/tests/callRequests.ts +++ b/tests/callRequests.ts @@ -16,7 +16,7 @@ async function runRequestSpecs( for (const name in requests) { const request = requests[name]; - const autoHeaders: { [key: string]: string } = { "user-agent": "zzAPI-cli/" + CLI_VERSION }; + const autoHeaders: { [key: string]: string } = { "user-agent": "zzAPI-test" }; if (request.httpRequest.body && typeof request.httpRequest.body == "object") autoHeaders["content-type"] = "application/json"; diff --git a/tests/execute_got_request.test.ts b/tests/execute_got_request.test.ts index e618a64..c123543 100644 --- a/tests/execute_got_request.test.ts +++ b/tests/execute_got_request.test.ts @@ -1,10 +1,19 @@ import got from "got"; import { executeGotRequest } from "../src"; +import { RawRequest } from "./utils/requestUtils"; +import { callRequests } from "./callRequests"; +import { getStatusCode } from "./utils/errors"; -test("execute simple-get GOT request", async () => { - const response = await executeGotRequest(got("https://postman-echo.com/get", { method: "GET" })); - expect(response.byteLength).toBeGreaterThan(0); - expect(response.executionTime).toBeGreaterThan(0); - expect(response.error.length).toBeLessThan(1); +// test("execute simple-get GOT request", async () => { +// const response = await executeGotRequest(got("https://postman-echo.com/get", { method: "GET" })); +// expect(response.byteLength).toBeGreaterThan(0); +// expect(response.executionTime).toBeGreaterThan(0); +// expect(response.error.length).toBeLessThan(1); +// }); + +test("execute tests-bundle.zzb run", async () => { + const rawReq = new RawRequest("./examples/auto-tests.zzb", "default"); + await callRequests(rawReq); + expect(getStatusCode()).toBe(0); }); diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 831c003..d6f5dcf 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -61,7 +61,8 @@ export async function runRequestTests( console.log(`running ${bundleName}`); for (const name in requests) { - let message = `${name}: `; + let fail: boolean = true; + let message = `${name}: FAIL`; const req: RequestSpec = requests[name]; req.httpRequest.body = replaceFileContents(req.httpRequest.body, bundlePath); @@ -77,7 +78,7 @@ export async function runRequestTests( } = await executeGotRequest(httpRequest); if (error) { - message += `FAIL\n${BULLET} error executing request: ${error}`; + message += `\n${BULLET} error executing request: ${error}`; console.log(message + "\n"); process.exitCode = getStatusCode() + 1; continue; @@ -94,7 +95,7 @@ export async function runRequestTests( const parseError = parseBody(response, req.expectJson); if (parseError) { - message += `FAIL\n${BULLET} unable to parse body: ${parseError}`; + message += `\n${BULLET} unable to parse body: ${parseError}`; console.log(message + "\n"); process.exitCode = getStatusCode() + 1; continue; @@ -103,12 +104,11 @@ export async function runRequestTests( const results = runAllTests(req.tests, response, req.options.stopOnFailure); const errors = compareReqAndResp(req, results); if (errors.length > 0) { - message += " FAIL"; process.exitCode = getStatusCode() + errors.length; - message = [message, ...errors].join(`\n${BULLET}`); + message = [message, ...errors].join(`\n${BULLET} `); } else { - message += " SUCCESS"; + fail = false; } const { capturedVars, captureErrors } = captureVariables(req, response); @@ -118,7 +118,8 @@ export async function runRequestTests( if (undefs.length > 0) message = message + "\nWARNING: undefined vars - " + undefs.join(","); - - console.log(message + "\n"); + + if (fail) + console.log(message + "\n"); } } diff --git a/tests/runTests.ts b/tests/runTests.ts index 1966dc2..a98199f 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -3,7 +3,7 @@ import { Tests } from "../src/index"; function formatTestResult(res: TestResult, spec: string, skip?: boolean): string { const status = - (skip || res.pass ? " ✓ " : " ✗ ") + + (skip || res.pass ? "✓ " : "✗ ") + ("test " + spec + " ") + (res.op === ":" ? "$eq" : res.op) + " " + @@ -60,7 +60,7 @@ function allPositive(res: SpecResult, numTests: number): string[] { const errors: string[] = []; const [passed, all] = getResultData(res); - if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); + if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}`); if (passed === all) return errors; From 05066e36edfa0cd6eb83fbba240fae268c3d5e9e Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Tue, 18 Jun 2024 23:15:27 +0530 Subject: [PATCH 22/47] test: removing skip clauses --- examples/auto-tests.zzb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/auto-tests.zzb b/examples/auto-tests.zzb index 3d331e0..44c76a5 100644 --- a/examples/auto-tests.zzb +++ b/examples/auto-tests.zzb @@ -68,7 +68,7 @@ requests: - multi-2 tests: $.args.foo1: bar - $.args.foo2: { $eq: ["multi-1", "multi-2"], $skip: true } + $.args.foo2: { $eq: ["multi-1", "multi-2"] } post-header-merge-positive: url: /post @@ -246,7 +246,6 @@ requests: $.data.phoneNumbers.0.available: { $eq: [7, 22], $type: array } $.data.phoneNumbers.1.available: { $eq: "[18,22]", $type: array } $.data.phoneNumbers.0: - $skip: true $tests: $.type: mobile $.number: { $ne: 0123-4567-8910 } From 1674567dc2ff86fa7dcf0045c50399ee03581e83 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:12:01 +0530 Subject: [PATCH 23/47] feat: testing working, corrected expected number of tests --- examples/auto-tests.zzb | 10 +++++---- tests/runTests.ts | 50 ++++++++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/examples/auto-tests.zzb b/examples/auto-tests.zzb index 44c76a5..abc9bd4 100644 --- a/examples/auto-tests.zzb +++ b/examples/auto-tests.zzb @@ -55,7 +55,7 @@ requests: - { name: foo2, value: multi-1 } - { name: foo2, value: multi-2 } tests: - $.args.foo1: bar1 + $.args.foo1: { $eq: bar1, $skip: true } $.args.foo2: { $eq: ["multi-1", "multi-2"] } get-with-params-values-as-array-positive: @@ -67,7 +67,7 @@ requests: - multi-1 - multi-2 tests: - $.args.foo1: bar + $.args.foo1: bar1 $.args.foo2: { $eq: ["multi-1", "multi-2"] } post-header-merge-positive: @@ -232,7 +232,7 @@ requests: number: 0123-4567-8910 available: [18, 22] tests: - $.data.firstName: Joh + $.data.firstName: John $.data.age: { $type: number, $eq: 26, $ne: 30, $gt: 25, $lt: 28 } $.data.address: { $type: object, $size: 3, $exists: true } $.data.address.city: Nara @@ -253,7 +253,9 @@ requests: $.data.lastName: { $exists: true } # $.data.middleName: { $exists: true, $type: "null" } $.data.middleName: null - $.data.otherName: { $exists: false, $type: undefined } + $.data.otherName: + $tests: + $.: { $exists: true, $type: undefined, $skip: true } # stress: ensure corner cases don't crash $.data.otherName.value: { $exists: false } # don't recurse down undefined $.data.middleName.value: { $exists: false } # don't recurse down null diff --git a/tests/runTests.ts b/tests/runTests.ts index a98199f..3702ce2 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -1,17 +1,25 @@ import { RequestSpec, SpecResult, TestResult } from "../src/index"; import { Tests } from "../src/index"; -function formatTestResult(res: TestResult, spec: string, skip?: boolean): string { +function convertToString(item: any): string { + if (item === null) return "null"; + if (item === undefined) return "undefined"; + if (typeof item === "object") return JSON.stringify(item); + return item.toString(); + +} + +function formatTestResult(res: TestResult, spec: string | null, skip?: boolean): string { const status = (skip || res.pass ? "✓ " : "✗ ") + - ("test " + spec + " ") + + ("test " + (spec ?? "") + " ") + (res.op === ":" ? "$eq" : res.op) + " " + - res.expected; + convertToString(res.expected); if (res.pass) return status; if (skip) return status + " (skipped)"; - return status + " | actual " + res.received; + return status + " | actual " + res.received + (res.message ? `[${res.message}]` : ""); } function getResultData(res: SpecResult): [number, number] { @@ -64,14 +72,14 @@ function allPositive(res: SpecResult, numTests: number): string[] { if (passed === all) return errors; - function getFailingTests(res: SpecResult, spec: string): string[] { + function getFailingTests(res: SpecResult, spec: string | null): string[] { const failingTests: string[] = []; if (res.skipped) return failingTests; const rootFailing = res.results.filter((r) => !r.pass); failingTests.push(...rootFailing.map((r) => formatTestResult(r, spec, false))); - spec += " > " + res.spec ?? ""; + spec = (spec ? spec + " > " : "") + (res.spec ?? ""); for (const s of res.subResults) failingTests.push(...getFailingTests(s, spec)); @@ -82,13 +90,39 @@ function allPositive(res: SpecResult, numTests: number): string[] { return errors; } +const SKIP_CLAUSE = "$skip"; +const TEST_CLAUSE = "$tests"; +const NON_TEST_KEYS = ["$options", TEST_CLAUSE, SKIP_CLAUSE]; + function getNumTests(tests: Tests) { let numTests = 0; if (tests.body) numTests += 1; - if(tests.status) numTests += 1; + if (tests.status) numTests += 1; if (tests.headers) numTests += Object.keys(tests.headers).length; - if (tests.json) numTests += Object.keys(tests.json).length; + + function getJSONTests(json: { [key: string]: any }): number { + let count = 0; + for (const key in json) { + const assertion = json[key]; + if (assertion === null || typeof assertion !== "object") { + count += 1; + continue; + } + + if (assertion[SKIP_CLAUSE]) continue; + const testKeys = Object.keys(assertion); + + count += testKeys.filter((k) => !(NON_TEST_KEYS.includes(k))).length; + if (testKeys.includes(TEST_CLAUSE)) + count += getNumTests(assertion[TEST_CLAUSE]); + } + + return count; + } + + if (tests.json) + numTests += getJSONTests(tests.json); return numTests; From 79f901aac8912aba1c1eb0211a6642859f978060 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:14:05 +0530 Subject: [PATCH 24/47] test: added prettier check for tests dir --- .github/workflows/formatting.yml | 2 +- .prettierrc | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .prettierrc diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index 4fd1741..fec87c2 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -11,4 +11,4 @@ jobs: with: node-version: "20" - name: prettier check - run: npx prettier -c ./src --config ./src/.prettierrc + run: npx prettier -c ./src ./tests --config ./.prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0ee588c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 2, + "useTabs": false, + "printWidth": 105 +} From cc249b5286345a05541b3aac432f89afb5bf326e Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:15:42 +0530 Subject: [PATCH 25/47] test: further increased timeout, renamed index testing file --- jest.config.js | 2 +- src/.prettierrc | 5 - tests/getResponses.ts | 11 +- ...ot_request.test.ts => integration.test.ts} | 6 +- tests/runTests.ts | 128 ++++++++---------- 5 files changed, 67 insertions(+), 85 deletions(-) delete mode 100644 src/.prettierrc rename tests/{execute_got_request.test.ts => integration.test.ts} (80%) diff --git a/jest.config.js b/jest.config.js index 887ba0d..1a731ea 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,5 +2,5 @@ module.exports = { preset: "ts-jest", testEnvironment: "node", silent: false, - testTimeout: 60*1000, + testTimeout: 120*1000, }; \ No newline at end of file diff --git a/src/.prettierrc b/src/.prettierrc deleted file mode 100644 index 0ee588c..0000000 --- a/src/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tabWidth": 2, - "useTabs": false, - "printWidth": 105 -} diff --git a/tests/getResponses.ts b/tests/getResponses.ts index d6f5dcf..337eb24 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -113,13 +113,10 @@ export async function runRequestTests( const { capturedVars, captureErrors } = captureVariables(req, response); rawReq.variables.mergeCapturedVariables(capturedVars); - if (captureErrors) - message = message + "\nWARNING: " + captureErrors; + if (captureErrors) message = message + "\nWARNING: " + captureErrors; - if (undefs.length > 0) - message = message + "\nWARNING: undefined vars - " + undefs.join(","); - - if (fail) - console.log(message + "\n"); + if (undefs.length > 0) message = message + "\nWARNING: undefined vars - " + undefs.join(","); + + if (fail) console.log(message + "\n"); } } diff --git a/tests/execute_got_request.test.ts b/tests/integration.test.ts similarity index 80% rename from tests/execute_got_request.test.ts rename to tests/integration.test.ts index c123543..8d14346 100644 --- a/tests/execute_got_request.test.ts +++ b/tests/integration.test.ts @@ -13,7 +13,7 @@ import { getStatusCode } from "./utils/errors"; // }); test("execute tests-bundle.zzb run", async () => { - const rawReq = new RawRequest("./examples/auto-tests.zzb", "default"); - await callRequests(rawReq); - expect(getStatusCode()).toBe(0); + const rawReq = new RawRequest("./examples/auto-tests.zzb", "default"); + await callRequests(rawReq); + expect(getStatusCode()).toBe(0); }); diff --git a/tests/runTests.ts b/tests/runTests.ts index 3702ce2..c220e96 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -2,11 +2,10 @@ import { RequestSpec, SpecResult, TestResult } from "../src/index"; import { Tests } from "../src/index"; function convertToString(item: any): string { - if (item === null) return "null"; - if (item === undefined) return "undefined"; - if (typeof item === "object") return JSON.stringify(item); - return item.toString(); - + if (item === null) return "null"; + if (item === undefined) return "undefined"; + if (typeof item === "object") return JSON.stringify(item); + return item.toString(); } function formatTestResult(res: TestResult, spec: string | null, skip?: boolean): string { @@ -16,10 +15,10 @@ function formatTestResult(res: TestResult, spec: string | null, skip?: boolean): (res.op === ":" ? "$eq" : res.op) + " " + convertToString(res.expected); - if (res.pass) return status; - if (skip) return status + " (skipped)"; - - return status + " | actual " + res.received + (res.message ? `[${res.message}]` : ""); + if (res.pass) return status; + if (skip) return status + " (skipped)"; + + return status + " | actual " + res.received + (res.message ? `[${res.message}]` : ""); } function getResultData(res: SpecResult): [number, number] { @@ -39,29 +38,28 @@ function getResultData(res: SpecResult): [number, number] { } function allNegative(res: SpecResult, numTests: number): string[] { - const errors: string[] = []; - - const [passed, all] = getResultData(res); - if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); - - if (passed === 0) return errors; - - function getPassingTests(res: SpecResult, spec: string): string[] { - const passingTests: string[] = []; - if (res.skipped) return passingTests; - - const rootPassing = res.results.filter((r) => r.pass); - passingTests.push(...rootPassing.map((r) => formatTestResult(r, spec, false))); - - spec += " > " + res.spec ?? ""; - for (const s of res.subResults) - passingTests.push(...getPassingTests(s, spec)); - - return passingTests; - } - - errors.push(...getPassingTests(res, res.spec ?? "")); - return errors; + const errors: string[] = []; + + const [passed, all] = getResultData(res); + if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); + + if (passed === 0) return errors; + + function getPassingTests(res: SpecResult, spec: string): string[] { + const passingTests: string[] = []; + if (res.skipped) return passingTests; + + const rootPassing = res.results.filter((r) => r.pass); + passingTests.push(...rootPassing.map((r) => formatTestResult(r, spec, false))); + + spec += " > " + res.spec ?? ""; + for (const s of res.subResults) passingTests.push(...getPassingTests(s, spec)); + + return passingTests; + } + + errors.push(...getPassingTests(res, res.spec ?? "")); + return errors; } function allPositive(res: SpecResult, numTests: number): string[] { @@ -80,10 +78,9 @@ function allPositive(res: SpecResult, numTests: number): string[] { failingTests.push(...rootFailing.map((r) => formatTestResult(r, spec, false))); spec = (spec ? spec + " > " : "") + (res.spec ?? ""); - for (const s of res.subResults) - failingTests.push(...getFailingTests(s, spec)); + for (const s of res.subResults) failingTests.push(...getFailingTests(s, spec)); - return failingTests; + return failingTests; } errors.push(...getFailingTests(res, res.spec ?? "")); @@ -95,45 +92,38 @@ const TEST_CLAUSE = "$tests"; const NON_TEST_KEYS = ["$options", TEST_CLAUSE, SKIP_CLAUSE]; function getNumTests(tests: Tests) { - let numTests = 0; - if (tests.body) numTests += 1; - if (tests.status) numTests += 1; - if (tests.headers) - numTests += Object.keys(tests.headers).length; - - function getJSONTests(json: { [key: string]: any }): number { - let count = 0; - for (const key in json) { - const assertion = json[key]; - if (assertion === null || typeof assertion !== "object") { - count += 1; - continue; - } - - if (assertion[SKIP_CLAUSE]) continue; - const testKeys = Object.keys(assertion); - - count += testKeys.filter((k) => !(NON_TEST_KEYS.includes(k))).length; - if (testKeys.includes(TEST_CLAUSE)) - count += getNumTests(assertion[TEST_CLAUSE]); - } - - return count; + let numTests = 0; + if (tests.body) numTests += 1; + if (tests.status) numTests += 1; + if (tests.headers) numTests += Object.keys(tests.headers).length; + + function getJSONTests(json: { [key: string]: any }): number { + let count = 0; + for (const key in json) { + const assertion = json[key]; + if (assertion === null || typeof assertion !== "object") { + count += 1; + continue; + } + if (assertion[SKIP_CLAUSE]) continue; + + const testKeys = Object.keys(assertion); + if (testKeys.includes(TEST_CLAUSE)) count += getNumTests(assertion[TEST_CLAUSE]); + count += testKeys.filter((k) => !NON_TEST_KEYS.includes(k)).length; } - if (tests.json) - numTests += getJSONTests(tests.json); + return count; + } + + if (tests.json) numTests += getJSONTests(tests.json); - return numTests; - + return numTests; } export function compareReqAndResp(req: RequestSpec, res: SpecResult) { - const numTests = getNumTests(req.tests); - if (req.name.includes("positive")) - return allPositive(res, numTests); - else if (req.name.includes("negative")) - return allNegative(res, numTests); + const numTests = getNumTests(req.tests); + if (req.name.includes("positive")) return allPositive(res, numTests); + else if (req.name.includes("negative")) return allNegative(res, numTests); - return ["not a valid test type for automated tests"]; + return ["not a valid test type for automated tests"]; } From d92c5fff24768984041f3ffabafd4168d4a43528 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:17:02 +0530 Subject: [PATCH 26/47] refactor: removing bundle printing in test run to reduce log output from jest --- tests/getResponses.ts | 4 ---- tests/integration.test.ts | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 337eb24..bce4d08 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -56,10 +56,6 @@ export async function runRequestTests( rawReq: RawRequest, ): Promise { const bundlePath = rawReq.bundle.bundlePath; - const bundleName = bundlePath.substring(bundlePath.lastIndexOf(path.sep) + 1); - - console.log(`running ${bundleName}`); - for (const name in requests) { let fail: boolean = true; let message = `${name}: FAIL`; diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 8d14346..053d0c6 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -12,7 +12,7 @@ import { getStatusCode } from "./utils/errors"; // expect(response.error.length).toBeLessThan(1); // }); -test("execute tests-bundle.zzb run", async () => { +test("execute auto-tests.zzb in default env", async () => { const rawReq = new RawRequest("./examples/auto-tests.zzb", "default"); await callRequests(rawReq); expect(getStatusCode()).toBe(0); From b61b69e05f8ef11ddbc66e8845cfa22f390f473b Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:19:08 +0530 Subject: [PATCH 27/47] refactor test: renaming recursive call in getNumTests --- tests/runTests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/runTests.ts b/tests/runTests.ts index c220e96..95a7bb3 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -97,7 +97,7 @@ function getNumTests(tests: Tests) { if (tests.status) numTests += 1; if (tests.headers) numTests += Object.keys(tests.headers).length; - function getJSONTests(json: { [key: string]: any }): number { + function getNumJSON(json: { [key: string]: any }): number { let count = 0; for (const key in json) { const assertion = json[key]; @@ -115,7 +115,7 @@ function getNumTests(tests: Tests) { return count; } - if (tests.json) numTests += getJSONTests(tests.json); + if (tests.json) numTests += getNumJSON(tests.json); return numTests; } From a8bffd166428b4e88c29c353a2807e3e5e799315 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:26:42 +0530 Subject: [PATCH 28/47] test: moved auto-tests to the tests dir, from examples --- {examples => tests/bundles}/auto-tests.zzb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {examples => tests/bundles}/auto-tests.zzb (99%) diff --git a/examples/auto-tests.zzb b/tests/bundles/auto-tests.zzb similarity index 99% rename from examples/auto-tests.zzb rename to tests/bundles/auto-tests.zzb index abc9bd4..2c4e818 100644 --- a/examples/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../schemas/zzapi-bundle.schema.json +# yaml-language-server: $schema=../../schemas/zzapi-bundle.schema.json # This bundle contains a series of test requests against various endpoints, # most of them being the postman echo service. We use this to both serve as From 5fc59214048662e79c98048dbf960531b8a9b043 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 00:29:43 +0530 Subject: [PATCH 29/47] refactor: reorganised headers --- tests/getResponses.ts | 3 +-- tests/integration.test.ts | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/getResponses.ts b/tests/getResponses.ts index bce4d08..202a0c1 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -1,5 +1,3 @@ -import path from "path"; - import { RequestSpec, ResponseData, @@ -13,6 +11,7 @@ import { import { RawRequest } from "./utils/requestUtils"; import { replaceFileContents } from "./utils/fileContents"; import { getStatusCode } from "./utils/errors"; + import { compareReqAndResp } from "./runTests"; function parseBody(response: ResponseData, expectJson?: boolean): string | undefined { diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 053d0c6..6b23355 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,10 +1,8 @@ -import got from "got"; - -import { executeGotRequest } from "../src"; import { RawRequest } from "./utils/requestUtils"; -import { callRequests } from "./callRequests"; import { getStatusCode } from "./utils/errors"; +import { callRequests } from "./callRequests"; + // test("execute simple-get GOT request", async () => { // const response = await executeGotRequest(got("https://postman-echo.com/get", { method: "GET" })); // expect(response.byteLength).toBeGreaterThan(0); @@ -13,7 +11,7 @@ import { getStatusCode } from "./utils/errors"; // }); test("execute auto-tests.zzb in default env", async () => { - const rawReq = new RawRequest("./examples/auto-tests.zzb", "default"); + const rawReq = new RawRequest("./tests/bundles/auto-tests.zzb", "default"); await callRequests(rawReq); expect(getStatusCode()).toBe(0); }); From 42c8e336cdfb9ed0f373deb025b750f63c9a710b Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 03:19:32 +0530 Subject: [PATCH 30/47] test: moving some variables to separate varfiles to test reading from them --- tests/bundles/auto-test.zzv | 9 +++++++++ tests/bundles/auto-tests.zzb | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 tests/bundles/auto-test.zzv diff --git a/tests/bundles/auto-test.zzv b/tests/bundles/auto-test.zzv new file mode 100644 index 0000000..6ca1407 --- /dev/null +++ b/tests/bundles/auto-test.zzv @@ -0,0 +1,9 @@ +# to ensure that the module is reading from varfiles, we put some essential +# variables in the varfiles + +default: + getUrl: /get + fooVar: bar +random: + getUrl: /random + fooVar: random diff --git a/tests/bundles/auto-tests.zzb b/tests/bundles/auto-tests.zzb index 2c4e818..4097a60 100644 --- a/tests/bundles/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -19,8 +19,6 @@ variables: streetAddress: 7th street city: Nara postalCode: "560001" # string. This will make a difference when used in the body - getUrl: /get - fooVar: bar agrostar: https://test-farmerapp.agrostar.in pi: 3.14 From 0b68645dc8b2c18462bc0f251a44fc1f203c95bb Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 19 Jun 2024 03:22:04 +0530 Subject: [PATCH 31/47] build: fixing security vulnerability by npm audit fix --- package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index e451675..e3d272c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zzapi", - "version": "1.5.0", + "version": "1.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zzapi", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT", "dependencies": { "got": "11.8.6", @@ -2074,12 +2074,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2804,9 +2804,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" From fbb110f7f5b465ee6d1c69f9af19134c39132e96 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 22 Jun 2024 14:49:48 +0530 Subject: [PATCH 32/47] feat: if name contains negative, expecting all to fail, else, expecting all to pass --- tests/runTests.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/runTests.ts b/tests/runTests.ts index 95a7bb3..481d7a1 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -122,8 +122,6 @@ function getNumTests(tests: Tests) { export function compareReqAndResp(req: RequestSpec, res: SpecResult) { const numTests = getNumTests(req.tests); - if (req.name.includes("positive")) return allPositive(res, numTests); - else if (req.name.includes("negative")) return allNegative(res, numTests); - - return ["not a valid test type for automated tests"]; + if (req.name.includes("negative")) return allNegative(res, numTests); + return allPositive(res, numTests); } From 9454c3bce4c43b4b461e7450305c11042c133153 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 22 Jun 2024 14:52:13 +0530 Subject: [PATCH 33/47] feat: removing file replacement as it is the responsibility of the runner, not the module --- tests/getResponses.ts | 3 --- tests/utils/fileContents.ts | 18 ------------------ 2 files changed, 21 deletions(-) delete mode 100644 tests/utils/fileContents.ts diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 202a0c1..4565a37 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -9,7 +9,6 @@ import { } from "../src/index"; import { RawRequest } from "./utils/requestUtils"; -import { replaceFileContents } from "./utils/fileContents"; import { getStatusCode } from "./utils/errors"; import { compareReqAndResp } from "./runTests"; @@ -60,8 +59,6 @@ export async function runRequestTests( let message = `${name}: FAIL`; const req: RequestSpec = requests[name]; - req.httpRequest.body = replaceFileContents(req.httpRequest.body, bundlePath); - const undefs = replaceVariablesInRequest(req, rawReq.variables.getAllVariables()); const httpRequest = constructGotRequest(req); diff --git a/tests/utils/fileContents.ts b/tests/utils/fileContents.ts deleted file mode 100644 index c9360be..0000000 --- a/tests/utils/fileContents.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; - -export function replaceFileContents(body: T, bundlePath: string): T { - if (typeof body !== "string") return body; - - /* - finds all file:// instances with atleast 1 succeeding word character - matches the file-name referred to by this instance - */ - const fileRegex = /file:\/\/([^\s]+)/g; - return body.replace(fileRegex, (match, givenFilePath) => { - if (match !== body) return match; // we only perform a replacement if file:// is the ENTIRE body - - const filePath = path.resolve(path.dirname(bundlePath), givenFilePath); - return fs.readFileSync(filePath, "utf-8"); - }) as T & string; -} From 59904625ce1dd8d15d857ebf748319b7eb18f6c1 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 22 Jun 2024 15:04:25 +0530 Subject: [PATCH 34/47] feat: imrpoved recursive test formatting --- tests/bundles/auto-tests.zzb | 4 ++-- tests/runTests.ts | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/bundles/auto-tests.zzb b/tests/bundles/auto-tests.zzb index 4097a60..c5999b9 100644 --- a/tests/bundles/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -166,7 +166,7 @@ requests: - { name: verbatim1, value: $usernameelse } - { name: verbatim2, value: \$(username) } tests: - $.args.replaced1: tom + $.args.replaced1: to $.args.replaced2: some$username $.args.replaced3: sometomelse $.args.verbatim1: { $ne: tomelse, $eq: $usernameelse } @@ -178,7 +178,7 @@ requests: headers: - { name: foo, value: $(fooVar) } tests: - $.headers.foo: bar + $.headers.foo: ba variables-in-body-positive: method: POST diff --git a/tests/runTests.ts b/tests/runTests.ts index 481d7a1..35bac92 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -11,7 +11,7 @@ function convertToString(item: any): string { function formatTestResult(res: TestResult, spec: string | null, skip?: boolean): string { const status = (skip || res.pass ? "✓ " : "✗ ") + - ("test " + (spec ?? "") + " ") + + ("test " + spec + " ") + (res.op === ":" ? "$eq" : res.op) + " " + convertToString(res.expected); @@ -52,8 +52,7 @@ function allNegative(res: SpecResult, numTests: number): string[] { const rootPassing = res.results.filter((r) => r.pass); passingTests.push(...rootPassing.map((r) => formatTestResult(r, spec, false))); - spec += " > " + res.spec ?? ""; - for (const s of res.subResults) passingTests.push(...getPassingTests(s, spec)); + for (const s of res.subResults) passingTests.push(...getPassingTests(s, spec + " > " + s.spec)); return passingTests; } @@ -77,8 +76,7 @@ function allPositive(res: SpecResult, numTests: number): string[] { const rootFailing = res.results.filter((r) => !r.pass); failingTests.push(...rootFailing.map((r) => formatTestResult(r, spec, false))); - spec = (spec ? spec + " > " : "") + (res.spec ?? ""); - for (const s of res.subResults) failingTests.push(...getFailingTests(s, spec)); + for (const s of res.subResults) failingTests.push(...getFailingTests(s, spec + " > " + s.spec)); return failingTests; } From 1253f6e592972d67e8381c43c5ddc09820c16b9c Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Sat, 22 Jun 2024 15:07:25 +0530 Subject: [PATCH 35/47] bug: corrected test file --- tests/bundles/auto-tests.zzb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bundles/auto-tests.zzb b/tests/bundles/auto-tests.zzb index c5999b9..4097a60 100644 --- a/tests/bundles/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -166,7 +166,7 @@ requests: - { name: verbatim1, value: $usernameelse } - { name: verbatim2, value: \$(username) } tests: - $.args.replaced1: to + $.args.replaced1: tom $.args.replaced2: some$username $.args.replaced3: sometomelse $.args.verbatim1: { $ne: tomelse, $eq: $usernameelse } @@ -178,7 +178,7 @@ requests: headers: - { name: foo, value: $(fooVar) } tests: - $.headers.foo: ba + $.headers.foo: bar variables-in-body-positive: method: POST From d93f83bc69a0517102dd1a73c65f50c7f264ae38 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 02:58:41 +0530 Subject: [PATCH 36/47] todo: adding instruction to introduce skipped in names --- tests/runTests.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/runTests.ts b/tests/runTests.ts index 35bac92..5b946fe 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -118,6 +118,7 @@ function getNumTests(tests: Tests) { return numTests; } +// TODO. also introduce skipped -> ensure nothing negative or failure export function compareReqAndResp(req: RequestSpec, res: SpecResult) { const numTests = getNumTests(req.tests); if (req.name.includes("negative")) return allNegative(res, numTests); From e080398226b327a88c5aba3f3e50b91917ab1ac9 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 03:01:11 +0530 Subject: [PATCH 37/47] feat: removing undefined vars from env var replacement --- src/variableParser.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index 035c2f8..acdd558 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -38,34 +38,27 @@ export function getEnvironments(bundleContent: string | undefined, varFileConten return [...uniqueNames]; } -function replaceEnvironmentVariables(vars: Variables): { - replacedVars: Variables; - undefinedVars: string[]; -} { +function replaceEnvironmentVariables(vars: Variables): Variables { const PREFIX = "$env."; - const undefinedVars: string[] = []; const getVal = (val: any): any => { if (typeof val !== "string" || !val.startsWith(PREFIX)) return val; const envVarName = val.slice(PREFIX.length); - if (envVarName in process.env) return process.env[envVarName]; - - undefinedVars.push(val); - return val; + return envVarName in process.env ? process.env[envVarName] : val; }; const replacedVars: Variables = {}; for (const key in vars) replacedVars[key] = getVal(vars[key]); - return { replacedVars, undefinedVars }; + return replacedVars; } export function loadVariables( envName: string | undefined, bundleContent: string | undefined, varFileContents: string[], -): { vars: Variables; undefinedVars: string[] } { +): Variables { if (!envName) return { vars: {}, undefinedVars: [] }; const allBundleVariables = getBundleVariables(bundleContent); From 00b55c0a9a1c051be5a9c6e0246e07bd293d179f Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 03:03:04 +0530 Subject: [PATCH 38/47] bug: removing returns of empty undefined vars from loadVars --- src/variableParser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/variableParser.ts b/src/variableParser.ts index acdd558..0d2e715 100644 --- a/src/variableParser.ts +++ b/src/variableParser.ts @@ -59,7 +59,7 @@ export function loadVariables( bundleContent: string | undefined, varFileContents: string[], ): Variables { - if (!envName) return { vars: {}, undefinedVars: [] }; + if (!envName) return {}; const allBundleVariables = getBundleVariables(bundleContent); const bundleVars: Variables = allBundleVariables[envName] ?? {}; @@ -71,7 +71,7 @@ export function loadVariables( }); const basicVars = Object.assign({}, envVars, bundleVars); - const { replacedVars: vars, undefinedVars } = replaceEnvironmentVariables(basicVars); + const vars = replaceEnvironmentVariables(basicVars); - return { vars, undefinedVars }; + return vars; } From b713a9f8987100c0324d019ef66fa0f1492a08ad Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:20:39 +0530 Subject: [PATCH 39/47] feat: introduce "multi" as a non-operator clause --- tests/runTests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runTests.ts b/tests/runTests.ts index 5b946fe..f35a1bb 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -87,7 +87,7 @@ function allPositive(res: SpecResult, numTests: number): string[] { const SKIP_CLAUSE = "$skip"; const TEST_CLAUSE = "$tests"; -const NON_TEST_KEYS = ["$options", TEST_CLAUSE, SKIP_CLAUSE]; +const NON_TEST_KEYS = ["$options", "multi", TEST_CLAUSE, SKIP_CLAUSE]; function getNumTests(tests: Tests) { let numTests = 0; From a9c658615fd4fef6ad361a89750d6beb4291aeb6 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:22:45 +0530 Subject: [PATCH 40/47] chore: remove redundant bundlePath var --- tests/getResponses.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 4565a37..8b66dbb 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -53,7 +53,6 @@ export async function runRequestTests( requests: { [name: string]: RequestSpec }, rawReq: RawRequest, ): Promise { - const bundlePath = rawReq.bundle.bundlePath; for (const name in requests) { let fail: boolean = true; let message = `${name}: FAIL`; From 21e71f2438e0268d47304290ef4e64f207a74b84 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:33:04 +0530 Subject: [PATCH 41/47] feat: introduce 'skip' clause in names to allow for all tests to be skipped --- tests/runTests.ts | 52 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/tests/runTests.ts b/tests/runTests.ts index f35a1bb..82188c2 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -21,26 +21,34 @@ function formatTestResult(res: TestResult, spec: string | null, skip?: boolean): return status + " | actual " + res.received + (res.message ? `[${res.message}]` : ""); } -function getResultData(res: SpecResult): [number, number] { - if (res.skipped) return [0, 0]; +// pass, skipped, total +function getResultData(res: SpecResult): [number, number, number] { + let passed = 0, + skipped = 0, + total = 0; const rootResults = res.results; - let passed = rootResults.filter((r) => r.pass).length; - let all = rootResults.length; + if (res.skipped) skipped = rootResults.length; + else passed = rootResults.filter((r) => r.pass).length; + total = rootResults.length; for (const s of res.subResults) { - const [subPassed, subAll] = getResultData(s); + if (res.skipped && !s.skipped) + throw new Error("root result skipped but sub result not marked as skipped"); + + const [subPassed, subSkipped, subTotal] = getResultData(s); passed += subPassed; - all += subAll; + skipped += subSkipped; + total += subTotal; } - return [passed, all]; + return [passed, skipped, total]; } function allNegative(res: SpecResult, numTests: number): string[] { const errors: string[] = []; - const [passed, all] = getResultData(res); + const [passed, _skipped, all] = getResultData(res); if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); if (passed === 0) return errors; @@ -61,10 +69,33 @@ function allNegative(res: SpecResult, numTests: number): string[] { return errors; } +function allSkipped(res: SpecResult, numTests: number): string[] { + const errors: string[] = []; + + const [_passed, skipped, all] = getResultData(res); + if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); + if (skipped === all) return errors; + + function getSkippedTests(res: SpecResult, spec: string): string[] { + const skippedTests: string[] = []; + if (!res.skipped) return skippedTests; + + const rootSkipped = res.results; + skippedTests.push(...rootSkipped.map((r) => formatTestResult(r, spec, true))); + + for (const s of res.subResults) skippedTests.push(...getSkippedTests(s, spec + " > " + s.spec)); + + return skippedTests; + } + + errors.push(...getSkippedTests(res, res.spec ?? "")); + return errors; +} + function allPositive(res: SpecResult, numTests: number): string[] { const errors: string[] = []; - const [passed, all] = getResultData(res); + const [passed, _skipped, all] = getResultData(res); if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}`); if (passed === all) return errors; @@ -103,7 +134,6 @@ function getNumTests(tests: Tests) { count += 1; continue; } - if (assertion[SKIP_CLAUSE]) continue; const testKeys = Object.keys(assertion); if (testKeys.includes(TEST_CLAUSE)) count += getNumTests(assertion[TEST_CLAUSE]); @@ -118,9 +148,9 @@ function getNumTests(tests: Tests) { return numTests; } -// TODO. also introduce skipped -> ensure nothing negative or failure export function compareReqAndResp(req: RequestSpec, res: SpecResult) { const numTests = getNumTests(req.tests); if (req.name.includes("negative")) return allNegative(res, numTests); + if (req.name.includes("skip")) return allSkipped(res, numTests); return allPositive(res, numTests); } From 25f7100ea486b942a151b4e95cf26e718a7733d2 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:46:57 +0530 Subject: [PATCH 42/47] bug: corrected non-skipped tests collection --- tests/bundles/auto-tests.zzb | 22 ++++++++++++++++++---- tests/runTests.ts | 17 +++++++---------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/tests/bundles/auto-tests.zzb b/tests/bundles/auto-tests.zzb index 4097a60..ff2b84f 100644 --- a/tests/bundles/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -36,6 +36,22 @@ requests: $.args.foo1: bar1 $.args.foo2: bar2 + get-with-params-skip: + method: GET + url: /get + params: + foo1: bar1 + foo2: bar2 + tests: # old way of specifying json tests + status: { $eq: 0, $skip: true } + $h.Content-type: { $eq: random-test, $skip: true } + json: + $.args: + $skip: false + $tests: + $.foo1 : bar2 + $.foo2: bar1 + get-with-params-no-value-positive: method: GET url: /get @@ -53,7 +69,7 @@ requests: - { name: foo2, value: multi-1 } - { name: foo2, value: multi-2 } tests: - $.args.foo1: { $eq: bar1, $skip: true } + $.args.foo1: bar1 $.args.foo2: { $eq: ["multi-1", "multi-2"] } get-with-params-values-as-array-positive: @@ -251,9 +267,7 @@ requests: $.data.lastName: { $exists: true } # $.data.middleName: { $exists: true, $type: "null" } $.data.middleName: null - $.data.otherName: - $tests: - $.: { $exists: true, $type: undefined, $skip: true } + $.data.otherName: { $exists: false, $type: undefined } # stress: ensure corner cases don't crash $.data.otherName.value: { $exists: false } # don't recurse down undefined $.data.middleName.value: { $exists: false } # don't recurse down null diff --git a/tests/runTests.ts b/tests/runTests.ts index 82188c2..1b858fd 100644 --- a/tests/runTests.ts +++ b/tests/runTests.ts @@ -71,24 +71,21 @@ function allNegative(res: SpecResult, numTests: number): string[] { function allSkipped(res: SpecResult, numTests: number): string[] { const errors: string[] = []; - const [_passed, skipped, all] = getResultData(res); if (all !== numTests) errors.push(`expected ${numTests} tests, got ${all}\n`); if (skipped === all) return errors; - function getSkippedTests(res: SpecResult, spec: string): string[] { - const skippedTests: string[] = []; - if (!res.skipped) return skippedTests; - - const rootSkipped = res.results; - skippedTests.push(...rootSkipped.map((r) => formatTestResult(r, spec, true))); + function getRanTests(res: SpecResult, spec: string): string[] { + const nonSkippedTests: string[] = []; + if (res.skipped) return nonSkippedTests; - for (const s of res.subResults) skippedTests.push(...getSkippedTests(s, spec + " > " + s.spec)); + nonSkippedTests.push(...res.results.map((r) => formatTestResult(r, spec, res.skipped))); + for (const s of res.subResults) nonSkippedTests.push(...getRanTests(s, spec + " > " + s.spec)); - return skippedTests; + return nonSkippedTests; } - errors.push(...getSkippedTests(res, res.spec ?? "")); + errors.push(...getRanTests(res, res.spec ?? "")); return errors; } From 07c8da0fbae17eb5d93d57183c897152ff81da32 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:48:41 +0530 Subject: [PATCH 43/47] test: corrected test file to add skip clause testing --- tests/bundles/auto-tests.zzb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bundles/auto-tests.zzb b/tests/bundles/auto-tests.zzb index ff2b84f..c9c4856 100644 --- a/tests/bundles/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -47,7 +47,7 @@ requests: $h.Content-type: { $eq: random-test, $skip: true } json: $.args: - $skip: false + $skip: true $tests: $.foo1 : bar2 $.foo2: bar1 From 793b94651aee8effaf59413a235d56d4a155d9fe Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:48:59 +0530 Subject: [PATCH 44/47] refactor: test file --- tests/bundles/auto-tests.zzb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/bundles/auto-tests.zzb b/tests/bundles/auto-tests.zzb index c9c4856..8ed4365 100644 --- a/tests/bundles/auto-tests.zzb +++ b/tests/bundles/auto-tests.zzb @@ -46,11 +46,11 @@ requests: status: { $eq: 0, $skip: true } $h.Content-type: { $eq: random-test, $skip: true } json: - $.args: - $skip: true - $tests: - $.foo1 : bar2 - $.foo2: bar1 + $.args: + $skip: true + $tests: + $.foo1: bar2 + $.foo2: bar1 get-with-params-no-value-positive: method: GET From 9ca0e4295a32efc3ad1179cbfc5a7fcc098b2d01 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:53:13 +0530 Subject: [PATCH 45/47] feat: returning number of failing requests instead of incrementing exit code --- tests/callRequests.ts | 12 ++++++------ tests/getResponses.ts | 15 +++++++++------ tests/integration.test.ts | 4 +--- tests/utils/errors.ts | 3 --- 4 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 tests/utils/errors.ts diff --git a/tests/callRequests.ts b/tests/callRequests.ts index 5c14484..398342b 100644 --- a/tests/callRequests.ts +++ b/tests/callRequests.ts @@ -11,8 +11,8 @@ import { runRequestTests } from "./getResponses"; async function runRequestSpecs( requests: { [name: string]: RequestSpec }, - rawRequest: RawRequest, -): Promise { + rawRequest: RawRequest +): Promise { for (const name in requests) { const request = requests[name]; @@ -23,17 +23,17 @@ async function runRequestSpecs( request.httpRequest.headers = Object.assign(autoHeaders, request.httpRequest.headers); } - await runRequestTests(requests, rawRequest); + return await runRequestTests(requests, rawRequest); } -export async function callRequests(request: RawRequest): Promise { +export async function callRequests(request: RawRequest): Promise { try { // load the variables const env = request.envName; const loadedVariables: Variables = loadVariables( env, request.bundle.bundleContents, - getVarFileContents(path.dirname(request.bundle.bundlePath)), + getVarFileContents(path.dirname(request.bundle.bundlePath)) ); if (env && Object.keys(loadedVariables).length < 1) console.log(`warning: no variables added from env: ${env}`); @@ -54,5 +54,5 @@ export async function callRequests(request: RawRequest): Promise { } // finally, run the request specs - await runRequestSpecs(allRequests, request); + return await runRequestSpecs(allRequests, request); } diff --git a/tests/getResponses.ts b/tests/getResponses.ts index 8b66dbb..e068c87 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -9,7 +9,6 @@ import { } from "../src/index"; import { RawRequest } from "./utils/requestUtils"; -import { getStatusCode } from "./utils/errors"; import { compareReqAndResp } from "./runTests"; @@ -51,8 +50,10 @@ const BULLET = "-->"; export async function runRequestTests( requests: { [name: string]: RequestSpec }, - rawReq: RawRequest, -): Promise { + rawReq: RawRequest +): Promise { + let numFailingReqs = 0; // any request that does not perform as expected + for (const name in requests) { let fail: boolean = true; let message = `${name}: FAIL`; @@ -71,7 +72,7 @@ export async function runRequestTests( if (error) { message += `\n${BULLET} error executing request: ${error}`; console.log(message + "\n"); - process.exitCode = getStatusCode() + 1; + numFailingReqs += 1; continue; } @@ -88,14 +89,14 @@ export async function runRequestTests( if (parseError) { message += `\n${BULLET} unable to parse body: ${parseError}`; console.log(message + "\n"); - process.exitCode = getStatusCode() + 1; + numFailingReqs += 1; continue; } const results = runAllTests(req.tests, response, req.options.stopOnFailure); const errors = compareReqAndResp(req, results); if (errors.length > 0) { - process.exitCode = getStatusCode() + errors.length; + numFailingReqs += errors.length; message = [message, ...errors].join(`\n${BULLET} `); } else { @@ -110,4 +111,6 @@ export async function runRequestTests( if (fail) console.log(message + "\n"); } + + return numFailingReqs; } diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 6b23355..ddc2f43 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,5 +1,4 @@ import { RawRequest } from "./utils/requestUtils"; -import { getStatusCode } from "./utils/errors"; import { callRequests } from "./callRequests"; @@ -12,6 +11,5 @@ import { callRequests } from "./callRequests"; test("execute auto-tests.zzb in default env", async () => { const rawReq = new RawRequest("./tests/bundles/auto-tests.zzb", "default"); - await callRequests(rawReq); - expect(getStatusCode()).toBe(0); + expect(await callRequests(rawReq)).toBe(0); }); diff --git a/tests/utils/errors.ts b/tests/utils/errors.ts deleted file mode 100644 index 9d5c969..0000000 --- a/tests/utils/errors.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function getStatusCode(): number { - return process.exitCode ?? 0; -} From 1e30024a74a67ae9f726732c92ed575b30f4c4b2 Mon Sep 17 00:00:00 2001 From: Varun0157 Date: Wed, 26 Jun 2024 14:53:58 +0530 Subject: [PATCH 46/47] refactor, chore: removed redundant test, refactored w/ prettier --- tests/callRequests.ts | 4 ++-- tests/getResponses.ts | 2 +- tests/integration.test.ts | 7 ------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/tests/callRequests.ts b/tests/callRequests.ts index 398342b..79aa306 100644 --- a/tests/callRequests.ts +++ b/tests/callRequests.ts @@ -11,7 +11,7 @@ import { runRequestTests } from "./getResponses"; async function runRequestSpecs( requests: { [name: string]: RequestSpec }, - rawRequest: RawRequest + rawRequest: RawRequest, ): Promise { for (const name in requests) { const request = requests[name]; @@ -33,7 +33,7 @@ export async function callRequests(request: RawRequest): Promise { const loadedVariables: Variables = loadVariables( env, request.bundle.bundleContents, - getVarFileContents(path.dirname(request.bundle.bundlePath)) + getVarFileContents(path.dirname(request.bundle.bundlePath)), ); if (env && Object.keys(loadedVariables).length < 1) console.log(`warning: no variables added from env: ${env}`); diff --git a/tests/getResponses.ts b/tests/getResponses.ts index e068c87..c2da848 100644 --- a/tests/getResponses.ts +++ b/tests/getResponses.ts @@ -50,7 +50,7 @@ const BULLET = "-->"; export async function runRequestTests( requests: { [name: string]: RequestSpec }, - rawReq: RawRequest + rawReq: RawRequest, ): Promise { let numFailingReqs = 0; // any request that does not perform as expected diff --git a/tests/integration.test.ts b/tests/integration.test.ts index ddc2f43..9fcd142 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -2,13 +2,6 @@ import { RawRequest } from "./utils/requestUtils"; import { callRequests } from "./callRequests"; -// test("execute simple-get GOT request", async () => { -// const response = await executeGotRequest(got("https://postman-echo.com/get", { method: "GET" })); -// expect(response.byteLength).toBeGreaterThan(0); -// expect(response.executionTime).toBeGreaterThan(0); -// expect(response.error.length).toBeLessThan(1); -// }); - test("execute auto-tests.zzb in default env", async () => { const rawReq = new RawRequest("./tests/bundles/auto-tests.zzb", "default"); expect(await callRequests(rawReq)).toBe(0); From 3498b601721e433527f7973c78afa6f76d29dd11 Mon Sep 17 00:00:00 2001 From: Vasan Subramanian Date: Sat, 29 Jun 2024 17:20:47 +0530 Subject: [PATCH 47/47] Added changelog for auotmated tests --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index add73c9..313e9a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## [todo] +- Introduced automated tests + ## 1.5.1 - Fixed regression (status check failures were not detected)