diff --git a/.gitignore b/.gitignore index 19b8ac3..0446ef0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ npm-debug.log* node_modules tests/testrepo +build/.docker package-lock.json .npm diff --git a/bin/allrounder b/bin/allrounder index 149d342..17ee71b 100755 --- a/bin/allrounder +++ b/bin/allrounder @@ -21,7 +21,7 @@ if (showHelp === undefined || showHelp) { console.log(' OR to make http(s) call: '+name+' [options] -e [method=GET] https://myrestcallurl?withquery=parametersifany [headers^2] [payload^2]'); console.log(''); console.log(' Options:'); - console.log(' -r, --read [value] : Read the configuration from a json file, rather than passing from command line.^1'); + console.log(' -c, --conf [value] : Read the configuration from a json file, rather than passing from command line.^1'); console.log(' -f, --file [value] : The json file which contains the set of tests (a test suite) you want to execute.^1'); console.log(' -j, --jsondir [value] : The directory which contains the set of json files each of which is a test suite.^1'); console.log(' -b, --bail : If present, it will exit upon the first failure and ignore the rest.'); @@ -33,7 +33,7 @@ if (showHelp === undefined || showHelp) { console.log(' -o, --timeout : The timeout value that will abort testcase, if not finished in miliseconds'); console.log(' -w, --whileinterval : Miliseconds to wait for betweeen while loop iterations.'); console.log(' -t, --type [value] : What kind of test case if type if not present in testcase or testsuite.'); - console.log(' Should be on of rest,unit,db,command.'); + console.log(' Should be one of rest,unit,db,command.'); console.log(' -u, --utility [value] : The javascript filepath that will have utility functions.^1'); console.log(' It will be used for replacing method variables in test cases.'); console.log(' -s, --srcdir [value] : The source directory^1, which will be used to map the unit test file to corresponding source file'); @@ -97,7 +97,9 @@ const resolveFile = function resolveFile(cb){ } } options.file = path.join(tmpdir, 'allroundertests', 'test.json'); + options.jsondir = path.join(tmpdir, 'allroundertests'); writeFileSync(options.file, JSON.stringify(resp, undefined, 2)); + if (Array.isArray(resp.tests)) resp.tests.forEach((tt,ind) => { tt.ARtestIndex = ind; }); options.fileArray = [[options.file, resp, { EVAL: eval }]]; cb(); }).catch(function(er){ diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 0000000..32040a3 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,6 @@ +FROM node:18-alpine +ARG VERSION +RUN npm install -g allrounder@${VERSION} --allow-root --unsafe-perm=true +USER nobody:nobody +WORKDIR /app +ENTRYPOINT ["allrounder"] diff --git a/build/it.sh b/build/it.sh new file mode 100644 index 0000000..aa01551 --- /dev/null +++ b/build/it.sh @@ -0,0 +1,7 @@ + +set -e +VER=`sed 's/.*"version": "\(.*\)".*/\1/;t;d' ../package.json` +docker build . --build-arg VERSION=${VER} -t codeofnode/allrounder:$VER +docker tag codeofnode/allrounder:$VER codeofnode/allrounder:latest +docker push codeofnode/allrounder:$VER +docker push codeofnode/allrounder:latest diff --git a/extractArgs.js b/extractArgs.js index f678d3b..2015fdb 100644 --- a/extractArgs.js +++ b/extractArgs.js @@ -109,6 +109,14 @@ function parseReq(splits) { } break; default: + if (splits[1].indexOf('caFile=') === 0) { + request.caFile = splits[1].substring(7); + splits.splice(1, 1); + } + if (splits[1].indexOf('http2=') === 0) { + request.http2Options = getObjectFromFileOrArgument(splits[1].substring(6)); + splits.splice(1, 1); + } if (splits[1].indexOf('http') === 0) { splits.splice(1, 0, 'GET'); } @@ -180,10 +188,10 @@ function parseArguments() { options.file = getStringValue(value, true); } break; - case '-r': - case '--read': + case '-c': + case '--conf': if (value) { - options.read = getStringValue(read, true); + options.confs = getStringValue(value, true); } break; case '-j': @@ -370,6 +378,7 @@ function getArp(fn) { } function getTests(fl) { + if (!fl.type && fl.require) fl.type = 'unit' return fl.tests || fl.testcases || fl.steps || fl.entries || fl.records; } @@ -384,12 +393,14 @@ function getNthActiveElement(ar, ind, retInd) { } } -function createNewStep(og, ae) { +function createNewStep(prefixedObject, og, ae) { if (typeof ae !== 'object' || ae === null || typeof og !== 'object' || og === null) return ae; - return Object.assign(ae, { + return Object.assign({}, ae, { neg: ae.neg === undefined ? og.neg : ae.neg, + summary: prefixedObject.summaryPrefix ? prefixedObject.summaryPrefix + ' => ' + (ae.summary || ae.import) : ae.summary, + require: ae.require || ae.import || prefixedObject.fixRequire, condition: ae.condition === undefined ? og.condition : (og.condition ? `(${og.condition}) && (${ae.condition})` : ae.condition), - }); + }) } function resolveJson (vars, replace, fa) { @@ -400,9 +411,12 @@ function resolveJson (vars, replace, fa) { for (let z = 0; z < ln; z++) { const steps = tests[z].steps; if (typeof tests[z].import === 'string' && !replace(tests[z].disabled, Object.assign(fl.vars || {}, vars), globalMethods)) { + const fixRequire = tests[z].require || (fl.type === 'unit' ? tests[z].import : undefined) + const summaryPrefix = tests[z].summary || tests[z].import if (!tests[z].import.endsWith('.json')) { tests[z].import += (options.srcdir ? ('.'+options.speckey) : '') + '.json'; } + const prefixedObject = { fixRequire, summaryPrefix } let arp = getArp(isAbsolute(tests[z].import) ? tests[z].import : join(dirname(fa[0]), tests[z].import)); let ar = JSON.parse(arp[3]); if (tests[z].fetchVars !== false) { @@ -427,7 +441,7 @@ function resolveJson (vars, replace, fa) { if (Array.isArray(steps)) { steps.forEach(st => { if (typeof st === 'number' && ar[st]) { - let toPush = createNewStep(tests[z], getNthActiveElement(ar, st)); + let toPush = createNewStep(prefixedObject, tests[z], getNthActiveElement(ar, st)); if (toPush) art.push(toPush); } }); @@ -436,17 +450,17 @@ function resolveJson (vars, replace, fa) { let ffrom = getNthActiveElement(ar, steps.from || 0, true); let fto = typeof steps.to !== 'number' ? ar.length - 1 : getNthActiveElement(ar, steps.to, true); for (let st = ffrom; ar[st] && st <= fto; st++) { - let toPush = createNewStep(tests[z], ar[st]); + let toPush = createNewStep(prefixedObject, tests[z], ar[st]); if (toPush) art.push(toPush); } } else if (typeof steps === 'number' && ar[steps]) { - toPush = createNewStep(tests[z], getNthActiveElement(ar, steps)); + const toPush = createNewStep(prefixedObject, tests[z], getNthActiveElement(ar, steps)); if (toPush) art.push(toPush); } ar = art; tests.splice.bind(tests, z, 1).apply(tests, ar); } else { - tests.splice.bind(tests, z, 1).apply(tests, ar.map(createNewStep.bind(null, tests[z]))); + tests.splice.bind(tests, z, 1).apply(tests, ar.map(createNewStep.bind(null, prefixedObject, tests[z]))); } if (preVars && tests[z]) tests[z].vars = Object.assign(preVars, tests[z].vars); ln += ar.length - 1; @@ -481,9 +495,7 @@ function afterResolve(fa) { */ exports.getOptions = function getOptions(options) { const OPTS = {}; - if (!options.read){ - const readFile = join(cwd, 'allrounder.json'); - } + const readFile = options.confs || join(cwd, 'allrounder.json'); let read = {}; try { read = require(readFile); diff --git a/index.js b/index.js index 65214ff..e1f5013 100644 --- a/index.js +++ b/index.js @@ -38,11 +38,11 @@ function ifConditionFailed(condition){ } } -function resolveWhile(inputwhile, OPTS, ts){ +function resolveWhile(inputwhile, OPTS, ts, vars, methods){ if (typeof inputwhile === 'string') { return { while : inputwhile, - interval: typeof ts.whileinterval === 'number' ? ts.whileinterval : OPTS.whileinterval + interval: ts.whileinterval !== undefined ? OPTS.replace(ts.whileinterval, vars, methods) : OPTS.replace(OPTS.whileinterval, vars, methods) }; } else if (typeof inputwhile === 'object' && inputwhile !== null && typeof inputwhile.while === 'string' && typeof inputwhile.interval === 'number') { @@ -129,7 +129,7 @@ exports.forTS = function forTS(fileData) { require(`./types/${test.type || fileData[1].type || OPTS.type}`) .call(that, OPTS, test, fileData, cb, OPTS.logger.bind(OPTS, that.test, getFinalDebug(currDebug, mainDebug, OPTS.debug), getFinalDebug(currDebugOnFail, mainDebugOnFail, OPTS.debugonfail))); } - const resolvedwhile = resolveWhile(test.while, OPTS, fileData[1]); + const resolvedwhile = resolveWhile(test.while, OPTS, fileData[1], vars, methods); if (resolvedwhile) { that.ARshouldClone = true; function loopingFunction(curIndex){ @@ -153,7 +153,7 @@ exports.forTS = function forTS(fileData) { tto = false; } if (sleep) { - that.timeout(sleep + (tto || OPTS.timeout || 2000)); + that.timeout(sleep + (tto || fileData[1].timeout || OPTS.timeout || 2000)); setTimeout(execNow, sleep); } else { if (tto) that.timeout(tto); @@ -216,7 +216,7 @@ exports.start = function start(){ } OPTS.fileArray.forEach((fileData) => { if (!fileData[1].disabled && (!OPTS.file || OPTS.file === fileData[0])) { - describe(fileData[1].testsuite || fileData[1].scenario || basename(fileData[0].split('.').shift()), function(){ + describe(fileData[1].testsuite || fileData[1].scenario || fileData[0].split(OPTS.jsondir)[1], function(){ exports.forTS.call(this, fileData); }); } diff --git a/mocha-test.js b/mocha-test.js index 2ac65bb..48ff1e9 100644 --- a/mocha-test.js +++ b/mocha-test.js @@ -4,6 +4,6 @@ const testFile = require('./index'); if (!options.jsondir && !options.file && !options.srcdir) { throw new Error('`jsondir` or `file` argument is a must.'); } -delete options.read; +delete options.confs; testFile.init(options,true); testFile.start(); diff --git a/package.json b/package.json index 4d2aa93..bbde6f7 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "allrounder", - "version": "0.7.0", + "version": "0.8.6", "description": "All in one validator for testing, under one roof. From rest call to dbvalidation to system commands to unit tests. Just almost everything", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -27,8 +27,8 @@ "license": "MIT", "homepage": "https://github.com/codeofnode/allrounder", "dependencies": { - "json2server": "latest", - "jsonpath": "^1.0.0", - "mocha": "^4.1.0" + "json2server": "^0.1.31", + "jsonpath": "^1.0.2", + "mocha": "^7.1.0" } } diff --git a/tests/testjson/src/another/index.spec.json b/tests/testjson/src/another/index.spec.json new file mode 100644 index 0000000..bcfc601 --- /dev/null +++ b/tests/testjson/src/another/index.spec.json @@ -0,0 +1,111 @@ +{ + "type": "unit", + "tests" : [ + { + "summary": "Requiring the file", + "request": { + "method": "default" + }, + "assertions": { + "error.message": "Cannot read property 'type' of undefined" + } + }, + { + "require": "immutable", + "summary": "Getting first immutable object", + "request": { + "method": "fromJS", + "params": { + "todos": { + } + } + }, + "assertions": { + "error": null + }, + "extractors": { + "state": "output" + } + }, + { + "summary": "Adding an entry", + "request": { + "method": "default", + "params": [ + "{{state}}", + { + "type": "ADD", + "todo": "todo 1" + } + ] + }, + "assertions": { + "error": null, + "output.size": 2, + "output._root.entries.0.1.size": 1 + }, + "extractors": { + "todo1Id": "output._root.entries.1.1" + } + }, + { + "summary": "Updating an entry", + "request": { + "method": "default", + "params": [ + "{{state}}", + { + "type": "EDIT", + "id": "{{todo1Id}}", + "patch": { + "todo": "todo 1" + } + } + ] + }, + "assertions": { + "error": null, + "output._root.entries.0.1._root.entries.0.1.todo": "todo 1" + } + }, + { + "summary": "Deleting an entry", + "request": { + "method": "default", + "params": [ + "{{state}}", + { + "type": "DELETE", + "id": "{{todo1Id}}" + } + ] + }, + "assertions": { + "error": null, + "output.size": 1, + "output._root.entries.0.1.size": 0 + } + }, + { + "import": "reducer", + "steps": 2 + }, + { + "summary": "Deleting all", + "request": { + "method": "default", + "params": [ + "{{state}}", + { + "type": "DELETE_ALL" + } + ] + }, + "assertions": { + "error": null, + "output.size": 1, + "output._root.entries.0.1.size": 0 + } + } + ] +} diff --git a/tests/testjson/src/reducer.spec.json b/tests/testjson/src/reducer.spec.json index fff338d..bcfc601 100644 --- a/tests/testjson/src/reducer.spec.json +++ b/tests/testjson/src/reducer.spec.json @@ -7,7 +7,7 @@ "method": "default" }, "assertions": { - "error": {} + "error.message": "Cannot read property 'type' of undefined" } }, { diff --git a/types/db/dbconnectors/mongodb.js b/types/db/dbconnectors/mongodb.js index cd991b4..2acdd41 100644 --- a/types/db/dbconnectors/mongodb.js +++ b/types/db/dbconnectors/mongodb.js @@ -62,7 +62,7 @@ module.exports = function mongodb(reqObj, dbConfig, next) { } const query = reqObj.payload; let col; - try { col = db.collection(query && query.collection); } catch(erm){ } + try { col = db.collection(query && query.collection); } catch(erm){ console.log(erm) } if (!col){ return next({error : "Collection not available", code : 'COL_NOT_AVL', status : 400 }); } @@ -119,6 +119,7 @@ module.exports = function mongodb(reqObj, dbConfig, next) { if (connectionMap.hasOwnProperty(dbConfig.connectionName)) { afterConnection(null, connectionMap[dbConfig.connectionName]); } else { + dbConfig.options = Object.assign({ useUnifiedTopology: true }, dbConfig.options || {}) MongoClient.connect(dbConfig.dbUrl, dbConfig.options, afterConnection); } }; diff --git a/types/unit/index.js b/types/unit/index.js index a3bdae4..a765dfe 100644 --- a/types/unit/index.js +++ b/types/unit/index.js @@ -62,7 +62,7 @@ module.exports = function forTC(OPTS, test, fileData, done, noti) { unit = require(path); } else { try { - unit = require(require.resolve(path, { paths : [OPTS.jsondir] })); + unit = require(join(OPTS.jsondir, path)); } catch (er) { try { unit = require(path); @@ -77,6 +77,7 @@ module.exports = function forTC(OPTS, test, fileData, done, noti) { } else { unit = global; } + if (typeof reqObj.method !== 'string') return callback({ error: null, output: unit }) const method = OPTS.jsonquery(unit, OPTS.replace(reqObj.method, vars, methods)); let payload = (reqObj.payload === undefined) ? [] : OPTS.replace(reqObj.payload, vars, methods); if (!Array.isArray(payload)) { payload = [payload]; } diff --git a/utils.js b/utils.js index e373ee2..97b2fb3 100644 --- a/utils.js +++ b/utils.js @@ -60,22 +60,22 @@ exports.cropString = function cropString(str, ln = 100) { exports.jsonquery = function jsonquery(data, path) { let res = data; - if (typeof data === 'object' && data !== null) { - if (path.indexOf('LEN()<') === 0) { - return jsonpath.query(data, path.substring(6)).length; - } else if(typeof path === 'string' && path.indexOf('TYPEOF<') === 0) { - return (typeof jsonpath.query(data, path.substring(7))[0]); - } else if (path.indexOf('ARRAY<') === 0) { - return jsonpath.query(data, path.substring(6)); - } else if (path.indexOf('<') === 5) { - const count = parseInt(path.substr(0, 5), 10); - if (!isNaN(count)) { - return jsonpath.query(data, path.substring(6), count); - } + if (path.indexOf('LEN()<') === 0) { + return jsonpath.query(data, path.substring(6)).length; + } else if(typeof path === 'string' && path.indexOf('TYPEOF<') === 0) { + return (typeof jsonpath.query(data, path.substring(7))[0]); + } else if (path.indexOf('ARRAY<') === 0) { + return jsonpath.query(data, path.substring(6)); + } else if (path.indexOf('<') === 5) { + const count = parseInt(path.substr(0, 5), 10); + if (!isNaN(count)) { + return jsonpath.query(data, path.substring(6), count); } + } + if (data instanceof Object && typeof path === 'string' && path) { res = jsonpath.query(data, path, 1); - res = (Array.isArray(res) && res.length < 2) ? res[0] : res; } + res = (Array.isArray(res) && res.length < 2) ? res[0] : res; return res; };