[add] first
This commit is contained in:
1
test/Base.ts
Normal file
1
test/Base.ts
Normal file
@@ -0,0 +1 @@
|
||||
import 'k8w-extend-native';
|
||||
10
test/api/ApiObjId.ts
Normal file
10
test/api/ApiObjId.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ApiCall } from "../../src/server/base/ApiCall";
|
||||
import { ReqObjId, ResObjId } from "../proto/PtlObjId";
|
||||
|
||||
export async function ApiObjId(call: ApiCall<ReqObjId, ResObjId>) {
|
||||
call.succ({
|
||||
id2: call.req.id1,
|
||||
buf: call.req.buf,
|
||||
date: call.req.date
|
||||
})
|
||||
}
|
||||
27
test/api/ApiTest.ts
Normal file
27
test/api/ApiTest.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { TsrpcError } from "tsrpc-proto";
|
||||
import { ApiCall } from "../../src/server/base/ApiCall";
|
||||
import { ReqTest, ResTest } from "../proto/PtlTest";
|
||||
|
||||
export async function ApiTest(call: ApiCall<ReqTest, ResTest>) {
|
||||
if (call.req.name === 'InnerError') {
|
||||
await new Promise(rs => { setTimeout(rs, 50) })
|
||||
throw new Error('Test InnerError')
|
||||
}
|
||||
else if (call.req.name === 'TsrpcError') {
|
||||
await new Promise(rs => { setTimeout(rs, 50) })
|
||||
throw new TsrpcError('Test TsrpcError', {
|
||||
code: 'CODE_TEST',
|
||||
info: 'ErrInfo Test'
|
||||
});
|
||||
}
|
||||
else if (call.req.name === 'error') {
|
||||
await new Promise(rs => { setTimeout(rs, 50) })
|
||||
call.error('Got an error')
|
||||
}
|
||||
else {
|
||||
await new Promise(rs => { setTimeout(rs, 50) })
|
||||
call.succ({
|
||||
reply: 'Test reply: ' + call.req.name
|
||||
})
|
||||
}
|
||||
}
|
||||
21
test/api/a/b/c/ApiTest.ts
Normal file
21
test/api/a/b/c/ApiTest.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { TsrpcError } from "tsrpc-proto";
|
||||
|
||||
export async function ApiTest(call: any) {
|
||||
if (call.req.name === 'InnerError') {
|
||||
throw new Error('a/b/c/Test InnerError')
|
||||
}
|
||||
else if (call.req.name === 'TsrpcError') {
|
||||
throw new TsrpcError('a/b/c/Test TsrpcError', {
|
||||
code: 'CODE_TEST',
|
||||
info: 'ErrInfo a/b/c/Test'
|
||||
});
|
||||
}
|
||||
else if (call.req.name === 'error') {
|
||||
call.error('Got an error')
|
||||
}
|
||||
else {
|
||||
call.succ({
|
||||
reply: 'a/b/c/Test reply: ' + call.req.name
|
||||
})
|
||||
}
|
||||
}
|
||||
907
test/cases/http.test.ts
Normal file
907
test/cases/http.test.ts
Normal file
@@ -0,0 +1,907 @@
|
||||
import { ObjectId } from 'bson';
|
||||
import { assert } from 'chai';
|
||||
import chalk from 'chalk';
|
||||
import * as path from "path";
|
||||
import { ServiceProto, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
|
||||
import { ApiCall, BaseServer, HttpConnection, MsgCall, TerminalColorLogger } from '../../src';
|
||||
import { HttpClient } from '../../src/client/http/HttpClient';
|
||||
import { HttpServer } from '../../src/server/http/HttpServer';
|
||||
import { PrefixLogger } from '../../src/server/models/PrefixLogger';
|
||||
import { ApiTest as ApiAbcTest } from '../api/a/b/c/ApiTest';
|
||||
import { ApiTest } from '../api/ApiTest';
|
||||
import { MsgChat } from '../proto/MsgChat';
|
||||
import { ReqTest, ResTest } from '../proto/PtlTest';
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
|
||||
const serverLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgGreen.white(' Server ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Server' })
|
||||
});
|
||||
const clientLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgBlue.white(' Client ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Client' })
|
||||
})
|
||||
|
||||
const getProto = () => Object.merge({}, serviceProto) as ServiceProto<ServiceType>;
|
||||
|
||||
async function testApi(server: HttpServer<ServiceType>, client: HttpClient<ServiceType>) {
|
||||
// Succ
|
||||
assert.deepStrictEqual(await client.callApi('Test', {
|
||||
name: 'Req1'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: Req1'
|
||||
}
|
||||
});
|
||||
assert.deepStrictEqual(await client.callApi('a/b/c/Test', {
|
||||
name: 'Req2'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'a/b/c/Test reply: Req2'
|
||||
}
|
||||
});
|
||||
|
||||
// Inner error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await client.callApi(v as any, {
|
||||
name: 'InnerError'
|
||||
});
|
||||
delete ret.err!.innerErr.stack;
|
||||
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
code: 'INTERNAL_ERR',
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: `${v} InnerError`
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// TsrpcError
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await client.callApi(v as any, {
|
||||
name: 'TsrpcError'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError(`${v} TsrpcError`, {
|
||||
code: 'CODE_TEST',
|
||||
type: TsrpcErrorType.ApiError,
|
||||
info: 'ErrInfo ' + v
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// call.error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await client.callApi(v as any, {
|
||||
name: 'error'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Got an error', {
|
||||
type: TsrpcErrorType.ApiError
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('HTTP Server & Client basic', function () {
|
||||
it('implement API manually', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.implementApi('Test', ApiTest);
|
||||
server.implementApi('a/b/c/Test', ApiAbcTest);
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger,
|
||||
debugBuf: true
|
||||
})
|
||||
|
||||
await testApi(server, client);
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('extend call in handler', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
|
||||
server.implementApi('Test', (call: MyApiCall<ReqTest, ResTest>) => {
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
});
|
||||
server.listenMsg('Chat', (call: MyMsgCall<MsgChat>) => {
|
||||
call.msg.content;
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
})
|
||||
})
|
||||
|
||||
it('extend call in flow', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyConn = HttpConnection<any> & {
|
||||
currentUser: {
|
||||
uid: string,
|
||||
nickName: string
|
||||
}
|
||||
}
|
||||
|
||||
server.flows.postConnectFlow.push((conn: MyConn) => {
|
||||
conn.currentUser.nickName = 'asdf';
|
||||
return conn;
|
||||
});
|
||||
server.flows.postConnectFlow.exec(null as any as MyConn, console);
|
||||
server.flows.preApiCallFlow.push((call: MyApiCall<any, any>) => {
|
||||
call.value2 = 'x';
|
||||
return call;
|
||||
});
|
||||
server.flows.preSendMsgFlow.push((call: MyMsgCall<any>) => {
|
||||
call.value2 = 'f';
|
||||
return call;
|
||||
})
|
||||
})
|
||||
|
||||
it('autoImplementApi', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
await testApi(server, client);
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('sendMsg', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
port: 3001,
|
||||
logger: serverLogger,
|
||||
// debugBuf: true
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
server: 'http://127.0.0.1:3001',
|
||||
logger: clientLogger,
|
||||
// debugBuf: true
|
||||
});
|
||||
|
||||
return new Promise(rs => {
|
||||
let msg: MsgChat = {
|
||||
channel: 123,
|
||||
userName: 'fff',
|
||||
content: '666',
|
||||
time: Date.now()
|
||||
};
|
||||
|
||||
server.listenMsg('Chat', async v => {
|
||||
assert.deepStrictEqual(v.msg, msg);
|
||||
await server.stop();
|
||||
rs();
|
||||
});
|
||||
|
||||
client.sendMsg('Chat', msg);
|
||||
})
|
||||
})
|
||||
|
||||
it('abort', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let result: any | undefined;
|
||||
let promise = client.callApi('Test', { name: 'aaaaaaaa' });
|
||||
let sn = client.lastSN;
|
||||
setTimeout(() => {
|
||||
client.abort(sn)
|
||||
}, 10);
|
||||
promise.then(v => {
|
||||
result = v;
|
||||
});
|
||||
|
||||
await new Promise<void>(rs => {
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(result, undefined);
|
||||
rs();
|
||||
}, 150)
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('abortByKey', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let result: any | undefined;
|
||||
let result1: any | undefined;
|
||||
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
|
||||
client.callApi('Test', { name: 'bbbbbb' }).then(v => { result1 = v; });
|
||||
|
||||
setTimeout(() => {
|
||||
client.abortByKey('XXX')
|
||||
}, 10);
|
||||
|
||||
await new Promise<void>(rs => {
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(result, undefined);
|
||||
assert.deepStrictEqual(result1, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: bbbbbb'
|
||||
}
|
||||
})
|
||||
rs();
|
||||
}, 150)
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('abortAll', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let result: any | undefined;
|
||||
let result1: any | undefined;
|
||||
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
|
||||
client.callApi('Test', { name: 'bbbbbb' }).then(v => { result1 = v; });
|
||||
|
||||
setTimeout(() => {
|
||||
client.abortAll()
|
||||
}, 10);
|
||||
|
||||
await new Promise<void>(rs => {
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(result, undefined);
|
||||
assert.strictEqual(result1, undefined);
|
||||
rs();
|
||||
}, 150)
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('pendingApis', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
let promise = Promise.all(Array.from({ length: 10 }, () => new Promise<void>(rs => {
|
||||
let name = ['Req', 'InnerError', 'TsrpcError', 'error'][Math.random() * 4 | 0];
|
||||
let ret: any | undefined;
|
||||
let promise = client.callApi('Test', { name: name });
|
||||
let sn = client.lastSN;
|
||||
let abort = Math.random() > 0.5;
|
||||
if (abort) {
|
||||
setTimeout(() => {
|
||||
client.abort(sn)
|
||||
}, 0);
|
||||
}
|
||||
promise.then(v => {
|
||||
ret = v;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
client.logger?.log('sn', sn, name, abort, ret)
|
||||
if (abort) {
|
||||
assert.strictEqual(ret, undefined);
|
||||
}
|
||||
else {
|
||||
assert.notEqual(ret, undefined);
|
||||
if (name === 'Req') {
|
||||
assert.strictEqual(ret.isSucc, true);
|
||||
}
|
||||
else {
|
||||
assert.strictEqual(ret.isSucc, false)
|
||||
}
|
||||
}
|
||||
rs();
|
||||
}, 300)
|
||||
})));
|
||||
assert.strictEqual(client['_pendingApis'].length, 10);
|
||||
await promise;
|
||||
assert.strictEqual(client['_pendingApis'].length, 0);
|
||||
}
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let client1 = new HttpClient(getProto(), {
|
||||
server: 'http://localhost:80',
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let ret = await client1.callApi('Test', { name: 'xx' });
|
||||
console.log(ret);
|
||||
assert.strictEqual(ret.isSucc, false);
|
||||
assert.strictEqual(ret.err?.code, 'ECONNREFUSED');
|
||||
assert.strictEqual(ret.err?.type, TsrpcErrorType.NetworkError);
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('server timeout', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 100
|
||||
});
|
||||
server.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.req && call.succ({
|
||||
reply: 'Hi, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
let ret = await client.callApi('Test', { name: 'Jack' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Server Timeout', {
|
||||
code: 'SERVER_TIMEOUT',
|
||||
type: TsrpcErrorType.ServerError
|
||||
})
|
||||
});
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('client timeout', async function () {
|
||||
let server1 = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
server1.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.succ({
|
||||
reply: 'Hello, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 2000)
|
||||
})
|
||||
})
|
||||
await server1.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
timeout: 100,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'Jack123' });
|
||||
// SERVER TIMEOUT的call还没执行完,但是call却被放入Pool了,导致这个BUG
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError({
|
||||
message: 'Request Timeout',
|
||||
code: 'TIMEOUT',
|
||||
type: TsrpcErrorType.NetworkError
|
||||
})
|
||||
});
|
||||
await server1.stop();
|
||||
});
|
||||
|
||||
it('Graceful stop', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
let reqNum = 0;
|
||||
server.implementApi('Test', async call => {
|
||||
if (++reqNum === 10) {
|
||||
server.gracefulStop();
|
||||
}
|
||||
await new Promise(rs => setTimeout(rs, parseInt(call.req.name)));
|
||||
call.succ({ reply: 'OK' });
|
||||
});
|
||||
|
||||
await server.start();
|
||||
let isStopped = false;
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let succNum = 0;
|
||||
await Promise.all(Array.from({ length: 10 }, (v, i) => client.callApi('Test', { name: '' + (i * 100) }).then(v => {
|
||||
if (v.res?.reply === 'OK') {
|
||||
++succNum;
|
||||
}
|
||||
})))
|
||||
assert.strictEqual(succNum, 10);
|
||||
})
|
||||
})
|
||||
|
||||
describe('HTTP Flows', function () {
|
||||
it('Server conn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
assert.strictEqual((call.conn as any).xxxx, 'asdfasdf')
|
||||
assert.strictEqual(flowExecResult.postConnectFlow, true);
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, undefined);
|
||||
call.succ({ reply: 'ok' });
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, undefined);
|
||||
});
|
||||
|
||||
server.flows.postConnectFlow.push(v => {
|
||||
flowExecResult.postConnectFlow = true;
|
||||
(v as any).xxxx = 'asdfasdf';
|
||||
return v;
|
||||
});
|
||||
server.flows.postDisconnectFlow.push(v => {
|
||||
flowExecResult.postDisconnectFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
assert.strictEqual(flowExecResult.postConnectFlow, undefined);
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, undefined);
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postConnectFlow, true);
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, true);
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('Buffer enc/dec flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'Enc&Dec' });
|
||||
});
|
||||
|
||||
server.flows.preRecvBufferFlow.push(v => {
|
||||
flowExecResult.preRecvBufferFlow = true;
|
||||
for (let i = 0; i < v.buf.length; ++i) {
|
||||
v.buf[i] ^= 128;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
server.flows.preSendBufferFlow.push(v => {
|
||||
flowExecResult.preSendBufferFlow = true;
|
||||
for (let i = 0; i < v.buf.length; ++i) {
|
||||
v.buf[i] ^= 128;
|
||||
}
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preSendBufferFlow.push(v => {
|
||||
for (let i = 0; i < v.buf.length; ++i) {
|
||||
v.buf[i] ^= 128;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
client.flows.preRecvBufferFlow.push(v => {
|
||||
for (let i = 0; i < v.buf.length; ++i) {
|
||||
v.buf[i] ^= 128;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preRecvBufferFlow, true);
|
||||
assert.strictEqual(flowExecResult.preSendBufferFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Enc&Dec'
|
||||
}
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('ApiCall flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return call;
|
||||
});
|
||||
server.flows.postApiCallFlow.push(v => {
|
||||
flowExecResult.postApiCallFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preCallApiFlow.push(v => {
|
||||
if (v.apiName !== 'ObjId') {
|
||||
v.req.name = 'Changed'
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postApiCallFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('ApiCall flow break', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return undefined;
|
||||
});
|
||||
server.flows.postApiCallFlow.push(v => {
|
||||
flowExecResult.postApiCallFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preCallApiFlow.push(v => {
|
||||
if (v.apiName !== 'ObjId') {
|
||||
v.req.name = 'Changed'
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postApiCallFlow, undefined);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('ApiCall flow error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
throw new Error('ASDFASDF')
|
||||
});
|
||||
server.flows.postApiCallFlow.push(v => {
|
||||
flowExecResult.postApiCallFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preCallApiFlow.push(v => {
|
||||
if (v.apiName !== 'ObjId') {
|
||||
v.req.name = 'Changed'
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postApiCallFlow, undefined);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: 'ASDFASDF',
|
||||
code: 'INTERNAL_ERR'
|
||||
})
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('server ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
server.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
}
|
||||
return v;
|
||||
});
|
||||
server.flows.postApiReturnFlow.push(v => {
|
||||
flowExecResult.postApiReturnFlow = true;
|
||||
v.call.logger.log('RETTT', v.return);
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.strictEqual(flowExecResult.postApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('client ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof HttpClient<any>['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
}
|
||||
return v;
|
||||
});
|
||||
client.flows.postApiReturnFlow.push(v => {
|
||||
flowExecResult.postApiReturnFlow = true;
|
||||
client.logger?.log('RETTT', v.return);
|
||||
return v;
|
||||
})
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.strictEqual(flowExecResult.postApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('client SendBufferFlow prevent', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
// const flowExecResult: { [K in (keyof BaseClient<any>['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preSendBufferFlow.push(v => {
|
||||
return undefined
|
||||
});
|
||||
|
||||
let ret: any;
|
||||
client.callApi('Test', { name: 'xxx' }).then(v => { ret = v });
|
||||
await new Promise(rs => { setTimeout(rs, 200) });
|
||||
assert.strictEqual(ret, undefined)
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('onInputBufferError', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
client.flows.preSendBufferFlow.push(v => {
|
||||
for (let i = 0; i < v.buf.length; ++i) {
|
||||
v.buf[i] += 1;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'XXX' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Invalid request buffer, please check the version of service proto.', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
code: 'INPUT_DATA_ERR'
|
||||
})
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('ObjectId', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
// ObjectId
|
||||
let objId1 = new ObjectId();
|
||||
let ret = await client.callApi('ObjId', {
|
||||
id1: objId1
|
||||
});
|
||||
assert.strictEqual(ret.isSucc, true, ret.err?.message);
|
||||
assert.strictEqual(objId1.toString(), ret.res!.id2.toString());
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
})
|
||||
997
test/cases/httpJSON.test.ts
Normal file
997
test/cases/httpJSON.test.ts
Normal file
@@ -0,0 +1,997 @@
|
||||
import { ObjectId } from 'bson';
|
||||
import { assert } from 'chai';
|
||||
import chalk from 'chalk';
|
||||
import * as path from "path";
|
||||
import { ServiceProto, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
|
||||
import { ApiCall, BaseServer, HttpConnection, MsgCall, TerminalColorLogger } from '../../src';
|
||||
import { HttpClient } from '../../src/client/http/HttpClient';
|
||||
import { HttpProxy } from '../../src/client/http/HttpProxy';
|
||||
import { HttpServer } from '../../src/server/http/HttpServer';
|
||||
import { PrefixLogger } from '../../src/server/models/PrefixLogger';
|
||||
import { ApiTest as ApiAbcTest } from '../api/a/b/c/ApiTest';
|
||||
import { ApiTest } from '../api/ApiTest';
|
||||
import { MsgChat } from '../proto/MsgChat';
|
||||
import { ReqTest, ResTest } from '../proto/PtlTest';
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
|
||||
const serverLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgGreen.white(' Server ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Server' })
|
||||
});
|
||||
const clientLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgBlue.white(' Client ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Client' })
|
||||
})
|
||||
|
||||
const getProto = () => Object.merge({}, serviceProto) as ServiceProto<ServiceType>;
|
||||
|
||||
async function testApi(server: HttpServer<ServiceType>, client: HttpClient<ServiceType>) {
|
||||
// Succ
|
||||
assert.deepStrictEqual(await client.callApi('Test', {
|
||||
name: 'Req1'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: Req1'
|
||||
}
|
||||
});
|
||||
assert.deepStrictEqual(await client.callApi('a/b/c/Test', {
|
||||
name: 'Req2'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'a/b/c/Test reply: Req2'
|
||||
}
|
||||
});
|
||||
|
||||
// Inner error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await client.callApi(v as any, {
|
||||
name: 'InnerError'
|
||||
});
|
||||
delete ret.err!.innerErr.stack;
|
||||
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
code: 'INTERNAL_ERR',
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: `${v} InnerError`
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// TsrpcError
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await client.callApi(v as any, {
|
||||
name: 'TsrpcError'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError(`${v} TsrpcError`, {
|
||||
code: 'CODE_TEST',
|
||||
type: TsrpcErrorType.ApiError,
|
||||
info: 'ErrInfo ' + v
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// call.error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await client.callApi(v as any, {
|
||||
name: 'error'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Got an error', {
|
||||
type: TsrpcErrorType.ApiError
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('HTTP Server & Client basic', function () {
|
||||
it('implement API manually', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.implementApi('Test', ApiTest);
|
||||
server.implementApi('a/b/c/Test', ApiAbcTest);
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger,
|
||||
debugBuf: true
|
||||
})
|
||||
|
||||
await testApi(server, client);
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('extend call in handler', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
|
||||
server.implementApi('Test', (call: MyApiCall<ReqTest, ResTest>) => {
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
});
|
||||
server.listenMsg('Chat', (call: MyMsgCall<MsgChat>) => {
|
||||
call.msg.content;
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
})
|
||||
})
|
||||
|
||||
it('extend call in flow', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyConn = HttpConnection<any> & {
|
||||
currentUser: {
|
||||
uid: string,
|
||||
nickName: string
|
||||
}
|
||||
}
|
||||
|
||||
server.flows.postConnectFlow.push((conn: MyConn) => {
|
||||
conn.currentUser.nickName = 'asdf';
|
||||
return conn;
|
||||
});
|
||||
server.flows.postConnectFlow.exec(null as any as MyConn, console);
|
||||
server.flows.preApiCallFlow.push((call: MyApiCall<any, any>) => {
|
||||
call.value2 = 'x';
|
||||
return call;
|
||||
});
|
||||
server.flows.preSendMsgFlow.push((call: MyMsgCall<any>) => {
|
||||
call.value2 = 'f';
|
||||
return call;
|
||||
})
|
||||
})
|
||||
|
||||
it('autoImplementApi', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
await testApi(server, client);
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('sendMsg', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
port: 3001,
|
||||
logger: serverLogger,
|
||||
// debugBuf: true
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
server: 'http://127.0.0.1:3001',
|
||||
logger: clientLogger,
|
||||
// debugBuf: true
|
||||
});
|
||||
|
||||
return new Promise(rs => {
|
||||
let msg: MsgChat = {
|
||||
channel: 123,
|
||||
userName: 'fff',
|
||||
content: '666',
|
||||
time: Date.now()
|
||||
};
|
||||
|
||||
server.listenMsg('Chat', async v => {
|
||||
assert.deepStrictEqual(v.msg, msg);
|
||||
await server.stop();
|
||||
rs();
|
||||
});
|
||||
|
||||
client.sendMsg('Chat', msg);
|
||||
})
|
||||
})
|
||||
|
||||
it('abort', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let result: any | undefined;
|
||||
let promise = client.callApi('Test', { name: 'aaaaaaaa' });
|
||||
let sn = client.lastSN;
|
||||
setTimeout(() => {
|
||||
client.abort(sn)
|
||||
}, 10);
|
||||
promise.then(v => {
|
||||
result = v;
|
||||
});
|
||||
|
||||
await new Promise<void>(rs => {
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(result, undefined);
|
||||
rs();
|
||||
}, 150)
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('abortByKey', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let result: any | undefined;
|
||||
let result1: any | undefined;
|
||||
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
|
||||
client.callApi('Test', { name: 'bbbbbb' }).then(v => { result1 = v; });
|
||||
|
||||
setTimeout(() => {
|
||||
client.abortByKey('XXX')
|
||||
}, 10);
|
||||
|
||||
await new Promise<void>(rs => {
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(result, undefined);
|
||||
assert.deepStrictEqual(result1, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: bbbbbb'
|
||||
}
|
||||
})
|
||||
rs();
|
||||
}, 150)
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('abortAll', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let result: any | undefined;
|
||||
let result1: any | undefined;
|
||||
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
client.callApi('Test', { name: 'aaaaaaaa' }, { abortKey: 'XXX' }).then(v => { result = v; });
|
||||
|
||||
client.callApi('Test', { name: 'bbbbbb' }).then(v => { result1 = v; });
|
||||
|
||||
setTimeout(() => {
|
||||
client.abortAll()
|
||||
}, 10);
|
||||
|
||||
await new Promise<void>(rs => {
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(result, undefined);
|
||||
assert.strictEqual(result1, undefined);
|
||||
rs();
|
||||
}, 150)
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('pendingApis', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
let promise = Promise.all(Array.from({ length: 10 }, () => new Promise<void>(rs => {
|
||||
let name = ['Req', 'InnerError', 'TsrpcError', 'error'][Math.random() * 4 | 0];
|
||||
let ret: any | undefined;
|
||||
let promise = client.callApi('Test', { name: name });
|
||||
let sn = client.lastSN;
|
||||
let abort = Math.random() > 0.5;
|
||||
if (abort) {
|
||||
setTimeout(() => {
|
||||
client.abort(sn)
|
||||
}, 0);
|
||||
}
|
||||
promise.then(v => {
|
||||
ret = v;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
client.logger?.log('sn', sn, name, abort, ret)
|
||||
if (abort) {
|
||||
assert.strictEqual(ret, undefined);
|
||||
}
|
||||
else {
|
||||
assert.notEqual(ret, undefined);
|
||||
if (name === 'Req') {
|
||||
assert.strictEqual(ret.isSucc, true);
|
||||
}
|
||||
else {
|
||||
assert.strictEqual(ret.isSucc, false)
|
||||
}
|
||||
}
|
||||
rs();
|
||||
}, 300)
|
||||
})));
|
||||
assert.strictEqual(client['_pendingApis'].length, 10);
|
||||
await promise;
|
||||
assert.strictEqual(client['_pendingApis'].length, 0);
|
||||
}
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let client1 = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
server: 'http://localhost:80',
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let ret = await client1.callApi('Test', { name: 'xx' });
|
||||
console.log(ret);
|
||||
assert.strictEqual(ret.isSucc, false);
|
||||
assert.strictEqual(ret.err?.code, 'ECONNREFUSED');
|
||||
assert.strictEqual(ret.err?.type, TsrpcErrorType.NetworkError);
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('server timeout', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger,
|
||||
apiTimeout: 100
|
||||
});
|
||||
server.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.req && call.succ({
|
||||
reply: 'Hi, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
let ret = await client.callApi('Test', { name: 'Jack' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Server Timeout', {
|
||||
code: 'SERVER_TIMEOUT',
|
||||
type: TsrpcErrorType.ServerError
|
||||
})
|
||||
});
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('client timeout', async function () {
|
||||
let server1 = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
server1.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.succ({
|
||||
reply: 'Hello, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 2000)
|
||||
})
|
||||
})
|
||||
await server1.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
timeout: 100,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'Jack123' });
|
||||
// SERVER TIMEOUT的call还没执行完,但是call却被放入Pool了,导致这个BUG
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError({
|
||||
message: 'Request Timeout',
|
||||
code: 'TIMEOUT',
|
||||
type: TsrpcErrorType.NetworkError
|
||||
})
|
||||
});
|
||||
await server1.stop();
|
||||
});
|
||||
|
||||
it('Graceful stop', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
let reqNum = 0;
|
||||
server.implementApi('Test', async call => {
|
||||
if (++reqNum === 10) {
|
||||
server.gracefulStop();
|
||||
}
|
||||
await new Promise(rs => setTimeout(rs, parseInt(call.req.name)));
|
||||
call.succ({ reply: 'OK' });
|
||||
});
|
||||
|
||||
await server.start();
|
||||
let isStopped = false;
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
})
|
||||
|
||||
let succNum = 0;
|
||||
await Promise.all(Array.from({ length: 10 }, (v, i) => client.callApi('Test', { name: '' + (i * 100) }).then(v => {
|
||||
if (v.res?.reply === 'OK') {
|
||||
++succNum;
|
||||
}
|
||||
})))
|
||||
assert.strictEqual(succNum, 10);
|
||||
})
|
||||
})
|
||||
|
||||
describe('HTTP Flows', function () {
|
||||
it('Server conn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
assert.strictEqual((call.conn as any).xxxx, 'asdfasdf')
|
||||
assert.strictEqual(flowExecResult.postConnectFlow, true);
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, undefined);
|
||||
call.succ({ reply: 'ok' });
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, undefined);
|
||||
});
|
||||
|
||||
server.flows.postConnectFlow.push(v => {
|
||||
flowExecResult.postConnectFlow = true;
|
||||
(v as any).xxxx = 'asdfasdf';
|
||||
return v;
|
||||
});
|
||||
server.flows.postDisconnectFlow.push(v => {
|
||||
flowExecResult.postDisconnectFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
assert.strictEqual(flowExecResult.postConnectFlow, undefined);
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, undefined);
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postConnectFlow, true);
|
||||
assert.strictEqual(flowExecResult.postDisconnectFlow, true);
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('Buffer enc/dec flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'Enc&Dec' });
|
||||
});
|
||||
|
||||
server.flows.preRecvDataFlow.push(v => {
|
||||
flowExecResult.preRecvDataFlow = true;
|
||||
v.data = (v.data as string).split('').reverse().join('');
|
||||
return v;
|
||||
});
|
||||
server.flows.preSendDataFlow.push(v => {
|
||||
flowExecResult.preSendDataFlow = true;
|
||||
v.data = (v.data as string).split('').reverse().join('');
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preSendDataFlow.push(v => {
|
||||
v.data = (v.data as string).split('').reverse().join('');
|
||||
return v;
|
||||
});
|
||||
|
||||
client.flows.preRecvDataFlow.push(v => {
|
||||
v.data = (v.data as string).split('').reverse().join('');
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preRecvDataFlow, true);
|
||||
assert.strictEqual(flowExecResult.preSendDataFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Enc&Dec'
|
||||
}
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('ApiCall flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return call;
|
||||
});
|
||||
server.flows.postApiCallFlow.push(v => {
|
||||
flowExecResult.postApiCallFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preCallApiFlow.push(v => {
|
||||
if (v.apiName !== 'ObjId') {
|
||||
v.req.name = 'Changed'
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postApiCallFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('ApiCall flow break', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return undefined;
|
||||
});
|
||||
server.flows.postApiCallFlow.push(v => {
|
||||
flowExecResult.postApiCallFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preCallApiFlow.push(v => {
|
||||
if (v.apiName !== 'ObjId') {
|
||||
v.req.name = 'Changed'
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postApiCallFlow, undefined);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('ApiCall flow error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
throw new Error('ASDFASDF')
|
||||
});
|
||||
server.flows.postApiCallFlow.push(v => {
|
||||
flowExecResult.postApiCallFlow = true;
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preCallApiFlow.push(v => {
|
||||
if (v.apiName !== 'ObjId') {
|
||||
v.req.name = 'Changed'
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.postApiCallFlow, undefined);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: 'ASDFASDF',
|
||||
code: 'INTERNAL_ERR'
|
||||
})
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('server ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
server.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
}
|
||||
return v;
|
||||
});
|
||||
server.flows.postApiReturnFlow.push(v => {
|
||||
flowExecResult.postApiReturnFlow = true;
|
||||
v.call.logger.log('RETTT', v.return);
|
||||
return v;
|
||||
})
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.strictEqual(flowExecResult.postApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('client ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof HttpClient<any>['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
}
|
||||
return v;
|
||||
});
|
||||
client.flows.postApiReturnFlow.push(v => {
|
||||
flowExecResult.postApiReturnFlow = true;
|
||||
client.logger?.log('RETTT', v.return);
|
||||
return v;
|
||||
})
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.strictEqual(flowExecResult.postApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('client SendBufferFlow prevent', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
// const flowExecResult: { [K in (keyof BaseClient<any>['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
client.flows.preSendDataFlow.push(v => {
|
||||
return undefined
|
||||
});
|
||||
|
||||
let ret: any;
|
||||
client.callApi('Test', { name: 'xxx' }).then(v => { ret = v });
|
||||
await new Promise(rs => { setTimeout(rs, 200) });
|
||||
assert.strictEqual(ret, undefined)
|
||||
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
it('onInputBufferError', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
client.flows.preSendDataFlow.push(v => {
|
||||
v.data = (v.data as string).split('').reverse().join('');
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 'XXX' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Input is not a valid JSON string: Unexpected token } in JSON at position 0', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
code: 'INPUT_DATA_ERR'
|
||||
})
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('throw type error in client', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
let ret = await client.callApi('Test', { name: 23456 } as any);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError({
|
||||
"code": "INPUT_DATA_ERR",
|
||||
"message": "Property `name`: Expected type to be `string`, actually `number`.",
|
||||
"type": TsrpcErrorType.ClientError
|
||||
})
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('throw type error in server', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.start();
|
||||
|
||||
let retHttp = await new HttpProxy().fetch({
|
||||
url: 'http://127.0.0.1:3000/Test',
|
||||
data: JSON.stringify({ name: 12345 }),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json; charset=utf-8'
|
||||
},
|
||||
transportOptions: {},
|
||||
responseType: 'text'
|
||||
}).promise;
|
||||
|
||||
assert.deepStrictEqual(JSON.parse((retHttp as any).res), {
|
||||
isSucc: false,
|
||||
err: {
|
||||
"code": "INPUT_DATA_ERR",
|
||||
"message": "Property `name`: Expected type to be `string`, actually `number`.",
|
||||
"type": TsrpcErrorType.ServerError
|
||||
}
|
||||
})
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
|
||||
it('ObjectId', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
json: true,
|
||||
logger: serverLogger
|
||||
});
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
await server.start();
|
||||
|
||||
let client = new HttpClient(getProto(), {
|
||||
json: true,
|
||||
logger: clientLogger
|
||||
});
|
||||
|
||||
// ObjectId
|
||||
let objId1 = new ObjectId();
|
||||
let ret = await client.callApi('ObjId', {
|
||||
id1: objId1
|
||||
});
|
||||
assert.strictEqual(ret.isSucc, true, ret.err?.message);
|
||||
assert.strictEqual(objId1.toString(), ret.res!.id2.toString());
|
||||
|
||||
await server.stop();
|
||||
})
|
||||
})
|
||||
396
test/cases/inner.test.ts
Normal file
396
test/cases/inner.test.ts
Normal file
@@ -0,0 +1,396 @@
|
||||
import { ObjectId } from 'bson';
|
||||
import { assert } from 'chai';
|
||||
import chalk from 'chalk';
|
||||
import * as path from "path";
|
||||
import { ServiceProto, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
|
||||
import { ApiCall, BaseServer, HttpConnection, MsgCall, TerminalColorLogger } from '../../src';
|
||||
import { HttpServer } from '../../src/server/http/HttpServer';
|
||||
import { PrefixLogger } from '../../src/server/models/PrefixLogger';
|
||||
import { ApiTest as ApiAbcTest } from '../api/a/b/c/ApiTest';
|
||||
import { ApiTest } from '../api/ApiTest';
|
||||
import { MsgChat } from '../proto/MsgChat';
|
||||
import { ReqTest, ResTest } from '../proto/PtlTest';
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
|
||||
const serverLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgGreen.white(' Server ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Server' })
|
||||
});
|
||||
|
||||
const getProto = () => Object.merge({}, serviceProto) as ServiceProto<ServiceType>;
|
||||
|
||||
async function testApi(server: HttpServer<ServiceType>) {
|
||||
// Succ
|
||||
assert.deepStrictEqual(await server.callApi('Test', {
|
||||
name: 'Req1'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: Req1'
|
||||
}
|
||||
});
|
||||
assert.deepStrictEqual(await server.callApi('a/b/c/Test', {
|
||||
name: 'Req2'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'a/b/c/Test reply: Req2'
|
||||
}
|
||||
});
|
||||
|
||||
// Inner error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await server.callApi(v as any, {
|
||||
name: 'InnerError'
|
||||
});
|
||||
delete ret.err!.innerErr.stack;
|
||||
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
code: 'INTERNAL_ERR',
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: `${v} InnerError`
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// TsrpcError
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await server.callApi(v as any, {
|
||||
name: 'TsrpcError'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError(`${v} TsrpcError`, {
|
||||
code: 'CODE_TEST',
|
||||
type: TsrpcErrorType.ApiError,
|
||||
info: 'ErrInfo ' + v
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// call.error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await server.callApi(v as any, {
|
||||
name: 'error'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Got an error', {
|
||||
type: TsrpcErrorType.ApiError
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('HTTP Server & Client basic', function () {
|
||||
it('implement API manually', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
|
||||
server.implementApi('Test', ApiTest);
|
||||
server.implementApi('a/b/c/Test', ApiAbcTest);
|
||||
|
||||
await testApi(server);
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('extend call in handler', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
|
||||
server.implementApi('Test', (call: MyApiCall<ReqTest, ResTest>) => {
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
});
|
||||
server.listenMsg('Chat', (call: MyMsgCall<MsgChat>) => {
|
||||
call.msg.content;
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
})
|
||||
})
|
||||
|
||||
it('extend call in flow', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyConn = HttpConnection<any> & {
|
||||
currentUser: {
|
||||
uid: string,
|
||||
nickName: string
|
||||
}
|
||||
}
|
||||
|
||||
server.flows.postConnectFlow.push((conn: MyConn) => {
|
||||
conn.currentUser.nickName = 'asdf';
|
||||
return conn;
|
||||
});
|
||||
server.flows.postConnectFlow.exec(null as any as MyConn, console);
|
||||
server.flows.preApiCallFlow.push((call: MyApiCall<any, any>) => {
|
||||
call.value2 = 'x';
|
||||
return call;
|
||||
});
|
||||
server.flows.preSendMsgFlow.push((call: MyMsgCall<any>) => {
|
||||
call.value2 = 'f';
|
||||
return call;
|
||||
})
|
||||
})
|
||||
|
||||
it('autoImplementApi', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
|
||||
await server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
await testApi(server);
|
||||
});
|
||||
|
||||
it('autoImplementApi delay', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'), true)
|
||||
|
||||
await testApi(server);
|
||||
});
|
||||
|
||||
it('error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
|
||||
let ret = await server.callApi('TesASFt' as any, { name: 'xx' } as any);
|
||||
console.log(ret);
|
||||
assert.strictEqual(ret.isSucc, false);
|
||||
assert.strictEqual(ret.err?.code, 'ERR_API_NAME');
|
||||
assert.strictEqual(ret.err?.type, TsrpcErrorType.ServerError);
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('server timeout', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 100
|
||||
});
|
||||
server.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.req && call.succ({
|
||||
reply: 'Hi, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
let ret = await server.callApi('Test', { name: 'Jack' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Server Timeout', {
|
||||
code: 'SERVER_TIMEOUT',
|
||||
type: TsrpcErrorType.ServerError
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('Graceful stop', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
let reqNum = 0;
|
||||
server.implementApi('Test', async call => {
|
||||
if (++reqNum === 10) {
|
||||
server.gracefulStop();
|
||||
}
|
||||
await new Promise(rs => setTimeout(rs, parseInt(call.req.name)));
|
||||
call.succ({ reply: 'OK' });
|
||||
});
|
||||
|
||||
|
||||
let isStopped = false;
|
||||
|
||||
let succNum = 0;
|
||||
await Promise.all(Array.from({ length: 10 }, (v, i) => server.callApi('Test', { name: '' + (i * 100) }).then(v => {
|
||||
if (v.res?.reply === 'OK') {
|
||||
++succNum;
|
||||
}
|
||||
})))
|
||||
assert.strictEqual(succNum, 10);
|
||||
})
|
||||
})
|
||||
|
||||
describe('HTTP Flows', function () {
|
||||
it('ApiCall flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
if (call.req.apiName !== 'ObjId') {
|
||||
call.req.name = 'Changed'
|
||||
}
|
||||
return call;
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return call;
|
||||
});
|
||||
|
||||
let ret = await server.callApi('Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
});
|
||||
|
||||
it('ApiCall flow break', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
call.error('You need login');
|
||||
return undefined;
|
||||
});
|
||||
|
||||
let ret = await server.callApi('Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('ApiCall flow error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
throw new Error('ASDFASDF')
|
||||
});
|
||||
|
||||
let ret = await server.callApi('Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: 'ASDFASDF',
|
||||
code: 'INTERNAL_ERR'
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('server ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
server.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await server.callApi('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('Extended JSON Types', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let buf = new Uint8Array([0, 1, 2, 3, 255, 254, 253, 252]);
|
||||
let date = new Date('2021/11/17');
|
||||
|
||||
// ObjectId
|
||||
let objId1 = new ObjectId();
|
||||
let ret = await server.callApi('ObjId', {
|
||||
id1: objId1,
|
||||
buf: buf,
|
||||
date: date
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
id2: objId1,
|
||||
buf: buf,
|
||||
date: date
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
414
test/cases/inputBuffer.test.ts
Normal file
414
test/cases/inputBuffer.test.ts
Normal file
@@ -0,0 +1,414 @@
|
||||
import { ObjectId } from 'bson';
|
||||
import { assert } from 'chai';
|
||||
import chalk from 'chalk';
|
||||
import * as path from "path";
|
||||
import { ServiceProto, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
|
||||
import { ApiCall, ApiService, BaseServer, HttpConnection, MsgCall, TerminalColorLogger, TransportDataUtil } from '../../src';
|
||||
import { HttpServer } from '../../src/server/http/HttpServer';
|
||||
import { PrefixLogger } from '../../src/server/models/PrefixLogger';
|
||||
import { ApiTest as ApiAbcTest } from '../api/a/b/c/ApiTest';
|
||||
import { ApiTest } from '../api/ApiTest';
|
||||
import { MsgChat } from '../proto/MsgChat';
|
||||
import { ReqTest, ResTest } from '../proto/PtlTest';
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
|
||||
const serverLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgGreen.white(' Server ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Server' })
|
||||
});
|
||||
|
||||
async function inputBuffer(server: BaseServer<any>, apiName: string, req: any) {
|
||||
let apiSvc: ApiService | undefined = server.serviceMap.apiName2Service[apiName];
|
||||
let inputBuf = apiSvc ? (await TransportDataUtil.encodeApiReq(server.tsbuffer, apiSvc, req, 'buffer')).output : new Uint8Array([0, 1, 2, 3, 255, 254, 253, 252]);
|
||||
if (!inputBuf) {
|
||||
throw new Error('failed to encode inputBuf')
|
||||
}
|
||||
|
||||
let retBuf = await server.inputBuffer(inputBuf);
|
||||
assert.ok(retBuf instanceof Uint8Array)
|
||||
|
||||
let opDecode = await TransportDataUtil.parseServerOutout(server.tsbuffer, server.serviceMap, retBuf, apiSvc?.id ?? 0);
|
||||
if (!opDecode.isSucc) {
|
||||
throw new Error('decode buf failed')
|
||||
}
|
||||
if (opDecode.result.type !== 'api') {
|
||||
throw new Error('decode result is not api')
|
||||
}
|
||||
return opDecode.result.ret;
|
||||
}
|
||||
|
||||
const getProto = () => Object.merge({}, serviceProto) as ServiceProto<ServiceType>;
|
||||
|
||||
async function testApi(server: HttpServer<ServiceType>) {
|
||||
// Succ
|
||||
assert.deepStrictEqual(await inputBuffer(server, 'Test', {
|
||||
name: 'Req1'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: Req1'
|
||||
}
|
||||
});
|
||||
assert.deepStrictEqual(await inputBuffer(server, 'a/b/c/Test', {
|
||||
name: 'Req2'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'a/b/c/Test reply: Req2'
|
||||
}
|
||||
});
|
||||
|
||||
// Inner error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await inputBuffer(server, v as any, {
|
||||
name: 'InnerError'
|
||||
});
|
||||
delete ret.err!.innerErr.stack;
|
||||
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
code: 'INTERNAL_ERR',
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: `${v} InnerError`
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// TsrpcError
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await inputBuffer(server, v as any, {
|
||||
name: 'TsrpcError'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError(`${v} TsrpcError`, {
|
||||
code: 'CODE_TEST',
|
||||
type: TsrpcErrorType.ApiError,
|
||||
info: 'ErrInfo ' + v
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// call.error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await inputBuffer(server, v as any, {
|
||||
name: 'error'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Got an error', {
|
||||
type: TsrpcErrorType.ApiError
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('HTTP Server & Client basic', function () {
|
||||
it('implement API manually', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
|
||||
server.implementApi('Test', ApiTest);
|
||||
server.implementApi('a/b/c/Test', ApiAbcTest);
|
||||
|
||||
await testApi(server);
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('extend call in handler', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
|
||||
server.implementApi('Test', (call: MyApiCall<ReqTest, ResTest>) => {
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
});
|
||||
server.listenMsg('Chat', (call: MyMsgCall<MsgChat>) => {
|
||||
call.msg.content;
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
})
|
||||
})
|
||||
|
||||
it('extend call in flow', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyConn = HttpConnection<any> & {
|
||||
currentUser: {
|
||||
uid: string,
|
||||
nickName: string
|
||||
}
|
||||
}
|
||||
|
||||
server.flows.postConnectFlow.push((conn: MyConn) => {
|
||||
conn.currentUser.nickName = 'asdf';
|
||||
return conn;
|
||||
});
|
||||
server.flows.postConnectFlow.exec(null as any as MyConn, console);
|
||||
server.flows.preApiCallFlow.push((call: MyApiCall<any, any>) => {
|
||||
call.value2 = 'x';
|
||||
return call;
|
||||
});
|
||||
server.flows.preSendMsgFlow.push((call: MyMsgCall<any>) => {
|
||||
call.value2 = 'f';
|
||||
return call;
|
||||
})
|
||||
})
|
||||
|
||||
it('autoImplementApi', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
|
||||
await server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
await testApi(server);
|
||||
});
|
||||
|
||||
it('autoImplementApi delay', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'), true)
|
||||
|
||||
await testApi(server);
|
||||
});
|
||||
|
||||
it('error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
|
||||
let ret = await inputBuffer(server, 'TesASFt' as any, { name: 'xx' } as any);
|
||||
console.log(ret);
|
||||
assert.strictEqual(ret.isSucc, false);
|
||||
assert.strictEqual(ret.err?.code, 'INPUT_DATA_ERR');
|
||||
assert.strictEqual(ret.err?.type, TsrpcErrorType.ServerError);
|
||||
})
|
||||
|
||||
it('server timeout', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 100
|
||||
});
|
||||
server.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.req && call.succ({
|
||||
reply: 'Hi, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
let ret = await inputBuffer(server, 'Test', { name: 'Jack' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Server Timeout', {
|
||||
code: 'SERVER_TIMEOUT',
|
||||
type: TsrpcErrorType.ServerError
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('Graceful stop', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
let reqNum = 0;
|
||||
server.implementApi('Test', async call => {
|
||||
if (++reqNum === 10) {
|
||||
server.gracefulStop();
|
||||
}
|
||||
await new Promise(rs => setTimeout(rs, parseInt(call.req.name)));
|
||||
call.succ({ reply: 'OK' });
|
||||
});
|
||||
|
||||
|
||||
let isStopped = false;
|
||||
|
||||
let succNum = 0;
|
||||
await Promise.all(Array.from({ length: 10 }, (v, i) => inputBuffer(server, 'Test', { name: '' + (i * 100) }).then((v: any) => {
|
||||
if (v.res?.reply === 'OK') {
|
||||
++succNum;
|
||||
}
|
||||
})))
|
||||
assert.strictEqual(succNum, 10);
|
||||
})
|
||||
})
|
||||
|
||||
describe('HTTP Flows', function () {
|
||||
it('ApiCall flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
if (call.req.apiName !== 'ObjId') {
|
||||
call.req.name = 'Changed'
|
||||
}
|
||||
return call;
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return call;
|
||||
});
|
||||
|
||||
let ret = await inputBuffer(server, 'Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
});
|
||||
|
||||
it('ApiCall flow break', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
call.error('You need login');
|
||||
return undefined;
|
||||
});
|
||||
|
||||
let ret = await inputBuffer(server, 'Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('You need login')
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('ApiCall flow error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
throw new Error('ASDFASDF')
|
||||
});
|
||||
|
||||
let ret = await inputBuffer(server, 'Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Internal Server Error', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: 'ASDFASDF',
|
||||
code: 'INTERNAL_ERR'
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('server ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
server.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await inputBuffer(server, 'Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: new TsrpcError('Ret changed')
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('Extended JSON Types', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let buf = new Uint8Array([0, 1, 2, 3, 255, 254, 253, 252]);
|
||||
let date = new Date('2021/11/17');
|
||||
|
||||
// ObjectId
|
||||
let objId1 = new ObjectId();
|
||||
let ret = await inputBuffer(server, 'ObjId', {
|
||||
id1: objId1,
|
||||
buf: buf,
|
||||
date: date
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
id2: objId1,
|
||||
buf: buf,
|
||||
date: date
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
410
test/cases/inputJSON.test.ts
Normal file
410
test/cases/inputJSON.test.ts
Normal file
@@ -0,0 +1,410 @@
|
||||
import { ObjectId } from 'bson';
|
||||
import { assert } from 'chai';
|
||||
import chalk from 'chalk';
|
||||
import * as path from "path";
|
||||
import { Base64Util } from 'tsbuffer';
|
||||
import { ServiceProto, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
|
||||
import { ApiCall, BaseServer, HttpConnection, MsgCall, TerminalColorLogger } from '../../src';
|
||||
import { HttpServer } from '../../src/server/http/HttpServer';
|
||||
import { PrefixLogger } from '../../src/server/models/PrefixLogger';
|
||||
import { ApiTest as ApiAbcTest } from '../api/a/b/c/ApiTest';
|
||||
import { ApiTest } from '../api/ApiTest';
|
||||
import { MsgChat } from '../proto/MsgChat';
|
||||
import { ReqTest, ResTest } from '../proto/PtlTest';
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
|
||||
const serverLogger = new PrefixLogger({
|
||||
prefixs: [chalk.bgGreen.white(' Server ')],
|
||||
logger: new TerminalColorLogger({ pid: 'Server' })
|
||||
});
|
||||
|
||||
const getProto = () => Object.merge({}, serviceProto) as ServiceProto<ServiceType>;
|
||||
|
||||
async function testApi(server: HttpServer<ServiceType>) {
|
||||
// Succ
|
||||
assert.deepStrictEqual(await server.inputJSON('Test', {
|
||||
name: 'Req1'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'Test reply: Req1'
|
||||
}
|
||||
});
|
||||
assert.deepStrictEqual(await server.inputJSON('/a/b/c/Test', {
|
||||
name: 'Req2'
|
||||
}), {
|
||||
isSucc: true,
|
||||
res: {
|
||||
reply: 'a/b/c/Test reply: Req2'
|
||||
}
|
||||
});
|
||||
|
||||
// Inner error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await server.inputJSON(v as any, {
|
||||
name: 'InnerError'
|
||||
});
|
||||
delete ret.err!.innerErr.stack;
|
||||
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError('Internal Server Error', {
|
||||
code: 'INTERNAL_ERR',
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: `${v} InnerError`
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TsrpcError
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await server.inputJSON(v as any, {
|
||||
name: 'TsrpcError'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError(`${v} TsrpcError`, {
|
||||
code: 'CODE_TEST',
|
||||
type: TsrpcErrorType.ApiError,
|
||||
info: 'ErrInfo ' + v
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// call.error
|
||||
for (let v of ['Test', 'a/b/c/Test']) {
|
||||
let ret = await server.inputJSON(v as any, {
|
||||
name: 'error'
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError('Got an error', {
|
||||
type: TsrpcErrorType.ApiError
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('HTTP Server & Client basic', function () {
|
||||
it('implement API manually', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
|
||||
server.implementApi('Test', ApiTest);
|
||||
server.implementApi('a/b/c/Test', ApiAbcTest);
|
||||
|
||||
await testApi(server);
|
||||
|
||||
|
||||
})
|
||||
|
||||
it('extend call in handler', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
|
||||
server.implementApi('Test', (call: MyApiCall<ReqTest, ResTest>) => {
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
});
|
||||
server.listenMsg('Chat', (call: MyMsgCall<MsgChat>) => {
|
||||
call.msg.content;
|
||||
call.value1 = 'xxx';
|
||||
call.value2 = 'xxx';
|
||||
})
|
||||
})
|
||||
|
||||
it('extend call in flow', function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
debugBuf: true
|
||||
});
|
||||
|
||||
type MyApiCall<Req, Res> = ApiCall<Req, Res> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyMsgCall<Msg> = MsgCall<Msg> & {
|
||||
value1?: string;
|
||||
value2: string;
|
||||
}
|
||||
type MyConn = HttpConnection<any> & {
|
||||
currentUser: {
|
||||
uid: string,
|
||||
nickName: string
|
||||
}
|
||||
}
|
||||
|
||||
server.flows.postConnectFlow.push((conn: MyConn) => {
|
||||
conn.currentUser.nickName = 'asdf';
|
||||
return conn;
|
||||
});
|
||||
server.flows.postConnectFlow.exec(null as any as MyConn, console);
|
||||
server.flows.preApiCallFlow.push((call: MyApiCall<any, any>) => {
|
||||
call.value2 = 'x';
|
||||
return call;
|
||||
});
|
||||
server.flows.preSendMsgFlow.push((call: MyMsgCall<any>) => {
|
||||
call.value2 = 'f';
|
||||
return call;
|
||||
})
|
||||
})
|
||||
|
||||
it('autoImplementApi', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
|
||||
await server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
await testApi(server);
|
||||
});
|
||||
|
||||
it('autoImplementApi delay', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 5000
|
||||
});
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, '../api'), true)
|
||||
|
||||
await testApi(server);
|
||||
});
|
||||
|
||||
it('error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
|
||||
let ret = await server.inputJSON('TesASFt' as any, { name: 'xx' } as any);
|
||||
console.log(ret);
|
||||
assert.strictEqual(ret.isSucc, false);
|
||||
assert.strictEqual(ret.err?.message, 'Invalid service name: TesASFt');
|
||||
assert.strictEqual(ret.err?.code, 'INPUT_DATA_ERR');
|
||||
assert.strictEqual(ret.err?.type, TsrpcErrorType.ServerError);
|
||||
})
|
||||
|
||||
it('server timeout', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger,
|
||||
apiTimeout: 100
|
||||
});
|
||||
server.implementApi('Test', call => {
|
||||
return new Promise(rs => {
|
||||
setTimeout(() => {
|
||||
call.req && call.succ({
|
||||
reply: 'Hi, ' + call.req.name
|
||||
});
|
||||
rs();
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
let ret = await server.inputJSON('Test', { name: 'Jack' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError('Server Timeout', {
|
||||
code: 'SERVER_TIMEOUT',
|
||||
type: TsrpcErrorType.ServerError
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('Graceful stop', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
let reqNum = 0;
|
||||
server.implementApi('Test', async call => {
|
||||
if (++reqNum === 10) {
|
||||
server.gracefulStop();
|
||||
}
|
||||
await new Promise(rs => setTimeout(rs, parseInt(call.req.name)));
|
||||
call.succ({ reply: 'OK' });
|
||||
});
|
||||
|
||||
|
||||
let isStopped = false;
|
||||
|
||||
let succNum = 0;
|
||||
await Promise.all(Array.from({ length: 10 }, (v, i) => server.inputJSON('Test', { name: '' + (i * 100) }).then((v: any) => {
|
||||
if (v.res?.reply === 'OK') {
|
||||
++succNum;
|
||||
}
|
||||
})))
|
||||
assert.strictEqual(succNum, 10);
|
||||
})
|
||||
})
|
||||
|
||||
describe('HTTP Flows', function () {
|
||||
it('ApiCall flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
if (call.req.apiName !== 'ObjId') {
|
||||
call.req.name = 'Changed'
|
||||
}
|
||||
return call;
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
assert.strictEqual(call.req.name, 'Changed')
|
||||
call.error('You need login');
|
||||
return call;
|
||||
});
|
||||
|
||||
let ret = await server.inputJSON('Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError('You need login')
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('ApiCall flow break', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
call.error('You need login');
|
||||
return undefined;
|
||||
});
|
||||
|
||||
let ret = await server.inputJSON('Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError('You need login')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('ApiCall flow error', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'asdgasdgasdgasdg' });
|
||||
});
|
||||
|
||||
server.flows.preApiCallFlow.push(call => {
|
||||
throw new Error('ASDFASDF')
|
||||
});
|
||||
|
||||
let ret = await server.inputJSON('Test', { name: 'xxx' });
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: {
|
||||
...new TsrpcError('Internal Server Error', {
|
||||
type: TsrpcErrorType.ServerError,
|
||||
innerErr: 'ASDFASDF',
|
||||
code: 'INTERNAL_ERR'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('server ApiReturn flow', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
|
||||
const flowExecResult: { [K in (keyof BaseServer['flows'])]?: boolean } = {};
|
||||
|
||||
server.implementApi('Test', async call => {
|
||||
call.succ({ reply: 'xxxxxxxxxxxxxxxxxxxx' });
|
||||
});
|
||||
|
||||
server.flows.preApiReturnFlow.push(v => {
|
||||
flowExecResult.preApiReturnFlow = true;
|
||||
v.return = {
|
||||
isSucc: false,
|
||||
err: { ...new TsrpcError('Ret changed') }
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
let ret = await server.inputJSON('Test', { name: 'xxx' });
|
||||
assert.strictEqual(flowExecResult.preApiReturnFlow, true);
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: false,
|
||||
err: { ...new TsrpcError('Ret changed') }
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('Extended JSON Types', async function () {
|
||||
let server = new HttpServer(getProto(), {
|
||||
logger: serverLogger
|
||||
});
|
||||
await server.autoImplementApi(path.resolve(__dirname, '../api'))
|
||||
|
||||
let buf = new Uint8Array([0, 1, 2, 3, 255, 254, 253, 252]);
|
||||
let date = new Date('2021/11/17');
|
||||
|
||||
// ObjectId
|
||||
let objId1 = new ObjectId();
|
||||
let ret = await server.inputJSON('ObjId', {
|
||||
id1: objId1,
|
||||
buf: buf,
|
||||
date: date
|
||||
});
|
||||
assert.deepStrictEqual(ret, {
|
||||
isSucc: true,
|
||||
res: {
|
||||
id2: objId1.toHexString(),
|
||||
buf: Base64Util.bufferToBase64(buf),
|
||||
date: date.toJSON()
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
1051
test/cases/ws.test.ts
Normal file
1051
test/cases/ws.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
1278
test/cases/wsJSON.test.ts
Normal file
1278
test/cases/wsJSON.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
6
test/proto/MsgChat.ts
Normal file
6
test/proto/MsgChat.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface MsgChat {
|
||||
channel: number,
|
||||
userName: string,
|
||||
content: string,
|
||||
time: number
|
||||
}
|
||||
14
test/proto/PtlObjId.ts
Normal file
14
test/proto/PtlObjId.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// @ts-ignore
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
export interface ReqObjId {
|
||||
id1: ObjectId;
|
||||
buf?: Uint8Array,
|
||||
date?: Date
|
||||
}
|
||||
|
||||
export interface ResObjId {
|
||||
id2: ObjectId;
|
||||
buf?: Uint8Array,
|
||||
date?: Date
|
||||
}
|
||||
7
test/proto/PtlTest.ts
Normal file
7
test/proto/PtlTest.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface ReqTest {
|
||||
name: string
|
||||
};
|
||||
|
||||
export type ResTest = {
|
||||
reply: string
|
||||
};
|
||||
10
test/proto/a/b/c/PtlTest.ts
Normal file
10
test/proto/a/b/c/PtlTest.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { MsgChat } from '../../../MsgChat';
|
||||
|
||||
export interface ReqTest {
|
||||
name: string
|
||||
};
|
||||
|
||||
export type ResTest = {
|
||||
reply: string,
|
||||
chat?: MsgChat
|
||||
};
|
||||
203
test/proto/serviceProto.ts
Normal file
203
test/proto/serviceProto.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { ServiceProto } from 'tsrpc-proto';
|
||||
import { ReqTest, ResTest } from './a/b/c/PtlTest';
|
||||
import { MsgChat } from './MsgChat';
|
||||
import { ReqObjId, ResObjId } from './PtlObjId';
|
||||
import { ReqTest as ReqTest_1, ResTest as ResTest_1 } from './PtlTest';
|
||||
|
||||
export interface ServiceType {
|
||||
api: {
|
||||
"a/b/c/Test": {
|
||||
req: ReqTest,
|
||||
res: ResTest
|
||||
},
|
||||
"ObjId": {
|
||||
req: ReqObjId,
|
||||
res: ResObjId
|
||||
},
|
||||
"Test": {
|
||||
req: ReqTest_1,
|
||||
res: ResTest_1
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
"Chat": MsgChat
|
||||
}
|
||||
}
|
||||
|
||||
export const serviceProto: ServiceProto<ServiceType> = {
|
||||
"version": 1,
|
||||
"services": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "a/b/c/Test",
|
||||
"type": "api"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Chat",
|
||||
"type": "msg"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "ObjId",
|
||||
"type": "api"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Test",
|
||||
"type": "api"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
"a/b/c/PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"a/b/c/PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "reply",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "chat",
|
||||
"type": {
|
||||
"type": "Reference",
|
||||
"target": "MsgChat/MsgChat"
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"MsgChat/MsgChat": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "channel",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "userName",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "content",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "time",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlObjId/ReqObjId": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "id1",
|
||||
"type": {
|
||||
"type": "Reference",
|
||||
"target": "?mongodb/ObjectId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "buf",
|
||||
"type": {
|
||||
"type": "Buffer",
|
||||
"arrayType": "Uint8Array"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "date",
|
||||
"type": {
|
||||
"type": "Date"
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlObjId/ResObjId": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "id2",
|
||||
"type": {
|
||||
"type": "Reference",
|
||||
"target": "?mongodb/ObjectId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "buf",
|
||||
"type": {
|
||||
"type": "Buffer",
|
||||
"arrayType": "Uint8Array"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "date",
|
||||
"type": {
|
||||
"type": "Date"
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "reply",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
18
test/test.ts
Normal file
18
test/test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { HttpServer } from '../src/server/http/HttpServer';
|
||||
import { serviceProto } from './proto/serviceProto';
|
||||
|
||||
let server = new HttpServer(serviceProto, {
|
||||
jsonEnabled: true,
|
||||
jsonRootPath: 'api'
|
||||
});
|
||||
|
||||
server.implementApi('a/b/c/Test', call => {
|
||||
call.logger.log('xxx', call.req);
|
||||
call.succ({
|
||||
reply: 'xxxxxxxxxxx',
|
||||
aasdg: 'd',
|
||||
b: 'asdg'
|
||||
} as any)
|
||||
});
|
||||
|
||||
server.start();
|
||||
22
test/try/client/http.ts
Normal file
22
test/try/client/http.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { HttpClient } from '../../src/client/http/HttpClient';
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
let client = new HttpClient<ServiceType>({
|
||||
server: 'http://localhost:3000',
|
||||
proto: serviceProto
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const P = 50, N = 1000;
|
||||
let max = 0;
|
||||
console.time(`test ${P}/${N}`);
|
||||
for (let i = 0, len = N / P; i < len; ++i) {
|
||||
let res = await Promise.all(Array.from({ length: P }, () => {
|
||||
let start = Date.now();
|
||||
return client.callApi('a/b/c/Test', { name: '123' }).then(() => Date.now() - start);
|
||||
}));
|
||||
max = Math.max(res.max(), max)
|
||||
}
|
||||
console.timeEnd(`test ${P}/${N}`);
|
||||
console.log('max', max)
|
||||
}
|
||||
main();
|
||||
82
test/try/client/ws.ts
Normal file
82
test/try/client/ws.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
import { WsClient } from '../../src/client/ws/WsClient';
|
||||
import SuperPromise from 'k8w-super-promise';
|
||||
import { Func } from 'mocha';
|
||||
|
||||
async function main() {
|
||||
let client = new WsClient({
|
||||
server: 'ws://127.0.0.1:3000',
|
||||
proto: serviceProto,
|
||||
onStatusChange: v => {
|
||||
console.log('StatusChange', v);
|
||||
},
|
||||
// onLostConnection: () => {
|
||||
// console.log('连接断开,2秒后重连');
|
||||
// setTimeout(() => {
|
||||
// client.connect().catch(() => { });
|
||||
// }, 2000)
|
||||
// }
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
let cancel = client.callApi('Test', { name: 'XXXXXXXXXXXXX' }).catch(e => e);
|
||||
cancel.cancel();
|
||||
|
||||
let res = await client.callApi('Test', { name: '小明同学' }).catch(e => e);
|
||||
console.log('Test Res', res);
|
||||
|
||||
res = await client.callApi('a/b/c/Test', { name: '小明同学' }).catch(e => e);
|
||||
console.log('Test1 Res', res);
|
||||
|
||||
// setInterval(async () => {
|
||||
// try {
|
||||
// let res = await client.callApi('Test', { name: '小明同学' });
|
||||
// console.log('收到回复', res);
|
||||
// }
|
||||
// catch (e) {
|
||||
// if (e.info === 'NETWORK_ERR') {
|
||||
// return;
|
||||
// }
|
||||
// console.log('API错误', e)
|
||||
// }
|
||||
// }, 1000);
|
||||
|
||||
// client.listenMsg('Chat', msg => {
|
||||
// console.log('收到MSG', msg);
|
||||
// });
|
||||
|
||||
// setInterval(() => {
|
||||
// try {
|
||||
// client.sendMsg('Chat', {
|
||||
// channel: 123,
|
||||
// userName: '王小明',
|
||||
// content: '你好',
|
||||
// time: Date.now()
|
||||
// }).catch(e => {
|
||||
// console.log('SendMsg Failed', e.message)
|
||||
// })
|
||||
// }
|
||||
// catch{ }
|
||||
// }, 1000)
|
||||
|
||||
// #region Benchmark
|
||||
// let maxTime = 0;
|
||||
// let done = 0;
|
||||
// let startTime = Date.now();
|
||||
|
||||
// setTimeout(() => {
|
||||
// console.log('done', maxTime, done);
|
||||
// process.exit();
|
||||
// }, 3000);
|
||||
|
||||
// for (let i = 0; i < 10000; ++i) {
|
||||
// client.callApi('Test', { name: '小明同学' }).then(() => {
|
||||
// ++done;
|
||||
// maxTime = Math.max(maxTime, Date.now() - startTime)
|
||||
// })
|
||||
// }
|
||||
// #endregion
|
||||
}
|
||||
|
||||
main();
|
||||
27
test/try/massive.ts
Normal file
27
test/try/massive.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { TSRPCClient } from "..";
|
||||
import { serviceProto, ServiceType } from './proto/serviceProto';
|
||||
|
||||
async function main() {
|
||||
setInterval(() => {
|
||||
for (let i = 0; i < 100; ++i) {
|
||||
let client = new TSRPCClient<ServiceType>({
|
||||
server: 'ws://127.0.0.1:3000',
|
||||
proto: serviceProto
|
||||
});
|
||||
|
||||
client.connect().then(() => {
|
||||
client.callApi('a/b/c/Test1', { name: '小明同学' }).then(v => {
|
||||
// console.log('成功', v)
|
||||
}).catch(e => {
|
||||
console.error('错误', e.message)
|
||||
}).then(() => {
|
||||
client.disconnect();
|
||||
});
|
||||
}).catch(e => {
|
||||
console.error('连接错误', e)
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
main();
|
||||
12
test/try/no-res-issue/client/index.ts
Normal file
12
test/try/no-res-issue/client/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TsrpcClient } from "../../..";
|
||||
import { serviceProto } from '../server/protocols/proto';
|
||||
|
||||
let client = new TsrpcClient({
|
||||
proto: serviceProto
|
||||
});
|
||||
|
||||
client.callApi('Test', { name: 'ssss' }).then(v => {
|
||||
console.log('then', v)
|
||||
}).catch(e => {
|
||||
console.log('catch', e)
|
||||
})
|
||||
10
test/try/no-res-issue/server/index.ts
Normal file
10
test/try/no-res-issue/server/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { TsrpcServer } from '../../../index';
|
||||
import { serviceProto } from './protocols/proto';
|
||||
|
||||
let server = new TsrpcServer({
|
||||
proto: serviceProto,
|
||||
});
|
||||
|
||||
server.autoImplementApi('src/api');
|
||||
|
||||
server.start();
|
||||
7
test/try/no-res-issue/server/protocols/PtlTest.ts
Normal file
7
test/try/no-res-issue/server/protocols/PtlTest.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface ReqTest {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface ResTest {
|
||||
reply: string
|
||||
}
|
||||
52
test/try/no-res-issue/server/protocols/proto.ts
Normal file
52
test/try/no-res-issue/server/protocols/proto.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { ServiceProto } from 'tsrpc-proto';
|
||||
import { ReqTest, ResTest } from './PtlTest'
|
||||
|
||||
export interface ServiceType {
|
||||
req: {
|
||||
"Test": ReqTest
|
||||
},
|
||||
res: {
|
||||
"Test": ResTest
|
||||
},
|
||||
msg: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export const serviceProto: ServiceProto<ServiceType> = {
|
||||
"services": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Test",
|
||||
"type": "api",
|
||||
"req": "PtlTest/ReqTest",
|
||||
"res": "PtlTest/ResTest"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
"PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "reply",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
18
test/try/no-res-issue/server/src/api/ApiTest.ts
Normal file
18
test/try/no-res-issue/server/src/api/ApiTest.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ReqTest, ResTest } from "../../protocols/PtlTest";
|
||||
import { ApiCall } from "../../../../..";
|
||||
|
||||
export async function ApiTest(call: ApiCall<ReqTest, ResTest>) {
|
||||
await new Promise(rs => {
|
||||
let i = 5;
|
||||
call.logger.log(i);
|
||||
let interval = setInterval(() => {
|
||||
call.logger.log(--i);
|
||||
if (i === 0) {
|
||||
clearInterval(interval);
|
||||
rs();
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
call.error('asdfasdf', { a: 1, b: 2 })
|
||||
}
|
||||
12
test/try/package-lock.json
generated
Normal file
12
test/try/package-lock.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "try",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
test/try/package.json
Normal file
11
test/try/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "try",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
6
test/try/proto/MsgChat.ts
Normal file
6
test/try/proto/MsgChat.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface MsgChat {
|
||||
channel: number,
|
||||
userName: string,
|
||||
content: string,
|
||||
time: number
|
||||
}
|
||||
7
test/try/proto/PtlTest.ts
Normal file
7
test/try/proto/PtlTest.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface ReqTest {
|
||||
name: string
|
||||
};
|
||||
|
||||
export type ResTest = {
|
||||
reply: string
|
||||
};
|
||||
10
test/try/proto/a/b/c/PtlTest.ts
Normal file
10
test/try/proto/a/b/c/PtlTest.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { MsgChat } from '../../../MsgChat';
|
||||
|
||||
export interface ReqTest {
|
||||
name: string
|
||||
};
|
||||
|
||||
export type ResTest = {
|
||||
reply: string,
|
||||
chat?: MsgChat
|
||||
};
|
||||
135
test/try/proto/serviceProto.ts
Normal file
135
test/try/proto/serviceProto.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { ServiceProto } from 'tsrpc-proto';
|
||||
import { ReqTest, ResTest } from './a/b/c/PtlTest'
|
||||
import { MsgChat } from './MsgChat'
|
||||
import { ReqTest as ReqTest_1, ResTest as ResTest_1 } from './PtlTest'
|
||||
|
||||
export interface ServiceType {
|
||||
req: {
|
||||
"a/b/c/Test": ReqTest,
|
||||
"Test": ReqTest_1
|
||||
},
|
||||
res: {
|
||||
"a/b/c/Test": ResTest,
|
||||
"Test": ResTest_1
|
||||
},
|
||||
msg: {
|
||||
"Chat": MsgChat
|
||||
}
|
||||
}
|
||||
|
||||
export const serviceProto: ServiceProto<ServiceType> = {
|
||||
"services": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "a/b/c/Test",
|
||||
"type": "api",
|
||||
"req": "a/b/c/PtlTest/ReqTest",
|
||||
"res": "a/b/c/PtlTest/ResTest"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Chat",
|
||||
"type": "msg",
|
||||
"msg": "MsgChat/MsgChat"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Test",
|
||||
"type": "api",
|
||||
"req": "PtlTest/ReqTest",
|
||||
"res": "PtlTest/ResTest"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
"a/b/c/PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"a/b/c/PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "reply",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "chat",
|
||||
"type": {
|
||||
"type": "Reference",
|
||||
"target": "MsgChat/MsgChat"
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"MsgChat/MsgChat": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "channel",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "userName",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "content",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "time",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "reply",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
59
test/try/proto/typeProto.json
Normal file
59
test/try/proto/typeProto.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"MsgChat/MsgChat": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "channel",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "userName",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "content",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "time",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "name",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "reply",
|
||||
"type": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
21
test/try/server/api/ApiTest.ts
Normal file
21
test/try/server/api/ApiTest.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { TsrpcError } from "tsrpc-proto";
|
||||
import { ApiCallHttp } from '../../../src/server/http/HttpCall';
|
||||
import { ReqTest } from "../../proto/PtlTest";
|
||||
import { ResTest } from '../../proto/a/b/c/PtlTest';
|
||||
|
||||
export async function ApiTest(call: ApiCallHttp<ReqTest, ResTest>) {
|
||||
if (Math.random() > 0.75) {
|
||||
call.succ({
|
||||
reply: 'Hello, ' + call.req.name
|
||||
})
|
||||
}
|
||||
else if (Math.random() > 0.5) {
|
||||
call.error('What the fuck??', { msg: '哈哈哈哈' })
|
||||
}
|
||||
else if (Math.random() > 0.25) {
|
||||
throw new Error('这应该是InternalERROR')
|
||||
}
|
||||
else {
|
||||
throw new TsrpcError('返回到前台的错误', 'ErrInfo');
|
||||
}
|
||||
}
|
||||
5
test/try/server/api/a/b/c/ApiTest.ts
Normal file
5
test/try/server/api/a/b/c/ApiTest.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export async function ApiTest(call: any) {
|
||||
call.succ({
|
||||
reply: 'Api Test1 Succ'
|
||||
})
|
||||
}
|
||||
22
test/try/server/http.ts
Normal file
22
test/try/server/http.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { serviceProto } from '../proto/serviceProto';
|
||||
import * as path from "path";
|
||||
import { TsrpcServer } from '../../index';
|
||||
|
||||
let server = new TsrpcServer({
|
||||
proto: serviceProto
|
||||
});
|
||||
|
||||
server.dataFlow.push((data, conn) => {
|
||||
let httpReq = conn.options.httpReq;
|
||||
if (httpReq.method === 'GET') {
|
||||
conn.logger.log('url', httpReq.url);
|
||||
conn.logger.log('host', httpReq.headers.host);
|
||||
conn.options.httpRes.end('Hello~~~');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, 'api'));
|
||||
server.start();
|
||||
18
test/try/server/ws.ts
Normal file
18
test/try/server/ws.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { serviceProto, ServiceType } from '../proto/serviceProto';
|
||||
import * as path from "path";
|
||||
import { TsrpcServerWs } from '../../index';
|
||||
|
||||
let server = new TsrpcServerWs<ServiceType>({
|
||||
proto: serviceProto
|
||||
});
|
||||
server.start();
|
||||
|
||||
server.autoImplementApi(path.resolve(__dirname, 'api'));
|
||||
server.listenMsg('Chat', v => {
|
||||
v.conn.sendMsg('Chat', {
|
||||
channel: v.msg.channel,
|
||||
userName: 'SYSTEM',
|
||||
content: '收到',
|
||||
time: Date.now()
|
||||
})
|
||||
})
|
||||
59
test/try/tsconfig.json
Normal file
59
test/try/tsconfig.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
"useUnknownInCatchVariables": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user