Authored by ccbikai

Merge branch 'feature/sale' into release/4.8

'use strict';
const mRoot = '../models';
const library = '../../../library';
const couponModel = require(`${mRoot}/coupon`); // 领取优惠券 model
const log = require(`${library}/logger`);
exports.index = (req, res, next) => {
var renderData = {
... ...
var API = require('../../../library/api').API;
var api = new API();
var api = global.yoho.API;
/**
* 分享页面基础参数
... ...
... ... @@ -8,8 +8,8 @@
const request = require('request-promise');
const Promise = require('bluebird');
const crypto = require('crypto');
const logger = require('../../../library/logger');
const cache = require('../../../library/cache');
const logger = global.yoho.logger;
const cache = global.yoho.cache;
// 此处请勿使用有货公众号的 appId, 此处使用的是 女生志 的appId
const appId = 'wxb52ec6a352f0b090';
... ...
... ... @@ -130,7 +130,9 @@ const _getChannelResource = (params) => {
params.new_device = true; // eslint-disable-line
}
return api.get('operations/api/v5/resource/home', params, true).then(result => {
return api.get('operations/api/v5/resource/home', params, {
cache: true
}).then(result => {
if (result && result.code === 200) {
return resourcesProcess(result.data.list);
} else {
... ... @@ -148,7 +150,9 @@ const _getChannelResource = (params) => {
const _getLeftNav = (choosed) => {
choosed = choosed || 'all';
return api.get('operations/api/v6/category/getCategory', {}, true).then(result => {
return api.get('operations/api/v6/category/getCategory', {}, {
cache: true
}).then(result => {
if (result && result.code === 200) {
return _processSideBar(result.data, choosed);
} else {
... ... @@ -163,7 +167,9 @@ const _getLeftNav = (choosed) => {
* @return {[type]}
*/
const _getChannelList = () => {
return api.get('operations/api/v5/entrance/getEntrance', {}, true).then((result) => {
return api.get('operations/api/v5/entrance/getEntrance', {}, {
cache: true
}).then((result) => {
if (result && result.code === 200) {
const list = {};
... ... @@ -198,7 +204,9 @@ const _getChannelList = () => {
const _getChannelBg = () => {
return api.get('operations/api/v5/resource/get', {
content_code: contentCode.index
}, true).then(result => {
}, {
cache: true
}).then(result => {
if (result && result.code === 200) {
return result.data.length && result.data[0] && result.data[0].data && result.data[0].data.list[0];
} else {
... ...
... ... @@ -71,14 +71,16 @@ let index = (req, res, next) => {
Object.assign(params.renderData.pageHeader, {
saleNav: saleModel.saleNav(req.yoho.channel)
});
// 此处 channel 需要读取 cookies 的 channel
req.cookies._Channel && (res.locals.pageChannel[req.cookies._Channel] = true);
res.render('sale/index', Object.assign(params.renderData, {
content: result,
floorHeader: {
title: {
title: '最新降价'
}
},
pageChannel: false // 需求中,头部不需要跟随频道变颜色
}
}));
}).catch(next);
};
... ...
... ... @@ -3,7 +3,7 @@
* @return {[type]}
*/
const headerModel = require('../models/header');
const logger = require('../../library/logger');
const logger = global.yoho.logger;
exports.notFound = () => {
return (req, res) => {
... ...
/**
* 接口公共方法
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/25
*/
'use strict';
const rp = require('request-promise');
const qs = require('querystring');
const md5 = require('md5');
const _ = require('lodash');
const log = require('./logger');
const cache = require('./cache');
const Timer = require('./timer');
const sign = require('./sign');
const config = require('../config/common');
const api = config.domains.api;
const serviceApi = config.domains.service;
const searchApi = config.domains.search;
// 错误返回
const API_BAD_RETSULT = {
code: 500,
message: 'API result is not JSON string or null.'
};
// 调用失败
const API_CALL_FAIL = {
code: 500,
message: 'Call API failed.'
};
// all 方法错误的传参
const API_ALL_METHOD_ERROR = 'the parameters of api all method should be Array!';
// 获取缓存数据失败
const SLAVE_CACHE_FAIL = 'get slave cache fail';
const MASTER_CACHE_FAIL = 'get master cache fail';
// 获取缓存数据成功
const SLAVE_CACHE_SUCCESS = 'get slave cache success';
const MASTER_CACHE_SUCCESS = 'get master cache success';
class Http {
constructor(baseUrl) {
this.ApiUrl = baseUrl;
}
/**
* 获取请求 ID
*/
_getReqId(options) {
return md5(`${options.url}?${qs.stringify(options.qs || options.form)}`);
}
/**
* 调用接口
*/
_requestFromAPI(options, cacheOption, reqId) {
const timer = new Timer();
const method = options.method || 'get';
timer.put('getApi');// 统计时间开始
return rp(options).then((result) => {
const duration = timer.put('getApi');// 统计时间结束
// 数据校验
if (!result) {
log.error(`error: ${API_BAD_RETSULT.message}`);
return Promise.reject(API_BAD_RETSULT);
}
// 写缓存, 否则返回 Slave 缓存服务器的数据
if (config.useCache && cacheOption) {
const cacheTime = _.isNumber(cacheOption) ? cacheOption : 60;
const catchErr = (err) => {
log.error(`cache: ${err.toString()}`);
};
reqId = reqId || this._getReqId(options);
cache.set(`apiCache:${reqId}`, result, cacheTime).catch(catchErr);
cache.setSlave(`apiCache:${reqId}`, result, 86400).catch(catchErr); // 二级缓存存储一天
}
let reqData = options.qs || options.form;
log.info(`use: ${duration}ms for ${method} api: ${options.url}?${qs.stringify(reqData)} `);
return result;
}).catch((err)=> {
const duration = timer.put('getApi');// 统计时间结束
log.error(`${method} api fail: use: ${duration}ms, code:${err.statusCode}, error: ${err.message}`);
log.error(`API: ${options.url}?${qs.stringify(options.qs)}`);
// 使用缓存的时候,读取二级缓存
if (config.useCache && cacheOption) {
return this._requestFromCache(options, true);
}
return Promise.resolve(API_CALL_FAIL);
});
}
/**
* 读取缓存
* @param {[object]} options
* @param {[boolean]} slave true: 读取二级缓存
* @return {[type]}
*/
_requestFromCache(options, slave) {
const reqId = this._getReqId(options);
const getCache = slave ? cache.getFromSlave : cache.get;
log.info(`get ${slave ? 'slave' : 'master'} cache: ${reqId}, url: ${options.url}?${qs.stringify(options.qs)}`);
return getCache(`apiCache:${reqId}`).then((result) => {
if (!_.isNil(result)) {
try {
result = JSON.parse(result);
} finally {
log.info(slave ? SLAVE_CACHE_SUCCESS : MASTER_CACHE_SUCCESS);
return result;
}
}
// 读取缓存失败,并且不是二级缓存的时候,调用 API
if (!slave) {
return this._requestFromAPI(options, true, reqId);
}
}).catch(() => {
log.error(slave ? SLAVE_CACHE_FAIL : MASTER_CACHE_FAIL);
// 读取缓存失败,并且不是二级缓存的时候,调用 API
if (!slave) {
return this._requestFromAPI(options, true, reqId);
}
return Promise.resolve(API_CALL_FAIL);
});
}
/**
* 使用 get 请求获取接口
* @param {[string]} url
* @param {[object]} data
* @param {[bool or number]} cacheOption 使用数字时,数字表示缓存时间
* @return {[type]}
*/
get(url, data, cacheOption) {
const options = {
url: `${this.ApiUrl}${url}`,
qs: data.client_secret ? data : sign.apiSign(data),
json: true,
gzip: true,
timeout: 3000
};
// 从缓存获取数据
if (config.useCache && cacheOption) {
return this._requestFromCache(options);
}
return this._requestFromAPI(options, cacheOption);
}
/**
* post
* @param url String
* @param data Obejct
*/
post(url, data) {
const options = {
url: `${this.ApiUrl}${url}`,
form: data.client_secret ? data : sign.apiSign(data),
method: 'post',
json: true,
timeout: 3000
};
return this._requestFromAPI(options);
}
all(list) {
if (_.isArray(list)) {
return Promise.all(list);
} else {
return Promise.reject(Error(API_ALL_METHOD_ERROR));
}
}
}
class API extends Http {
constructor() {
super(api);
}
}
class ServiceAPI extends Http {
constructor() {
super(serviceApi);
}
}
class SearchAPI extends Http {
constructor() {
super(searchApi);
}
}
exports.API = API;
exports.ServiceAPI = ServiceAPI;
exports.SearchAPI = SearchAPI;
/**
* 缓存封装
* 前期使用 memcache, 写方法的时候考虑一下如何转换为 redis
* @author bikai kai.bi@yoho.cn
* @date 2016/05/16
*/
'use strict';
const Promise = require('bluebird');
const Memcached = require('memcached');
const _ = require('lodash');
const config = require('../config/common');
let master = new Memcached(config.memcache.master, config.memcache);
let slave = new Memcached(config.memcache.slave, config.memcache);
master = Promise.promisifyAll(master);
slave = Promise.promisifyAll(slave);
/**
* 获取缓存
* @param {[string]} key 键
* @return {[type]}
*/
exports.get = (key) => {
if (_.isString(key)) {
return master.getAsync(key);
}
return Promise.resolve();
};
/**
* 批量获取缓存
* @param {[array]} list 字符串数组
* @return {[type]}
*/
exports.getMulti = (list) => {
if (_.isArray(list)) {
return master.getMultiAsync(list);
}
return Promise.resolve();
};
/**
* 获取缓存(从 Slave 服务器)
* @param {[string]} key 键
* @return {[type]}
*/
exports.getFromSlave = (key) => {
if (_.isString(key)) {
return slave.getAsync(key);
}
return Promise.resolve();
};
/**
* 批量获取缓存(从 Slave 服务器)
* @param {[array]} list 字符串数组
* @return {[type]}
*/
exports.getMultiFromSlave = (list) => {
if (_.isArray(list)) {
return slave.getMultiAsync(list);
}
return Promise.resolve();
};
/**
* 写缓存
* @param {[type]} key 键
* @param {[type]} value 值
* @param {[type]} lifetime 生命周期
* @return {[type]}
*/
exports.set = (key, value, lifetime) => {
lifetime = lifetime || 86400;
if (_.isObject(value)) {
value = JSON.stringify(value);
}
if (_.isString(key)) {
return master.setAsync(key, value, lifetime);
}
return Promise.resolve();
};
/**
* 写缓存(到 Slave 服务器)
* @param {[type]} key 键
* @param {[type]} value 值
* @param {[type]} lifetime 生命周期
* @return {[type]}
*/
exports.setSlave = (key, value, lifetime) => {
lifetime = lifetime || 86400;
if (_.isObject(value)) {
value = JSON.stringify(value);
}
if (_.isString(key)) {
return slave.setAsync(key, value, lifetime);
}
return Promise.resolve();
};
/**
* 删除缓存
* @param {[string]} key 键
* @return {[type]}
*/
exports.del = (key) => {
if (_.isString(key)) {
return master.delAsync(key);
}
return Promise.resolve();
};
/**
* 对象键名驼峰化
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const _ = require('lodash');
let camelCase,
camelCaseObject,
camelCaseArray;
camelCaseObject = (obj) => {
_.forEach(Object.keys(obj), (k) => {
obj[k] = camelCase(obj[k]);
if (/[_-]/.test(k)) {
obj[_.camelCase(k)] = obj[k];
delete obj[k];
}
});
return obj;
};
camelCaseArray = (list) => {
_.forEach(list, (k) => {
k = camelCase(k);
});
return list;
};
camelCase = (data) => {
if (_.isArray(data)) {
data = camelCaseArray(data);
} else if (_.isObject(data)) {
data = camelCaseObject(data);
}
return data;
};
module.exports = camelCase;
/**
* 获取 UID
* @param {[object]} req
* @return {[string]}
*/
'use strict';
const sign = require('./sign');
exports.getUid = (req) => {
const cookie = req.cookies._UID;
let _uid = 0;
let cookieList;
if (req.isApp) {
return req.query.uid || 0;
}
if (cookie) {
cookieList = cookie.split('::');
if (cookieList[1] && !isNaN(cookieList[1])) {
_uid = cookieList[1];
}
}
// 校验 cookie 的 uid 有没有被修改
if (req.cookies._TOKEN !== sign.makeToken(_uid)) {
_uid = 0;
}
return _uid;
};
exports.getShoppingKey = (req) => {
return req.cookies['_SPK'] ? req.cookies['_SPK'] : ''; // eslint-disable-line
};
/**
* Handlebars helpers
* bikai kai.bi@yoho.cn
* 2016-05-10
*/
'use strict';
const querystring = require('querystring');
const _ = require('lodash');
const moment = require('moment');
const config = require('../config/common');
/**
* 七牛图片路径处理
* @param {[string]} url
* @param {[string]} width
* @param {[string]} height
* @param {[string]} mode
* @return {[string]}
*/
exports.image = (url, width, height, mode) => {
mode = _.isNumber(mode) ? mode : 2;
url = url || '';
url = url.replace(/{width}/g, width).replace(/{height}/g, height).replace(/{mode}/g, mode);
return url.replace('http:', '');
};
/**
* 条件判断
* @param {[string]} v1
* @param {[string]} v2
* @param {[object]} options 上下文环境,一般不手动传
* @return {[boolen]}
*/
exports.isEqual = (v1, v2, _options) => {
if (_.isEqual(v1, v2)) {
return _options.fn(this); // eslint-disable-line
}
return _options.inverse(this); // eslint-disable-line
};
/**
* 站内地址格式化
* @param {[string]} uri 路径
* @param {[object]} qs 查询字符串
* @param {[string]} module 模块
* @return {[string]}
*/
exports.urlFormat = (uri, qs, module) => {
const subDomain = '.m.yohobuy.com';
const subName = {
default: config.siteUrl,
guang: `//guang${subDomain}`,
list: `//list${subDomain}`,
search: `//search${subDomain}`,
huodong: `//huodong${subDomain}`,
activity: '//activity.yohobuy.com',
index: config.siteUrl
};
let url;
module = module || 'default';
if (subName[module]) {
url = subName[module];
} else {
url = `//${module}${subDomain}`; // 规则没匹配到就把模块当作子域名
}
url += uri;
if (qs) {
url += `?${querystring.stringify(qs)}`;
}
return url;
};
/**
* 大写转小写处理
* @param {[string]} str 转换字符
*/
exports.lowerCase = (str) => {
str = str || '';
return str.toLowerCase();
};
/**
* 小写转大写处理
* @param {[string]} str 转换字符
*/
exports.upperCase = (str) => {
str = str || '';
return str.toUpperCase();
};
/**
* 四舍五入
* @param {[type]} num 数字
* @param {[type]} precision 精度
* @return {[type]}
*/
exports.round = (num, precision) => {
precision = _.isNumber(precision) ? precision : 2;
num = _.isInteger(num) ? (+num).toFixed(precision) : _.round(num, precision);
return num;
};
/**
* 链接改成自适应链接
* @return {[type]}
*/
exports.https = (url) => {
return url.replace('http:', '');
};
/**
* 时间格式化
* @param format 格式化token @see{http://momentjs.cn/docs/#/displaying/format/}
* @param date 日期或者数字
* @return string
*
*/
exports.dateFormat = (format, date) => {
if (typeof format !== 'string' || typeof date === 'undefined') {
return '';
} else {
if (date instanceof Date) {
return moment(date).format(format);
} else {
const d = moment.unix(date);
return moment(d).utc().format(format);
}
}
};
/**
* 时间差格式化
* @param {[string]} format 格式化字符串
* @param {[number]} diff 相差值
* @param {[string]} type diff时间类型 默认ms
*
* Key Shorthand
* years y
* quarters Q
* months M
* weeks w
* days d
* hours h
* minutes m
* seconds s
* milliseconds ms
*
* @example
* let diff = 60 * 60 * 24 * (1.3) + 2;
*
* let s = helpers.dateDiffFormat('{d}天{h}小时', diff, 's');
* >>> 1天7小时
*/
exports.dateDiffFormat = (format, diff, type) => {
if (typeof format !== 'string' || typeof diff === 'undefined') {
return '';
} else {
type = type || 'ms';
const m = moment.duration(diff, type);
format.match(/(\{.*?\})/g).forEach((s) => {
format = format.replace(s, m.get(s.substring(1, s.length - 1)));
});
return format;
}
};
/**
* 验证邮箱是否合法
*
* @param string email
* @return boolean
*/
exports.verifyEmail = (email) => {
if (!email) {
return false;
}
const emailRegExp = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
return emailRegExp.test(email);
};
/**
* 各国手机号规则
*/
function areaMobileVerify(phone, area) {
area = area || '86';
phone = phone.trim();
let verify = {
86: {
name: '中国',
match: /^1[3|4|5|8|7][0-9]{9}$/.test(phone)
},
852: {
name: '中国香港',
match: /^[9|6|5][0-9]{7}$/.test(phone)
},
853: {
name: '中国澳门',
match: /^[0-9]{8}$/.test(phone)
},
886: {
name: '中国台湾',
match: /^[0-9]{10}$/.test(phone)
},
65: {
name: '新加坡',
match: /^[9|8][0-9]{7}$/.test(phone)
},
60: {
name: '马来西亚',
match: /^1[1|2|3|4|6|7|9][0-9]{8}$/.test(phone)
},
1: {
name: '加拿大&美国',
match: /^[0-9]{10}$/.test(phone)
},
82: {
name: '韩国',
match: /^01[0-9]{9}$/.test(phone)
},
44: {
name: '英国',
match: /^7[7|8|9][0-9]{8}$/.test(phone)
},
81: {
name: '日本',
match: /^0[9|8|7][0-9]{9}$/.test(phone)
},
61: {
name: '澳大利亚',
match: /^[0-9]{11}$/.test(phone)
}
};
if (verify[area]) {
return verify[area].match;
} else {
return false;
}
}
/**
* 验证国际手机号是否合法
*/
exports.verifyAreaMobile = (areaMobile) => {
if (!areaMobile) {
return false;
}
let mobile = {
area: '86',
phone: ''
};
let splitMobile = areaMobile.split('-');
if (splitMobile.length === 2) {
mobile.area = splitMobile[0];
mobile.phone = splitMobile[1];
} else {
mobile.phone = splitMobile[0];
}
return areaMobileVerify(mobile.phone, mobile.area);
};
/**
* 验证手机是否合法
*/
exports.verifyMobile = (phone) => {
if (!phone) {
return false;
}
return /^1[3|4|5|8|7][0-9]{9}$/.test(phone);
};
/**
* 验证密码规则
*/
exports.verifyPassword = (password) => {
if (!password) {
return false;
}
return /^([a-zA-Z0-9\-\+_!@\#$%\^&\*\(\)\:\;\.=\[\]\\\',\?]){6,20}$/.test(password);
};
/**
* 日志工具类
* @author: hbomb<qiqi.zhou@yoho.cn>
* @date: 2016/05/06
*/
'use strict';
const winston = require('winston');
const config = require('../config/common');
const FileTransport = require('winston-daily-rotate-file');
require('influxdb-winston');
const logger = new(winston.Logger)({
transports: [
new(FileTransport)(config.loggers.infoFile),
new(FileTransport)(config.loggers.errorFile),
new(winston.transports.UdpTransport)(config.loggers.udp),
new(winston.transports.Console)(config.loggers.console)
],
exitOnError: false
});
module.exports = logger;
/**
* 签名
* @author: bikai
* @date: 2016/5/6
*/
'use strict';
const _ = require('lodash');
const md5 = require('md5');
const privateKey = {
android: 'fd4ad5fcfa0de589ef238c0e7331b585',
iphone: 'a85bb0674e08986c6b115d5e3a4884fa',
ipad: 'ad9fcda2e679cf9229e37feae2cdcf80',
web: '0ed29744ed318fd28d2c07985d3ba633',
yoho: 'fd4ad5fcsa0de589af23234ks1923ks',
h5: 'fd4ad5fcfa0de589ef238c0e7331b585'
};
/**
* 排序参数
* @param {Object} argument 需要排序的参数对象
* @return {Object} 排序之后的参数对象
*/
const packageSort = argument => {
const newObj = {};
for (const k of Object.keys(argument).sort()) {
newObj[k] = argument[k];
}
return newObj;
};
/**
* 生成签名
* @param {Object} argument 需要签名的数据
* @return {string} 生成的签名字符串
*/
const makeSign = argument => {
const qs = [];
_.forEach(argument, (value, key) => {
qs.push(`${key}=${_.trim(value)}`);
});
return md5(qs.join('&')).toLowerCase();
};
// 生成API签名,调用后端接口的时候有私钥校验
exports.apiSign = (params) => {
const clientType = params.client_type || 'h5';
/* eslint-disable */
let sign = packageSort(Object.assign({
client_type: clientType,
private_key: privateKey[clientType],
app_version: '4.6.0',
os_version: 'yohobuy:h5',
screen_size: '720x1280',
v: '7'
}, params));
/* eslint-enable */
sign = Object.assign(sign, {
client_secret: makeSign(sign) // eslint-disable-line camelcase
});
delete sign.private_key;
return sign;
};
// 检查签名,APP 访问 H5 页面的时候需要检查
exports.checkSign = (params) => {
const // eslint-disable-line camelcase
clientSecret = params.client_secret;
let sortedParams;
// 忽略部分参数
delete params.client_secret;
delete params.q;
delete params.debug_data;
delete params['/api'];
params.private_key = privateKey[params.client_type]; // eslint-disable-line camelcase
sortedParams = packageSort(params);
return clientSecret === makeSign(sortedParams);
};
// 检查签名,APP 访问 H5 页面的时候需要检查, 有可能不同于上边的签名方式
exports.webSign = (params) => {
const webPrivateKey = 'yohobuyapp';
return params.key === md5(md5(webPrivateKey) + params.uid);
};
// 生成 token
exports.makeToken = (string) => {
return md5(md5(string + '#@!@#'));
};
// 校验 token
exports.verifyToken = (string, token) => {
return exports.makeToken(string) === token;
};
/**
* 计时类
* @example
* let timer = new Timer();
* timer.put('profile');
* timer.put('proflie'); // console output: 12.14
*
* @author: hbomb<qiqi.zhou@yoho.cn>
* @date: 2016/05/07
*/
'use strict';
class Timer {
constructor() {
this.timers = {};
}
/**
* 打点计时
*/
put(label) {
const labelTime = this.timers[label];
if (labelTime) {
const duration = process.hrtime(labelTime);
return this._round(duration[0], duration[1]);
} else {
this.timers[label] = process.hrtime();
}
}
/**
* 格式化成毫秒
* @param {Number} value 纳秒
*/
_round(seconds, nanoseconds) {
return Math.round((seconds * 1e9 + nanoseconds) / 10000) / 100;
}
}
module.exports = Timer;
/**
* http api 测试
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 2016/05/17
*/
'use strict';
const test = require('ava');
const sign = require('../../library/sign');
const API = require('../../library/api').API;
const ServiceAPI = require('../../library/api').ServiceAPI;
const SearchAPI = require('../../library/api').SearchAPI;
const getUrl = 'operations/api/v6/category/getCategory';
test('api constructor test', (t) => {
let api = new ServiceAPI();
let api2 = new API();
let api3 = new SearchAPI();
t.true(api !== null);
t.true(api2 !== null);
t.true(api3 !== null);
});
test('api get test', t => {
let api = new ServiceAPI();
return api.get(getUrl, sign.apiSign({})).then(result => {
if (result && result.code) {
t.pass();
} else {
t.fail();
}
});
});
test('api get test, api return an error', t => {
let api = new ServiceAPI();
return api.get(getUrl + '/error', sign.apiSign({})).catch(err => {
// 故意调用一个错误的接口
if (err && err.code === 500) {
t.pass();
} else {
t.fail();
}
});
});
test('api get use cache test', t => {
let api = new ServiceAPI();
return api.get(getUrl, sign.apiSign({}), true).then(result => {
if (result && result.code) {
t.pass();
} else {
t.fail();
}
});
});
test('api post test', t => {
let api = new ServiceAPI();
return api.post(getUrl, sign.apiSign({})).then(result => {
if (result && result.code) {
t.pass();
} else {
t.fail();
}
});
});
test('api multiple call test', (t) => {
let api = new ServiceAPI();
let multi = [api.get(getUrl, sign.apiSign({})), api.get(getUrl, sign.apiSign({}))];
return api.all(multi).then(result => {
if (result.length === 2) {
t.pass();
} else {
t.fail();
}
});
});
test('api multiple fail call test', (t) => {
let api = new ServiceAPI();
return api.all(1).catch((e) => {
if (e) {
t.pass();
} else {
t.fail();
}
});
});
/**
* cache 测试
*
* @author: jf<jeff.jiang@yoho.cn>
* @date: 2016/5/18
*/
'use strict';
import test from 'ava';
import cache from '../../library/cache';
let testKey = 'test_unit_key:' + (new Date()).getTime();
let testValue = 'anotherValue';
let anotherKey = 'test_unit_key2:' + (new Date()).getTime();
let anotherValue = {a: 1};
let slaveTestKey = 'test_unit_key3:' + (new Date()).getTime();
let slaveTestValue = 'anotherValue3';
test.before('set test key', (t) => {
cache.set(testKey, testValue);
cache.set(anotherKey, anotherValue);
t.pass();
});
test.after('del test key', (t) => {
cache.del(testKey);
cache.del(anotherKey);
t.pass();
});
test('cache get test', (t) => {
return cache.get(testKey).then((v) => {
t.is(v, testValue);
});
});
test('cache get multi test', (t) => {
cache.set(anotherKey, anotherValue);
return cache.getMulti([testKey, anotherKey]).then((values) => {
t.is(values[testKey], testValue);
t.is(values[anotherKey], JSON.stringify(anotherValue));
});
});
test('cache get from slave test', (t) => {
return cache.getFromSlave(testKey).then((v) => {
t.is(v, testValue);
});
});
test('cache get multi from slave test', (t) => {
cache.set(anotherKey, anotherValue);
return cache.getMultiFromSlave([testKey, anotherKey]).then((values) => {
t.is(values[testKey], testValue);
t.is(values[anotherKey], JSON.stringify(anotherValue));
});
});
test('cache set to slave', (t) => {
return cache.setSlave(slaveTestKey, {
value: slaveTestValue
}).then(() => {
return cache.getFromSlave(slaveTestKey);
}).then((v) => {
v = JSON.parse(v);
t.is(v.value, slaveTestValue);
cache.del(slaveTestKey);
});
});
test('cache get test, key is not a string', (t) => {
return cache.get(123).then((v) => {
t.notOk(v);
});
});
test('cache get multi test, key is not an array', (t) => {
return cache.getMulti(123).then((v) => {
t.notOk(v);
});
});
test('cache get from slave test, key is not a string', (t) => {
return cache.getFromSlave(123).then((v) => {
t.notOk(v);
});
});
test('cache get multi from slave test, key is not an array', (t) => {
return cache.getMultiFromSlave(123).then((v) => {
t.notOk(v);
});
});
test('cache set test, key is not a string', (t) => {
return cache.set(123).then((v) => {
t.notOk(v);
});
});
test('cache set multi test, key is not an array', (t) => {
return cache.setSlave(123).then((v) => {
t.notOk(v);
});
});
test('cache del test, key is not a string', (t) => {
return cache.del(123).then((v) => {
t.notOk(v);
});
});
/**
* 对象键名驼峰测试
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 2016/05/17
*/
import {test} from 'ava';
const camelCase = require('../../library/camel-case');
test('camel case object', t => {
let o = {
A_B: 'ab_cd'
};
t.is(camelCase(o).aB, 'ab_cd');
});
test('camel case array', t => {
let arr = [{
A_B: 'ab_cd'
}, {
A_B: 'ab_cd'
}];
t.is(camelCase(arr)[1].aB, 'ab_cd');
});
/**
* library helpers 类单元测试
* @author jeff.jiang<jeff.jiang@yoho.cn>
* @date 2016/05/17
*/
'use strict';
const test = require('ava');
const helpers = require('../../library/helpers');
test('qiniu image url handle', t => {
let url = 'http://img11.static.yhbimg.com/yhb-img01/2016/04/18/03/016d50b20cfdec5a91c614b68546bc9d72.jpg?imageView2/{mode}/w/{width}/h/{height}';
let expected = 'http://img11.static.yhbimg.com/yhb-img01/2016/04/18/03/016d50b20cfdec5a91c614b68546bc9d72.jpg?imageView2/2/w/400/h/300';
t.is(helpers.image(url, 400, 300), expected);
});
test('uri format', t => {
let uri = '/test';
let qs = { name: 'yoho' };
let mod = 'list';
let expected = '//list.m.yohobuy.com/test?name=yoho';
t.is(helpers.urlFormat(uri, qs, mod), expected);
});
test('upper char to lowercase', t => {
let str = 'ABc';
let expected = 'abc';
t.is(helpers.lowerCase(str), expected);
});
test('lower char to uppercase', t => {
let str = 'abc!';
let expected = 'ABC!';
t.is(helpers.upperCase(str), expected);
});
test('date format test', (t) => {
let seconds = 60 * 60 * 3 + 2;
let str = helpers.dateFormat('HH:mm:ss', seconds, 'ss');
let expected = '03:00:02';
t.is(str, expected);
});
test('date diff format test', (t) => {
let seconds = 60 * 60 * 24 * (1.3) + 2;
let str = helpers.dateDiffFormat('{d}天{h}小时', seconds, 's');
console.log(str);
t.pass();
});
/**
* logger 工具类测试
*/
const test = require('ava');
const logger = require('../../library/logger');
test('logger error test', t => {
logger.error('error test', () => {
t.pass();
});
});
test('logger info test', t => {
logger.info('info test', () => {
t.pass();
});
});
/**
* 签名类测试
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 2016/05/17
*/
const test = require('ava');
const sign = require('../../library/sign');
test('app sign test', t => {
let params = {
client_type: 'h5', // eslint-disable-line
a: 1,
b: 'b'
};
let signedParams = sign.apiSign(params);
t.true(sign.checkSign(signedParams));
});
test('app sign test webSign', t => {
let params = {
uid: '123',
key: '3fc5a9fcea9fea49cce5432202a167ad'
};
t.true(sign.webSign(params));
});
/**
* Timer 计时类测试
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 2016/05/17
*/
const test = require('ava');
const Timer = require('../../library/timer');
const sleep = (timeout) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
};
test.cb('timer class ', t => {
let timer = new Timer();
timer.put('test');
sleep(300).then(() => {
let diff = timer.put('test');
t.true(diff >= 300);
t.end();
});
});