UNPKG

rapnc

Version:

Node client for RAP 1.0

271 lines (242 loc) 8.77 kB
/* 校验接口的正确性 编写json是一种开发测试用例的方式,另一种方式是在rap的图形界面上编辑 这种方式只能模拟一些数据,不能测试真实数据的合法性,真实数据还是要靠在插件里加check来完成 向自己机器上发起脚本自动化测试,对RAP服务器来说毫无压力!赞! - 要求必须是offline启动,才允许脚本自动测试 - cases都可以是数组或对象 - method为POST时body作为POST body,method为GET时body作为url params - 需要能够指定测试用例的目录 */ var async = require('async'); var querystring = require('querystring'); var urlmodule = require('url'); var fs = require('fs'); var path = require('path'); var program = require('commander'); var helper = require('../helpers'); var logger = helper.logger; var isObject = helper.types.isObject; var ajax = helper.ajax; var toQueryString = helper.toQueryString; var appendToUrl = helper.appendToUrl; var config = require('../config.json'); var CONSTS = require('../constants'); var SEPERATOR = CONSTS.SEPERATOR; var REAL_DATA_KEY = CONSTS.REAL_DATA_KEY; var checkHost = config.pull_host || CONSTS.PULL_HOST; var checkPort = config.pull_port || CONSTS.PULL_PORT; var URL_PREFIX = config.urlprefix || CONSTS.URL_PREFIX; function sendRealDataToRAP(projectId, url, testcase, real, callback) { url = URL_PREFIX + '/check/' + projectId + '/' + url.replace(/^\//g, ''); url = appendToUrl(url, CONSTS.URL_SEPERATOR); var condition = testcase.condition || {}; if (condition.params) { url = appendToUrl(url, condition.params); } if (condition.body) { url = appendToUrl(url, querystring.stringify(condition.body)); } url = appendToUrl(url, REAL_DATA_KEY + '=' + JSON.stringify(real)); // 使用post方法,real作为post的data发送 // querystring.stringify 处理嵌套的object时不是很给力,暂用get ajax({ host: checkHost, port: checkPort, method: 'get', path: url, agent: false }, function(err, data) { if (err) { callback('HOST: ' + checkHost + ', PORT: ' + checkPort + ', MSG: ' + err); return; } data = data.trim(); callback(null, data); }); } function getDataFromDataHost(dataHost, originUrl, condition, callback) { var result = urlmodule.parse(dataHost); var options = { host: result.hostname, port: result.port || 80, path: appendToUrl(originUrl, condition.params), body: condition.body, method: condition.method } ajax(options, callback) } function checkDataByRAP(projectId, url, testcase, callback) { logger.log('[RAPNC check] checking [PROJECT: ' + projectId + ', ACTION: ' + url + ', CASE: ' + testcase.index + ']') var originUrl = url; var real = testcase.realData; if (real === undefined) { var dataHost = testcase.dataHost; if (!dataHost) { callback('no real data defined in testcase. PROJECT: ' + projectId + ', ACTION: ' + originUrl + ', CASE: ' + testcase.index); return; } getDataFromDataHost(dataHost, originUrl, testcase.condition, function(err, real) { if (err) { callback('get real data from host "' + dataHost + '" error: ' + err); return; } sendRealDataToRAP(projectId, url, testcase, JSON.parse(real), callback) }); } else { sendRealDataToRAP(projectId, url, testcase, real, callback) } } function key(projectId, url, index) { var result = projectId + SEPERATOR + url; if (index !== undefined) { result += SEPERATOR + index; } return result; } function dekey(str) { var result = {}; var splited = str.split(SEPERATOR); if (splited.length >= 1) { result.projectId = splited[0]; } if (splited.length >= 2) { result.url = splited[1]; } if (splited.length >= 3) { result.index = splited[2]; } return result; } function fillAsyncTask(tasks, projectId, json, cache) { var url = json.url; var cases = json.cases; if (!cases) { return; } if (isObject(cases)) { cases = [cases]; } var index = 0; cases.forEach(function(testcase) { index++; var keygened = key(projectId, url, index); testcase.index = index; tasks[keygened] = function(next) { checkDataByRAP(projectId, url, testcase, next); } cache[keygened] = testcase; }) } function handle(argv) { program .usage('check PROJECT_ID|all [options]') .option('-d, --test-dir [testDir]', 'testcases dir, relative to current dir') .option('-c, --current', 'current dir as testcases dir') .option('--host [host]', 'host of Check Service Server') .option('--port [port]', 'port of Check Service Server') .parse(argv); if (argv.length < 4) { program.help(); process.exit(); } if (argv.indexOf('-h') != -1 || argv.indexOf('--help') != -1) { program.help(); process.exit(0); } var projectId = argv[3]; if (projectId != 'all' && !/^\d+$/.test(projectId)) { logger.error('[RAPNC check] PROJECT_ID should be integer or "all"', 'check'); return; } if (projectId == 'all') { logger.warn('o... all is not supported yet.', 'check') program.help(); return; } checkHost = program.host || checkHost; checkPort = program.port || checkPort; var cacheDir; if (program.current) { cacheDir = path.join(process.cwd(), '.').replace(/\/$/g, '') + '/' + projectId; } else if (program.testDir) { cacheDir = path.join(process.cwd(), program.testDir).replace(/\/$/g, '') + '/' + projectId; } else { cacheDir = path.join(__dirname, '../cache/tests/') + projectId; } var displayDir = cacheDir + '/'; if (!fs.existsSync(cacheDir)) { logger.error('[RAPNC check] Testcase dir: ' + cacheDir + ' does not exist'); return; } logger.log('') logger.ok('[RAPNC check] Test dir: ' + cacheDir); logger.log('') var files = fs.readdirSync(cacheDir); var tasks = {}; var cache = {}; files.forEach(function(f) { var json; try { json = require(path.relative(__dirname, path.join(cacheDir, f))); } catch (e) { logger.log('') logger.error('[RAPNC check] can\'t require ' + displayDir + f + ' as a Nodejs module'); logger.log('') return; } fillAsyncTask(tasks, projectId, json, cache); }); async.auto(tasks, function(err, results) { if (err) { logger.log('') logger.error('[RAPNC check] ' + err); logger.log('') return; } var checkFlag = true; var errorMsg = ''; for(var prop in results) { var props = dekey(prop); var projectId = props.projectId, url = props.url, index = props.index; var cached = cache[prop].expected || {}; var returned = JSON.parse(results[prop]); if (returned._RAPNC_STATUS_) { checkFlag = false; logger.log(); logger.error('ACTION: ' + url + ', CASE: ' + index + '\n\tERROR: ' + returned.msg); continue; } for(var propCached in cached) { if (cached[propCached] != returned[propCached]) { checkFlag = false; errorMsg += '\n- ACTION ' + url + ' | CASE ' + index + ' | ERROR \n\t' + propCached + ' : [expected:' + cached[propCached] + ' --> actural:' + returned[propCached] + ']\n'; } } for(var propCached in returned) { if (!(propCached in cached)) { checkFlag = false; errorMsg += '\n- ACTION ' + url + ' | CASE ' + index + ' | ERROR(unexpected) \n\t' + propCached + ': [' + returned[propCached] + ']\n'; } } } if (errorMsg) { logger.raw('\nERROR of [PROJECT_ID: ' + projectId + ']\n' + errorMsg); } if (!checkFlag) { logger.log(''); logger.error('[RAPNC check] something wrong.'); logger.log(''); } else { logger.log(''); logger.ok('[RAPNC check] nothing wrong, nice job!'); logger.log(''); } }) } exports.handle = handle;