lib/apiScenario/postmanHelper.ts (258 lines of code) (raw):
import * as jsonPointer from "json-pointer";
import {
Collection,
Event,
EventDefinition,
EventList,
Item,
ItemDefinition,
ItemGroup,
ItemGroupDefinition,
} from "postman-collection";
import { getRandomString } from "../util/utils";
import { ArmTemplate, StepResponseAssertion } from "./apiScenarioTypes";
export const PREPARE_FOLDER = "__Prepare__";
export const CLEANUP_FOLDER = "__CleanUp__";
function parseJsonPointer(jsonPointer: string): string[] {
if (jsonPointer === "") {
return [];
}
return jsonPointer
.substring(1)
.split(/\//)
.map((seg) => seg.replace(/~1/g, "/").replace(/~0/g, "~"));
}
interface TestScriptParameter {
name: string;
types: TestScriptType[];
variables?: Map<string, string>;
armTemplate?: ArmTemplate;
responseAssertion?: StepResponseAssertion;
}
export type TestScriptType =
| "StatusCodeAssertion"
| "ResponseDataAssertion"
| "DetailResponseLog"
| "OverwriteVariables"
| "ARMDeploymentStatusAssertion"
| "ExtractARMTemplateOutput";
export function createItem(definition?: ItemDefinition): Item {
return new Item({
id: getRandomString(),
...definition,
});
}
export function addItemGroup(
target: Collection,
definition?: ItemGroupDefinition
): ItemGroup<Item> {
const itemGroup = new ItemGroup<Item>({
id: getRandomString(),
...definition,
});
target.items.add(itemGroup);
return itemGroup;
}
export function addEvent(
target: EventList,
listen: "prerequest" | "test",
script: string | string[],
additionalDefinition?: EventDefinition
) {
target.add(
new Event({
id: getRandomString(),
listen,
script: {
id: getRandomString(),
type: "text/javascript",
exec: script,
},
...additionalDefinition,
})
);
}
function generateResponseDataAssertionScript(responseAssertion: StepResponseAssertion): string {
let ret = "";
const addBodyAssertion = (segments: string[], exp: string) => {
ret += `pm.expect(_.get(pm.response.json(), ${JSON.stringify(segments)})).${exp};\n`;
};
const addHeaderAssertion = (key: string, exp: string) => {
ret += `pm.expect(pm.response.headers.get("${key}")).${exp};\n`;
};
for (const [statusCode, v] of Object.entries(responseAssertion)) {
if (statusCode.includes("x")) {
ret += `if (pm.response.code.toString().startsWith("${statusCode.substring(0, 1)}")) {\n`;
} else {
ret += `if (pm.response.code === ${statusCode}) {\n`;
}
if (Array.isArray(v)) {
for (const assertion of v) {
const exp = assertion.value
? `to.deep.eql(${JSON.stringify(assertion.value)})`
: assertion.expression!;
const pathSegments = jsonPointer.parse(assertion.test);
const type = pathSegments.shift();
if (type === "body") {
addBodyAssertion(pathSegments, exp);
} else {
addHeaderAssertion(pathSegments[0], exp);
}
}
} else {
Object.entries(v.headers || {}).forEach(([k, v]) => {
addHeaderAssertion(k, `to.eql(${JSON.stringify(v)})`);
});
if (v.body) {
jsonPointer.walk(v.body, (value, path) => {
addBodyAssertion(jsonPointer.parse(path), `to.eql(${JSON.stringify(value)})`);
});
}
}
ret += "}\n";
}
return ret;
}
export function appendScripts(scripts: string[], parameter: TestScriptParameter) {
if (parameter.types.includes("DetailResponseLog")) {
scripts.push("console.log(pm.response.text());");
}
const assertions = [];
if (parameter.types.includes("StatusCodeAssertion")) {
if (parameter.responseAssertion === undefined) {
assertions.push("pm.response.to.be.success;");
} else {
const statusCodes = Object.keys(parameter.responseAssertion);
switch (statusCodes[0]) {
case "2xx":
assertions.push("pm.response.to.be.success;");
break;
case "4xx":
assertions.push("pm.response.to.be.clientError;");
break;
case "5xx":
assertions.push("pm.response.to.be.serverError;");
break;
default:
assertions.push(`pm.expect(pm.response.code).to.be.oneOf([${statusCodes.join(", ")}]);`);
}
}
}
if (parameter.types.includes("OverwriteVariables") && parameter.variables) {
for (const [k, v] of parameter.variables) {
const segments = parseJsonPointer(v);
if (segments.length === 0) {
assertions.push(`pm.environment.set("${k}", pm.response.json());`);
} else {
assertions.push(
`pm.environment.set("${k}", _.get(pm.response.json(), ${JSON.stringify(segments)}));`
);
}
}
}
if (parameter.types.includes("ARMDeploymentStatusAssertion")) {
assertions.push(
'pm.expect(pm.response.json().status).to.be.oneOf(["Succeeded", "Accepted", "Running", "Ready", "Creating", "Created", "Deleting", "Deleted", "Canceled", "Updating"]);'
);
}
if (parameter.types.includes("ExtractARMTemplateOutput") && parameter.armTemplate?.outputs) {
for (const key of Object.keys(parameter.armTemplate.outputs)) {
assertions.push(
`pm.environment.set("${key}", pm.response.json().properties.outputs.${key}.value);`
);
}
}
if (parameter.types.includes("ResponseDataAssertion") && parameter.responseAssertion) {
assertions.push(generateResponseDataAssertionScript(parameter.responseAssertion));
}
if (assertions.length > 0) {
scripts.push(`pm.test("${parameter.name}", function() {`);
assertions.forEach((s) => scripts.push(s));
scripts.push("});");
}
}
export const reservedCollectionVariables = [
{
key: "subscriptionId",
},
{
key: "resourceGroupName",
},
{
key: "location",
},
{
key: "client_id",
},
{
key: "client_secret",
type: "secret",
},
{
key: "tenantId",
},
{
key: "x_enable_auth",
value: "true",
},
];
export function generateAuthScript(scope: string, tokenName: string): string {
const script = `
if (pm.variables.get("x_enable_auth") !== "true") {
return;
}
if (
!pm.variables.get("${tokenName}") ||
Date.now() >
new Date(pm.variables.get("${tokenName}_expires_on") * 1000)
) {
let vars = ["client_id", "client_secret", "tenantId"];
vars.forEach(function (item, index, array) {
pm.expect(
pm.variables.get(item),
item + " variable not set"
).to.not.be.undefined;
pm.expect(pm.variables.get(item), item + " variable not set").to.not.be.empty;
});
pm.sendRequest(
{
url:
"https://login.microsoftonline.com/" +
pm.variables.get("tenantId") +
"/oauth2/v2.0/token",
method: "POST",
header: "Content-Type: application/x-www-form-urlencoded",
body: {
mode: "urlencoded",
urlencoded: [
{ key: "grant_type", value: "client_credentials", disabled: false },
{
key: "client_id",
value: pm.variables.get("client_id"),
disabled: false,
},
{
key: "client_secret",
value: pm.variables.get("client_secret"),
disabled: false,
},
{ key: "scope", value: "${scope}", disabled: false },
],
},
},
function (err, res) {
if (err) {
console.log(err);
} else {
let resJson = res.json();
pm.environment.set(
"${tokenName}_expires_on",
resJson.expires_in + Math.floor(Date.now() / 1000)
);
pm.environment.set("${tokenName}", resJson.access_token);
}
}
);
}`;
return script;
}