Handle failed requests and reduce duplication

This commit is contained in:
Thomas Pyle 2023-09-28 20:42:48 -04:00
parent 516411b9a2
commit 9bcf56d689
3 changed files with 335 additions and 259 deletions

View File

@ -1,8 +1,11 @@
{ {
"summary": { "summary": {
"totalRequests": 10,
"passedRequests": 10,
"failedRequests": 0,
"totalAssertions": 4, "totalAssertions": 4,
"passedAssertions": 4, "passedAssertions": 0,
"failedAssertions": 0, "failedAssertions": 4,
"totalTests": 0, "totalTests": 0,
"passedTests": 0, "passedTests": 0,
"failedTests": 0 "failedTests": 0
@ -11,53 +14,33 @@
{ {
"request": { "request": {
"method": "GET", "method": "GET",
"url": "http://localhost:8080/test/v4", "url": "http://localhost:3000/test/v4",
"headers": {} "headers": {}
}, },
"response": { "response": {
"status": 200, "status": 404,
"statusText": "OK", "statusText": "Not Found",
"headers": { "headers": {
"x-powered-by": "Express", "x-powered-by": "Express",
"content-type": "application/json; charset=utf-8", "content-security-policy": "default-src 'none'",
"content-length": "497", "x-content-type-options": "nosniff",
"etag": "W/\"1f1-08gGpUcq2NTnMCVT5AuXxQ0DzGE\"", "content-type": "text/html; charset=utf-8",
"date": "Mon, 25 Sep 2023 21:43:02 GMT", "content-length": "146",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close" "connection": "close"
}, },
"data": { "data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /test/v4</pre>\n</body>\n</html>\n"
"path": "/test/v4",
"headers": {
"accept": "application/json, text/plain, */*",
"user-agent": "axios/1.5.0",
"accept-encoding": "gzip, compress, deflate, br",
"host": "localhost:8080",
"connection": "close"
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "localhost",
"ip": "",
"ips": [],
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "05512cb2102c"
},
"connection": {}
}
}, },
"error": null,
"assertionResults": [ "assertionResults": [
{ {
"uid": "mTrKBl5YU6jiAVG-phKT4", "uid": "oidgfXLiyD8Jv0NBAHUHF",
"lhsExpr": "res.status", "lhsExpr": "res.status",
"rhsExpr": "200", "rhsExpr": "200",
"rhsOperand": "200", "rhsOperand": "200",
"operator": "eq", "operator": "eq",
"status": "pass" "status": "fail",
"error": "expected 404 to equal 200"
} }
], ],
"testResults": [] "testResults": []
@ -65,53 +48,33 @@
{ {
"request": { "request": {
"method": "GET", "method": "GET",
"url": "http://localhost:8080/test/v2", "url": "http://localhost:3000/test/v2",
"headers": {} "headers": {}
}, },
"response": { "response": {
"status": 200, "status": 404,
"statusText": "OK", "statusText": "Not Found",
"headers": { "headers": {
"x-powered-by": "Express", "x-powered-by": "Express",
"content-type": "application/json; charset=utf-8", "content-security-policy": "default-src 'none'",
"content-length": "497", "x-content-type-options": "nosniff",
"etag": "W/\"1f1-lMqxZgVOJiQXjF5yk3AFEU8O9Ro\"", "content-type": "text/html; charset=utf-8",
"date": "Mon, 25 Sep 2023 21:43:02 GMT", "content-length": "146",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close" "connection": "close"
}, },
"data": { "data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /test/v2</pre>\n</body>\n</html>\n"
"path": "/test/v2",
"headers": {
"accept": "application/json, text/plain, */*",
"user-agent": "axios/1.5.0",
"accept-encoding": "gzip, compress, deflate, br",
"host": "localhost:8080",
"connection": "close"
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "localhost",
"ip": "",
"ips": [],
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "05512cb2102c"
},
"connection": {}
}
}, },
"error": null,
"assertionResults": [ "assertionResults": [
{ {
"uid": "XsjjGx9cjt5t8tE_t69ZB", "uid": "IgliYuHd9wKp6JNyqyHFK",
"lhsExpr": "res.status", "lhsExpr": "res.status",
"rhsExpr": "200", "rhsExpr": "200",
"rhsOperand": "200", "rhsOperand": "200",
"operator": "eq", "operator": "eq",
"status": "pass" "status": "fail",
"error": "expected 404 to equal 200"
} }
], ],
"testResults": [] "testResults": []
@ -119,53 +82,33 @@
{ {
"request": { "request": {
"method": "GET", "method": "GET",
"url": "http://localhost:8080/test/v3", "url": "http://localhost:3000/test/v3",
"headers": {} "headers": {}
}, },
"response": { "response": {
"status": 200, "status": 404,
"statusText": "OK", "statusText": "Not Found",
"headers": { "headers": {
"x-powered-by": "Express", "x-powered-by": "Express",
"content-type": "application/json; charset=utf-8", "content-security-policy": "default-src 'none'",
"content-length": "497", "x-content-type-options": "nosniff",
"etag": "W/\"1f1-tSiYu0/vWz3r+NYRCaed0aW1waw\"", "content-type": "text/html; charset=utf-8",
"date": "Mon, 25 Sep 2023 21:43:02 GMT", "content-length": "146",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close" "connection": "close"
}, },
"data": { "data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /test/v3</pre>\n</body>\n</html>\n"
"path": "/test/v3",
"headers": {
"accept": "application/json, text/plain, */*",
"user-agent": "axios/1.5.0",
"accept-encoding": "gzip, compress, deflate, br",
"host": "localhost:8080",
"connection": "close"
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "localhost",
"ip": "",
"ips": [],
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "05512cb2102c"
},
"connection": {}
}
}, },
"error": null,
"assertionResults": [ "assertionResults": [
{ {
"uid": "i_8MmDMtJA9YfvB_FrW15", "uid": "u-3sRebrCyuUbZOkwS0z8",
"lhsExpr": "res.status", "lhsExpr": "res.status",
"rhsExpr": "200", "rhsExpr": "200",
"rhsOperand": "200", "rhsOperand": "200",
"operator": "eq", "operator": "eq",
"status": "pass" "status": "fail",
"error": "expected 404 to equal 200"
} }
], ],
"testResults": [] "testResults": []
@ -173,7 +116,7 @@
{ {
"request": { "request": {
"method": "POST", "method": "POST",
"url": "http://localhost:8080/test/v1", "url": "http://localhost:3000/test/v1",
"headers": { "headers": {
"content-type": "application/json" "content-type": "application/json"
}, },
@ -181,57 +124,201 @@
"test": "hello" "test": "hello"
} }
}, },
"response": {
"status": 404,
"statusText": "Not Found",
"headers": {
"x-powered-by": "Express",
"content-security-policy": "default-src 'none'",
"x-content-type-options": "nosniff",
"content-type": "text/html; charset=utf-8",
"content-length": "147",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close"
},
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /test/v1</pre>\n</body>\n</html>\n"
},
"error": null,
"assertionResults": [
{
"uid": "PpKLK6I38I5_ibw4lZqLb",
"lhsExpr": "res.status",
"rhsExpr": "eq 200",
"rhsOperand": "200",
"operator": "eq",
"status": "fail",
"error": "expected 404 to equal 200"
}
],
"testResults": []
},
{
"request": {
"method": "POST",
"url": "http://localhost:3000/test",
"headers": {}
},
"response": {
"status": 404,
"statusText": "Not Found",
"headers": {
"x-powered-by": "Express",
"content-security-policy": "default-src 'none'",
"x-content-type-options": "nosniff",
"content-type": "text/html; charset=utf-8",
"content-length": "144",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close"
},
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /test</pre>\n</body>\n</html>\n"
},
"error": null,
"assertionResults": [],
"testResults": []
},
{
"request": {
"method": "HEAD",
"url": "http://localhost:3000/",
"headers": {}
},
"response": { "response": {
"status": 200, "status": 200,
"statusText": "OK", "statusText": "OK",
"headers": { "headers": {
"x-powered-by": "Express", "x-powered-by": "Express",
"content-type": "application/json; charset=utf-8", "content-type": "text/html; charset=utf-8",
"content-length": "623", "content-length": "12",
"etag": "W/\"26f-ku5QGz4p9f02u79vJIve7JH3QYM\"", "etag": "W/\"c-Lve95gjOVATpfV8EL5X4nxwjKHE\"",
"date": "Mon, 25 Sep 2023 21:43:02 GMT", "date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close" "connection": "close"
}, },
"data": ""
},
"error": null,
"assertionResults": [],
"testResults": []
},
{
"request": {
"method": "POST",
"url": "http://localhost:3000",
"headers": {}
},
"response": {
"status": 404,
"statusText": "Not Found",
"headers": {
"x-powered-by": "Express",
"content-security-policy": "default-src 'none'",
"x-content-type-options": "nosniff",
"content-type": "text/html; charset=utf-8",
"content-length": "140",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close"
},
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /</pre>\n</body>\n</html>\n"
},
"error": null,
"assertionResults": [],
"testResults": []
},
{
"request": {
"method": "POST",
"url": "http://localhost:3000/",
"headers": {
"content-type": "multipart/form-data; boundary=--------------------------897965859410704836065858"
},
"data": { "data": {
"path": "/test/v1", "_overheadLength": 103,
"headers": { "_valueLength": 3,
"accept": "application/json, text/plain, */*", "_valuesToMeasure": [],
"content-type": "application/json", "writable": false,
"user-agent": "axios/1.5.0", "readable": true,
"content-length": "16", "dataSize": 0,
"accept-encoding": "gzip, compress, deflate, br", "maxDataSize": 2097152,
"host": "localhost:8080", "pauseStreams": true,
"connection": "close" "_released": true,
}, "_streams": [],
"method": "POST", "_currentStream": null,
"body": "{\"test\":\"hello\"}", "_insideLoop": false,
"fresh": false, "_pendingNext": false,
"hostname": "localhost", "_boundary": "--------------------------897965859410704836065858",
"ip": "", "_events": {},
"ips": [], "_eventsCount": 3
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "05512cb2102c"
},
"connection": {},
"json": {
"test": "hello"
}
} }
}, },
"assertionResults": [ "response": {
{ "status": 404,
"uid": "hNBSF_GBdSTFHNiyCcOn9", "statusText": "Not Found",
"lhsExpr": "res.status", "headers": {
"rhsExpr": "200", "x-powered-by": "Express",
"rhsOperand": "200", "content-security-policy": "default-src 'none'",
"operator": "eq", "x-content-type-options": "nosniff",
"status": "pass" "content-type": "text/html; charset=utf-8",
} "content-length": "140",
], "date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close"
},
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /</pre>\n</body>\n</html>\n"
},
"error": null,
"assertionResults": [],
"testResults": []
},
{
"request": {
"method": "POST",
"url": "http://localhost:3000/",
"headers": {
"content-type": "application/x-www-form-urlencoded"
},
"data": "a=b&c=d"
},
"response": {
"status": 404,
"statusText": "Not Found",
"headers": {
"x-powered-by": "Express",
"content-security-policy": "default-src 'none'",
"x-content-type-options": "nosniff",
"content-type": "text/html; charset=utf-8",
"content-length": "140",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close"
},
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /</pre>\n</body>\n</html>\n"
},
"error": null,
"assertionResults": [],
"testResults": []
},
{
"request": {
"method": "POST",
"url": "http://localhost:3000/test",
"headers": {
"content-type": "text/xml"
},
"data": "<xml>\n <test>1</test>\n</xml>"
},
"response": {
"status": 404,
"statusText": "Not Found",
"headers": {
"x-powered-by": "Express",
"content-security-policy": "default-src 'none'",
"x-content-type-options": "nosniff",
"content-type": "text/html; charset=utf-8",
"content-length": "144",
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
"connection": "close"
},
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /test</pre>\n</body>\n</html>\n"
},
"error": null,
"assertionResults": [],
"testResults": [] "testResults": []
} }
] ]

View File

@ -12,17 +12,56 @@ const { dotenvToJson } = require('@usebruno/lang');
const command = 'run [filename]'; const command = 'run [filename]';
const desc = 'Run a request'; const desc = 'Run a request';
const printRunSummary = (assertionResults, testResults) => { const printRunSummary = (results) => {
// display assertion results and test results summary let totalRequests = 0;
const totalAssertions = assertionResults.length; let passedRequests = 0;
const passedAssertions = assertionResults.filter((result) => result.status === 'pass').length; let failedRequests = 0;
const failedAssertions = totalAssertions - passedAssertions; let totalAssertions = 0;
let passedAssertions = 0;
let failedAssertions = 0;
let totalTests = 0;
let passedTests = 0;
let failedTests = 0;
for (const result of results) {
totalRequests += 1;
totalTests += result.testResults.length;
totalAssertions += result.assertionResults.length;
let anyFailed = false;
let hasAnyTestsOrAssertions = false;
for (const testResult of result.testResults) {
hasAnyTestsOrAssertions = true;
if (testResult.status === 'pass') {
passedTests += 1;
} else {
anyFailed = true;
failedTests += 1;
}
}
for (const assertionResult of result.assertionResults) {
hasAnyTestsOrAssertions = true;
if (assertionResult.status === 'pass') {
passedAssertions += 1;
} else {
anyFailed = true;
failedAssertions += 1;
}
}
if (!hasAnyTestsOrAssertions && result.error) {
failedRequests += 1;
} else {
passedRequests += 1;
}
}
const totalTests = testResults.length;
const passedTests = testResults.filter((result) => result.status === 'pass').length;
const failedTests = totalTests - passedTests;
const maxLength = 12; const maxLength = 12;
let requestSummary = `${rpad('Requests:', maxLength)} ${chalk.green(`${passedRequests} passed`)}`;
if (failedRequests > 0) {
requestSummary += `, ${chalk.red(`${failedRequests} failed`)}`;
}
requestSummary += `, ${totalRequests} total`;
let assertSummary = `${rpad('Tests:', maxLength)} ${chalk.green(`${passedTests} passed`)}`; let assertSummary = `${rpad('Tests:', maxLength)} ${chalk.green(`${passedTests} passed`)}`;
if (failedTests > 0) { if (failedTests > 0) {
assertSummary += `, ${chalk.red(`${failedTests} failed`)}`; assertSummary += `, ${chalk.red(`${failedTests} failed`)}`;
@ -35,10 +74,14 @@ const printRunSummary = (assertionResults, testResults) => {
} }
testSummary += `, ${totalAssertions} total`; testSummary += `, ${totalAssertions} total`;
console.log('\n' + chalk.bold(assertSummary)); console.log('\n' + chalk.bold(requestSummary));
console.log(chalk.bold(assertSummary));
console.log(chalk.bold(testSummary)); console.log(chalk.bold(testSummary));
return { return {
totalRequests,
passedRequests,
failedRequests,
totalAssertions, totalAssertions,
passedAssertions, passedAssertions,
failedAssertions, failedAssertions,
@ -255,9 +298,7 @@ const handler = async function (argv) {
} }
const _isFile = await isFile(filename); const _isFile = await isFile(filename);
let assertionResults = []; let results = [];
let testResults = [];
let testrunResults = [];
let bruJsons = []; let bruJsons = [];
@ -311,16 +352,10 @@ const handler = async function (argv) {
brunoConfig brunoConfig
); );
if (result) { results.push(result);
testrunResults.push(result);
const { assertionResults: _assertionResults, testResults: _testResults } = result;
assertionResults = assertionResults.concat(_assertionResults);
testResults = testResults.concat(_testResults);
}
} }
const summary = printRunSummary(assertionResults, testResults); const summary = printRunSummary(results);
console.log(chalk.dim(chalk.grey('Ran all requests.'))); console.log(chalk.dim(chalk.grey('Ran all requests.')));
if (outputPath && outputPath.length) { if (outputPath && outputPath.length) {
@ -333,14 +368,14 @@ const handler = async function (argv) {
const outputJson = { const outputJson = {
summary, summary,
results: testrunResults results
}; };
fs.writeFileSync(outputPath, JSON.stringify(outputJson, null, 2)); fs.writeFileSync(outputPath, JSON.stringify(outputJson, null, 2));
console.log(chalk.dim(chalk.grey(`Wrote results to ${outputPath}`))); console.log(chalk.dim(chalk.grey(`Wrote results to ${outputPath}`)));
} }
if (summary.failedAssertions > 0 || summary.failedTests > 0) { if (summary.failedAssertions + summary.failedTests + summary.failedRequests > 0) {
process.exit(1); process.exit(1);
} }
} catch (err) { } catch (err) {

View File

@ -20,9 +20,9 @@ const runSingleRequest = async function (
processEnvVars, processEnvVars,
brunoConfig brunoConfig
) { ) {
let request;
try { try {
let request;
request = prepareRequest(bruJson.request); request = prepareRequest(bruJson.request);
// make axios work in node using form data // make axios work in node using form data
@ -122,8 +122,34 @@ const runSingleRequest = async function (
request.data = qs.stringify(request.data); request.data = qs.stringify(request.data);
} }
// run request let response;
const response = await axios(request); try {
// run request
response = await axios(request);
} catch (err) {
if (err && err.response) {
response = err.response;
} else {
console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`));
return {
request: {
method: request.method,
url: request.url,
headers: request.headers,
data: request.data
},
response: {
status: null,
statusText: null,
headers: null,
data: null
},
error: err.message,
assertionResults: [],
testResults: []
};
}
}
console.log(chalk.green(stripExtension(filename)) + chalk.dim(` (${response.status} ${response.statusText})`)); console.log(chalk.green(stripExtension(filename)) + chalk.dim(` (${response.status} ${response.statusText})`));
@ -223,100 +249,28 @@ const runSingleRequest = async function (
headers: response.headers, headers: response.headers,
data: response.data data: response.data
}, },
error: null,
assertionResults, assertionResults,
testResults testResults
}; };
} catch (err) { } catch (err) {
if (err && err.response) { return {
console.log( request: {
chalk.green(stripExtension(filename)) + chalk.dim(` (${err.response.status} ${err.response.statusText})`) method: null,
); url: null,
headers: null,
// run post-response vars data: null
const postResponseVars = get(bruJson, 'request.vars.res'); },
if (postResponseVars && postResponseVars.length) { response: {
const varsRuntime = new VarsRuntime(); status: null,
varsRuntime.runPostResponseVars( statusText: null,
postResponseVars, headers: null,
request, data: null
err.response, },
envVariables, error: err.message,
collectionVariables, assertionResults: [],
collectionPath, testResults: []
processEnvVars };
);
}
// run post response script
const responseScriptFile = get(bruJson, 'request.script.res');
if (responseScriptFile && responseScriptFile.length) {
const scriptRuntime = new ScriptRuntime();
await scriptRuntime.runResponseScript(
responseScriptFile,
request,
err.response,
envVariables,
collectionVariables,
collectionPath,
null,
processEnvVars
);
}
// run assertions
let assertionResults = [];
const assertions = get(bruJson, 'request.assertions');
if (assertions && assertions.length) {
const assertRuntime = new AssertRuntime();
assertionResults = assertRuntime.runAssertions(
assertions,
request,
err.response,
envVariables,
collectionVariables,
collectionPath
);
each(assertionResults, (r) => {
if (r.status === 'pass') {
console.log(chalk.green(``) + chalk.dim(`assert: ${r.lhsExpr}: ${r.rhsExpr}`));
} else {
console.log(chalk.red(``) + chalk.red(`assert: ${r.lhsExpr}: ${r.rhsExpr}`));
console.log(chalk.red(` ${r.error}`));
}
});
}
// run tests
let testResults = [];
const testFile = get(bruJson, 'request.tests');
if (testFile && testFile.length) {
const testRuntime = new TestRuntime();
const result = testRuntime.runTests(
testFile,
request,
err.response,
envVariables,
collectionVariables,
collectionPath,
null,
processEnvVars
);
testResults = get(result, 'results', []);
}
if (testResults && testResults.length) {
each(testResults, (testResult) => {
if (testResult.status === 'pass') {
console.log(chalk.green(``) + chalk.dim(testResult.description));
} else {
console.log(chalk.red(``) + chalk.red(testResult.description));
}
});
}
} else {
console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`));
}
} }
}; };