[add] first

This commit is contained in:
2022-04-29 15:25:10 +08:00
commit da8024fc30
87 changed files with 16892 additions and 0 deletions

907
test/cases/http.test.ts Normal file
View 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
View 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
View 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
}
});
})
})

View 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
}
});
})
})

View 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

File diff suppressed because it is too large Load Diff

1278
test/cases/wsJSON.test.ts Normal file

File diff suppressed because it is too large Load Diff