anyproxy/test/util/CommonUtil.js
2018-01-21 19:43:02 +08:00

270 lines
6.9 KiB
JavaScript

/**
*
* The utility class for test
*/
const color = require('colorful');
function _isDeepEqual(source, target) {
// if the objects are Array
if (source.constructor === Array && target.constructor === Array) {
if (source.length !== target.length) {
return false;
}
let _isEqual = true;
for (let i = 0; i < source.length; i++) {
if (!_isDeepEqual(source[i], target[i])) {
_isEqual = false;
break;
}
}
return _isEqual;
}
// if the source and target are just object
if (typeof source === 'object' && typeof target === 'object') {
let _isEqual = true;
for (const key in source) {
if (!_isDeepEqual(source[key], target[key])) {
_isEqual = false;
break;
}
}
return _isEqual;
}
return source === target;
}
/*
* Compare whether tow object are equal
*/
function isObjectEqual(source = {}, target = {}, url = '') {
source = Object.assign({}, source);
target = Object.assign({}, target);
let isEqual = true;
for (const key in source) {
isEqual = isEqual && _isDeepEqual(source[key], target[key]);
if (!isEqual) {
console.info('source object :', source);
console.info('target object :', target);
printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}",
target is "${target[key]}" the url is ${url}`);
break;
}
delete source[key];
delete target[key];
}
for (const key in target) {
isEqual = isEqual && source[key] === target[key];
if (!isEqual) {
console.info('source object :', source);
console.info('target object :', target);
printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}",
target is "${target[key]}" the url is ${url}`);
break;
}
delete source[key];
delete target[key];
}
return isEqual;
}
/*
* Compare the header between direct with proxy
* Will exclude the header(s) which modified by proxy
*/
function isCommonResHeaderEqual(directHeaders, proxyHeaders, requestUrl) {
directHeaders = Object.assign({}, directHeaders);
proxyHeaders = Object.assign({}, proxyHeaders);
let isEqual = true;
const mustEqualFileds = []; // the fileds that have to be equal, or the assert will be failed
if (!/gzip/i.test(directHeaders['content-encoding'])) {
// if the content is gzipped, proxy will unzip and remove the header
mustEqualFileds.push('content-encoding');
}
mustEqualFileds.push('content-type');
mustEqualFileds.push('cache-control');
mustEqualFileds.push('allow');
// ensure the required fileds are same
mustEqualFileds.forEach(filedName => {
isEqual = directHeaders[filedName] === proxyHeaders[filedName];
delete directHeaders[filedName];
delete proxyHeaders[filedName];
});
// remained filed are good to be same, but are allowed to be different
// will warn out those different fileds
for (const key in directHeaders) {
if (!_isDeepEqual(directHeaders[key], proxyHeaders[key])) {
printWarn(`key "${key}" of two response headers are different in request "${requestUrl}" :
direct is: "${directHeaders[key]}", proxy is: "${proxyHeaders[key]}"`);
}
}
return isEqual;
}
/*
* Compare the request between direct with proxy
*
*/
function isCommonReqEqual(url, serverInstance) {
try {
let isEqual = true;
const directReqObj = serverInstance.getRequestRecord(url);
const proxyReqObj = serverInstance.getProxyRequestRecord(url);
// ensure the proxy header is correct
isEqual = isEqual && proxyReqObj.headers['via-proxy'] === 'true';
delete proxyReqObj.headers['via-proxy'];
directReqObj.headers['content-type'] = trimFormContentType(directReqObj.headers['content-type']);
proxyReqObj.headers['content-type'] = trimFormContentType(proxyReqObj.headers['content-type']);
// avoid compare content-length header via proxy
delete directReqObj.headers['content-length'];
delete proxyReqObj.headers['content-length'];
delete directReqObj.headers['transfer-encoding'];
delete proxyReqObj.headers['transfer-encoding'];
isEqual = isEqual && directReqObj.url === proxyReqObj.url;
isEqual = isEqual && isObjectEqual(directReqObj.headers, proxyReqObj.headers, url);
isEqual = isEqual && directReqObj.body === proxyReqObj.body;
return isEqual;
} catch (e) {
console.error(e);
}
}
/*
* for multipart-form, the boundary will be different with each update, we trim it here
*/
function trimFormContentType(contentType = '') {
return contentType.replace(/boundary.*/, '');
}
function printLog(content) {
console.log(color.blue('==LOG==: ' + content));
}
function printWarn(content) {
console.log(color.magenta('==WARN==: ' + content));
}
function printError(content) {
console.log(color.red('==ERROR==: ' + content));
}
function printHilite(content) {
console.log(color.yellow('==LOG==: ' + content));
}
function parseUrlQuery(string = '') {
const parameterArray = string.split('&');
const parsedObj = {};
parameterArray.forEach((parameter) => {
// 获取等号的位置
const indexOfEqual = parameter.indexOf('=');
const name = parameter.substr(0, indexOfEqual);
const value = parameter.substr(indexOfEqual + 1);
parsedObj[name] = value;
});
return parsedObj;
}
function stringSimilarity(a, b, precision = 2) {
let similarity = '0%';
let isCongruent = false;
if (a && b) {
const targetLen = Math.max(a.length, b.length);
targetLen > 1000 ?
similarity = simHasH(a, b) :
similarity = LevenshteinSimilarity(a, b);
isCongruent = similarity === 100;
similarity = similarity.toFixed(precision) + '%';
}
return {
isCongruent,
similarity
}
}
/**
* simhash similarity
*/
function simHasH(a, b) {
const simhash = require('node-simhash');
return (simhash.compare(a, b) * 100);
}
/**
* Levenshtein Distance
*/
function LevenshteinSimilarity(a, b) {
let cost;
const maxLen = Math.max(a.length, b.length);
const minOfThree = (numa, numb, numc) => {
if (numa > numb) {
return numb > numc ? numc : numb;
} else {
return numa > numc ? numc : numa;
}
}
if (a.length === 0) cost = b.length;
if (b.length === 0) cost = a.length;
if (a.length > b.length) {
const tmp = a;
a = b;
b = tmp;
}
const row = [];
for (let i = 0; i <= a.length; i++) {
row[i] = i;
}
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val;
if (b.charAt(i - 1) === a.charAt(j - 1)) {
val = row[j - 1];
} else {
val = minOfThree(row[j - 1] + 1, prev + 1, row[j] + 1);
}
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
cost = row[a.length];
return ((maxLen - cost) / maxLen * 100);
}
module.exports = {
isObjectEqual,
isCommonResHeaderEqual,
printLog,
printWarn,
printError,
printHilite,
isCommonReqEqual,
parseUrlQuery,
stringSimilarity,
isArrayEqual: _isDeepEqual
};