@qiniu/miku-delivery-mp-ks
Version:
Kuaishou Mini Program SDK for Miku Delivery
236 lines (234 loc) • 9.34 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
jest.setTimeout(15000);
jest.mock('../utils/env');
import { delay, randomText } from '../test';
import Logger, { getLogBody, apiPrefix } from '.';
import { makeSuccFetch } from './test';
import { Response, Headers } from '../utils/fetch';
const appInfo = {
appID: 'foo',
appSalt: 'bar'
};
describe('Logger', () => {
it('should construct well', () => {
new Logger(appInfo, makeSuccFetch());
});
describe('log', () => {
it('should work well', () => __awaiter(void 0, void 0, void 0, function* () {
const logger = new Logger(appInfo, makeSuccFetch());
logger.log(...makeDNSResolveLog());
}));
it('should batch', () => __awaiter(void 0, void 0, void 0, function* () {
const f = makeSuccFetch();
const logger = new Logger(appInfo, f);
logger.log(...makeDNSResolveLog());
expect(f).not.toBeCalled();
}));
it('should call api correctly', () => __awaiter(void 0, void 0, void 0, function* () {
var _a;
const f = makeSuccFetch();
const logger = new Logger(appInfo, f, undefined, 1);
logger.log(...makeDownloadLog());
logger.log(...makeDNSResolveLog());
logger.log(...makeDownloadLog());
logger.log(...makeDNSResolveLog());
logger.log(...makeDownloadLog());
yield delay(1500);
expect(f).toBeCalledTimes(1);
const req = f.mock.calls[0][0];
expect((_a = req.headers.get('X-Env')) === null || _a === void 0 ? void 0 : _a.length).toBeGreaterThan(10);
expect([
`${apiPrefix}/v1/log/DownloadLog,DnsResolveLog`,
`${apiPrefix}/v1/log/DnsResolveLog,DownloadLog`
]).toContain(req.url);
const countArray = countLogs(req);
expect(countArray.length).toBe(2);
expect(countArray.sort()).toEqual([2, 3]);
}));
it('should flush when $flushNum reached', () => __awaiter(void 0, void 0, void 0, function* () {
const flushNum = 5;
const f = makeSuccFetch();
const logger = new Logger(appInfo, f, flushNum);
logger.log(...makeDownloadLog());
logger.log(...makeDNSResolveLog());
logger.log(...makeDownloadLog());
logger.log(...makeDNSResolveLog());
yield delay(0);
expect(f).not.toBeCalled();
logger.log(...makeDownloadLog());
yield delay(0);
expect(f).toBeCalledTimes(1);
expect([
`${apiPrefix}/v1/log/DownloadLog,DnsResolveLog`,
`${apiPrefix}/v1/log/DnsResolveLog,DownloadLog`
]).toContain(f.mock.calls[0][0].url);
}));
it('should flush when $flushNum exceeded', () => __awaiter(void 0, void 0, void 0, function* () {
const flushNum = 5;
const logNum = flushNum + 3;
const f = makeSuccFetch();
const logger = new Logger(appInfo, f, flushNum);
for (let i = 0; i < logNum; i++) {
logger.log(...makeDownloadLog());
}
yield delay(0);
expect(f).toBeCalledTimes(1);
const req = f.mock.calls[0][0];
expect(req.url).toBe(`${apiPrefix}/v1/log/DownloadLog`);
expect(countLogs(req)).toEqual([logNum]);
}));
it('should flush when $flushWait reached', () => __awaiter(void 0, void 0, void 0, function* () {
const flushWait = 1;
const f = makeSuccFetch();
const logger = new Logger(appInfo, f, 100, flushWait);
for (let i = 0; i < 10; i++) {
logger.log(...makeDownloadLog());
}
yield delay(0);
expect(f).not.toBeCalled();
for (let i = 0; i < 10; i++) {
logger.log(...makeDNSResolveLog());
}
yield delay(500);
expect(f).not.toBeCalled();
yield delay(1000);
expect(f).toBeCalledTimes(1);
expect(countLogs(f.mock.calls[0][0])).toEqual([10, 10]);
f.mockClear();
logger.log(...makeDownloadLog());
expect(f).not.toBeCalled();
yield delay(1500);
expect(f).toBeCalledTimes(1);
expect(countLogs(f.mock.calls[0][0])).toEqual([1]);
}));
it('should send 200 logs at most once', () => __awaiter(void 0, void 0, void 0, function* () {
const logNum = 502;
const f = makeSuccFetch();
const logger = new Logger(appInfo, f);
for (let i = 0; i < logNum; i += 2) {
logger.log(...makeDownloadLog());
logger.log(...makeDNSResolveLog());
}
yield delay(0);
expect(f).toBeCalledTimes(3);
expect(countLogs(f.mock.calls[0][0])).toEqual([100, 100]);
expect(countLogs(f.mock.calls[1][0])).toEqual([100, 100]);
expect(countLogs(f.mock.calls[2][0])).toEqual([51, 51]);
}));
it('should retry when call api failed', function () {
return __awaiter(this, void 0, void 0, function* () {
const f = jest.fn()
.mockResolvedValueOnce(new Response('', { status: 502, headers: new Headers() }))
.mockResolvedValue(new Response('', { status: 200, headers: new Headers() }));
const logger = new Logger(appInfo, f, undefined, 1);
logger.log(...makeDNSResolveLog());
// flush & 1st try
yield delay(1500);
expect(f).toBeCalledTimes(1);
// retry after 2s
yield delay(2000);
expect(f).toBeCalledTimes(2);
// no more retry after succeed
yield delay(2000);
expect(f).toBeCalledTimes(2);
});
});
it('should limit retry attempts', function () {
return __awaiter(this, void 0, void 0, function* () {
const f = jest.fn().mockResolvedValue(new Response('', { status: 502, headers: new Headers() }));
const logger = new Logger(appInfo, f, undefined, 1);
logger.log(...makeDNSResolveLog());
yield delay(10000);
expect(f).toBeCalledTimes(3);
});
});
});
});
describe('getLogBody', () => {
it('should work well', () => {
const now = Date.now();
expect(getLogBody([{
schema: 'DnsResolveLog',
logs: [
{ ts: now, foo: 1, bar: 'bar' },
{ ts: now + 1, foo: 2, bar: 'bar,2' },
{ ts: now + 2, foo: 3, bar: 'bar,"3' }
]
}, {
schema: 'DownloadLog',
logs: [
{ ts: now, baz: 4 }
]
}])).toBe(`
ts,foo,bar
${now},1,bar
${now + 1},2,"bar,2"
${now + 2},3,"bar,""3"
ts,baz
${now},4
`.trim());
});
it('should put ts as the first column', () => {
const now = Date.now();
expect(getLogBody([{
schema: 'DnsResolveLog',
logs: [
{ bar: 'bar', ts: now, foo: 1 },
{ bar: 'bar,2', ts: now + 1, foo: 2 },
{ bar: 'bar,"3', ts: now + 2, foo: 3 }
]
}])).toBe(`
ts,bar,foo
${now},bar,1
${now + 1},"bar,2",2
${now + 2},"bar,""3",3
`.trim());
});
});
function makeDNSResolveLog() {
return ['DnsResolveLog', {
r_id: randomText(20),
loc: '127.0.0.1',
ip: '1.1.1.1',
domain: 'foo.com',
status_code: 200,
err_msg: '',
err_desc: '',
t_conn: 0.5,
t_total: 0.8
}];
}
function makeDownloadLog() {
return ['DownloadLog', {
r_id: randomText(20),
loc: '127.0.0.1',
ip: '1.1.1.1',
domain: 'foo.com',
status_code: 200,
err_msg: '',
err_desc: '',
range_st: 0,
range_end: 0,
retry: 1,
ftask_id: randomText(20),
d_id: randomText(20),
t_conn: 0.5,
t_tls: 0.2,
t_st_trans: 0.2,
t_content_trans: 0.5,
t_total: 1.5,
resp_size: 1 << 10
}];
}
function countLogs(request) {
const blocks = request.body.split('\n\n');
return blocks.map(block => block.split('\n').length - 1);
}