import { describe, it } from "mocha"; import { RPCServer, RPCSocket, SubscriptionResponse, makeSubResponse } from '../Index' import * as uuidv4 from "uuid/v4" const add = (...args:number[]) => {return args.reduce((a,b)=>a+b, 0)} function makeServer(){ let subcallback return new RPCServer<{ topic: string }>(20000, [{ name: "test", exportRPCs: () => [ { name: 'echo', call: async (s:string) => s, },{ name: 'simpleSubscribe', hook: async(callback) => { subcallback = callback return makeSubResponse<{topic: string}>({topic: "test"}) } },{ name: 'subscribe', hook: async (callback) => { subcallback = callback return makeSubResponse<{topic: string}>({topic: "test"}) }, onClose: (res, rpc) => { console.log("onClose", rpc.name === 'subscribe' && res?"OK":"") subcallback = null }, onCallback: (...args:any) => { console.log("onCallback", args[0] === "test" && args[1] === "callback"?"OK":"") } }, add, function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)}, ] }],{ connectionHandler: (socket) => { console.log("connectionHandler OK") }, closeHandler: (socket) => { console.log("closeHandler OK") }, errorHandler: (socket, err) => { console.error("errorHandler OK SO YOU SHOULDN'T SEE THIS"); throw err } }) } describe('RPCServer', () => { let server: RPCServer<{ topic: string }, any> before((done) => { server = makeServer() done() }) after(async() => { await server.destroy() }) it('should be able to use all kinds of RPC definitions', (done) => { const echo = (x) => x const server = new RPCServer(20003, [{ name: 'HelloWorldRPCGroup', exportRPCs: () => [ echo, //named function variable function echof(x){ return x }, //named function { name: 'echoExplicit', //describing object call: async (x) => x } ] }]) const client = new RPCSocket(20003, 'localhost') client.connect().then(async () => { const r0 = await client['HelloWorldRPCGroup'].echo('Hello') const r1 = await client['HelloWorldRPCGroup'].echof('World') const r2 = await client['HelloWorldRPCGroup'].echoExplicit('RPC!') if(r0 === 'Hello' && r1 === 'World' && r2 ==='RPC!'){ client.destroy() server.destroy() done() } }) }) it('new RPCServer() should fail on bad RPC', (done) => { try{ new RPCServer(20001, [{ name: "bad", exportRPCs: () => [ (aaa,bbb,ccc) => { return aaa+bbb+ccc } ] }]) done(new Error("Didn't fail with bad RPC")) }catch(badRPCError){ done() } }) }) describe('RPCSocket', () => { let client: RPCSocket let server: RPCServer<{topic: string}> before(async() => { server = makeServer() client = new RPCSocket(20000, "localhost") return await client.connect() }) after(() => { client.destroy() server.destroy() }) it('should have rpc echo', (done) => { client['test'].echo("x").then(x => { if(x === 'x') done() else done(new Error('echo RPC response did not match')) }) }) it('should add up to 6', (done) => { client['test'].add(1,2,3).then(x => { if(x === 6) done() else done(new Error('add RPC response did not match')) }) }) it('should subscribe with success', (done) => { client['test'].simpleSubscribe(console.log).then(res => { if(res.result === 'Success'){ done() }else{ console.error(res) done(new Error('Subscribe did not return success')) } }) }) it('subscribe should call back', (done) => { client['test'].subscribe((...args: any) => { if(args[0] === "test" && args[1] === "callback") done() else done(new Error("Bad callback value "+ args)) }).then( async () => { await client['test'].triggerCallback("test", "callback") }) }) it('simpleSubscribe should call back', (done) => { client['test'].simpleSubscribe((...args: any) => { if(args[0] === "test_" && args[1] === "callback_") done() else done(new Error("Bad callback value "+ args)) }).then( async () => { await client['test'].triggerCallback("test_", "callback_") }) }) }) describe('It should do unhook', () => { let candy = "OK" let cb: Function let client: RPCSocket let server: RPCServer<{topic: string}> before(async() => { server = new RPCServer<{ topic: string }>(20000, [{ name: "test", exportRPCs: () => [{ name: 'subscribe', hook: async(callback):Promise> => { cb = callback return { result: "Success", uuid: uuidv4(), topic: "test" } } }, function checkCandy():string { cb(candy); return candy }, function stealCandy():string { candy = "_OK"; cb(candy); cb = (...any) => console.log.apply(console,["Server:", ...any]); return candy } ] }],{ connectionHandler: (socket) => { console.log("connectionHandler OK") }, closeHandler: (socket) => { console.log("closeHandler OK") }, errorHandler: (socket, err) => { console.error("errorHandler OK SO YOU SHOULDN'T SEE THIS"); throw err } }) client = new RPCSocket(20000, "localhost") return await client.connect() }) after(() => { client.destroy() server.destroy() }) it('Unhook+unsubscribe should stop callbacks', (done) => { client['test'].subscribe(c => console.log("Client: "+c)).then( async (res: SubscriptionResponse) => { const r1 = await client['test'].checkCandy() const r3 = await client['test'].stealCandy() client.unhook(res.uuid) console.log("---- No client output below this line") const r2 = await client['test'].checkCandy() const r4 = await client['test'].checkCandy() console.log("---- More output below") if(r1 === "OK" && r3 === "_OK" && r2 === "_OK" && r4 === "_OK") done() else done(new Error("Results did not match: "+[r1,r2,r3,r4])) }) }) }) type SesameTestIfc = { test: { checkCandy: ()=>Promise} } describe('Sesame should unlock the socket', () => { let candy = "OK" let client: RPCSocket & SesameTestIfc let server: RPCServer<{topic: string}, SesameTestIfc> before(async() => { server = new RPCServer<{ topic: string }, SesameTestIfc>(20004, [{ name: "test", exportRPCs: () => [ async function checkCandy():Promise { return candy }, ]} ],{ sesame: (_sesame) => _sesame === 'sesame!' }) const sock = new RPCSocket(20004, "localhost") client = await sock.connect('sesame!') }) after(() => { client.destroy() server.destroy() }) it('should not work without sesame', (done) => { const sock = new RPCSocket(20004, "localhost") sock.connect( /* no sesame */).then(async (c) => { c.test.checkCandy().then(d => { if(d === candy) done("should not be able to get candy") done() }).finally(() => { sock.destroy() }) }) }) it('should work with sesame', (done) => { client.test.checkCandy().then(c => done()) }) })