You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Test.ts 28KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. import { describe, it } from "mocha";
  2. import { RPCExporter, Socket } from "../src/Interfaces";
  3. import { ConnectedSocket, Callback, GenericFunction } from "../src/Types";
  4. import * as log from 'why-is-node-running';
  5. import * as http from 'http';
  6. import * as express from 'express';
  7. import * as fetch from 'node-fetch';
  8. import { PromiseIO } from "../src/PromiseIO/Server";
  9. import { PromiseIOClient } from "../src/PromiseIO/Client";
  10. import { assert, expect } from 'chai';
  11. import { CLASSNAME_ATTRIBUTE, USER_DEFINED_TIMEOUT } from "../src/Strings";
  12. import { RPCServer, RPCSocket } from "../Index";
  13. var should = require('chai').should();
  14. var chai = require("chai");
  15. var chaiAsPromised = require("chai-as-promised");
  16. chai.use(chaiAsPromised);
  17. const noop = (...args: any[]) => { }
  18. const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
  19. function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = noop) {
  20. let subcallback: GenericFunction
  21. const serv = new RPCServer([{
  22. name: 'test',
  23. RPCs: [
  24. {
  25. name: 'echo',
  26. call: async (s: string) => s,
  27. }, {
  28. name: 'complexSignature',
  29. call: async ([a, b]) => {
  30. return [b, a]
  31. }
  32. }, {
  33. name: 'simpleSubscribe',
  34. hook: async (callback) => {
  35. subcallback = callback
  36. return { topic: "test" }
  37. },
  38. onDestroy: hookCloseHandler
  39. }, {
  40. name: 'subscribe',
  41. hook: async (callback) => {
  42. subcallback = callback
  43. return { topic: "test" }
  44. },
  45. onDestroy: hookCloseHandler,
  46. onCallback: onCallback
  47. },
  48. add,
  49. function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
  50. function brokenRPC() { throw new Error("Intended error") }
  51. ]
  52. }],
  53. {
  54. connectionHandler: connectionHandler,
  55. closeHandler: closeHandler,
  56. errorHandler: errorHandler
  57. })
  58. serv.listen(21010)
  59. return serv
  60. }
  61. describe('PromiseIO', () => {
  62. it("bind + fire", (done) => {
  63. const server = new PromiseIO()
  64. server.attach(new http.Server())
  65. server.on("socket", clientSocket => {
  66. clientSocket.bind("test123", (p1: string, p2:string) => {
  67. server.close()
  68. if (p1 === "p1" && p2 === "p2")
  69. done()
  70. })
  71. });
  72. server.listen(21003)
  73. PromiseIOClient.connect(21003, "localhost", { protocol: 'http' }).then(cli => {
  74. cli.fire("test123", "p1", "p2")
  75. cli.close()
  76. })
  77. })
  78. it("hook + call", (done) => {
  79. const server = new PromiseIO()
  80. server.attach(new http.Server())
  81. server.on("socket", clientSocket => {
  82. clientSocket.hook("test123", (p1, p2) => {
  83. if (p1 === "p1" && p2 === "p2")
  84. return "OK"
  85. })
  86. });
  87. server.listen(21003)
  88. PromiseIOClient.connect(21003, "localhost", { protocol: 'http' }).then(cli => {
  89. cli.call("test123", "p1", "p2").then(resp => {
  90. cli.close()
  91. server.close()
  92. if (resp === "OK")
  93. done()
  94. })
  95. })
  96. })
  97. it("on + emit", (done) => {
  98. const server = new PromiseIO()
  99. server.attach(new http.Server())
  100. server.on("socket", clientSocket => {
  101. clientSocket.on("test123", (p1, p2) => {
  102. server.close()
  103. if (p1 === "p1" && p2 === "p2")
  104. done()
  105. })
  106. });
  107. server.listen(21003)
  108. PromiseIOClient.connect(21003, "localhost", { protocol: 'http' }).then(cli => {
  109. cli.emit("test123", "p1", "p2")
  110. cli.close()
  111. })
  112. })
  113. })
  114. describe('RPCServer', () => {
  115. let client, server
  116. const echo = (x) => x
  117. before(done => {
  118. server = new RPCServer([{
  119. name: 'HelloWorldRPCGroup',
  120. RPCs: () => [
  121. echo, //named function variable
  122. function echof(x) { return x }, //named function
  123. {
  124. name: 'echoExplicit', //describing object
  125. call: async (x, y, z) => [x, y, z]
  126. }
  127. ]
  128. }])
  129. server.listen(21003)
  130. client = new RPCSocket(21003, 'localhost')
  131. done()
  132. })
  133. after(done => {
  134. client.close()
  135. server.close()
  136. done()
  137. })
  138. it('should be able to use all kinds of RPC definitions', async () => {
  139. await client.connect()
  140. const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
  141. const r1 = await client['HelloWorldRPCGroup'].echof('World')
  142. const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
  143. expect(r0).to.be.equal('Hello')
  144. expect(r1).to.be.equal('World')
  145. expect(r2.join('')).to.be.equal('RPC!')
  146. })
  147. it('new RPCServer() should fail on unnamed RPC', async () => {
  148. expect(() => {
  149. const sv = new RPCServer([{
  150. name: 'bad',
  151. RPCs: () => [
  152. (aaa, bbb, ccc) => { return aaa + bbb + ccc }
  153. ]
  154. }])
  155. }).to.throw()
  156. })
  157. })
  158. describe('RPCServer with premade http server', () => {
  159. const echo = (x) => x
  160. const RPCs = [
  161. echo, //named function variable
  162. function echof(x) { return x }, //named function
  163. {
  164. name: 'echoExplicit', //describing object
  165. call: async (x, y, z) => [x, y, z]
  166. }
  167. ]
  168. const RPCExporters = [
  169. {
  170. name: 'HelloWorldRPCGroup',
  171. RPCs: RPCs,
  172. }
  173. ]
  174. const RPCExporters2 = [
  175. {
  176. name: 'Grp2',
  177. RPCs: [
  178. function test() { return "test" }
  179. ],
  180. }
  181. ]
  182. let client: RPCSocket, server: RPCServer
  183. before(done => {
  184. const expressServer = express()
  185. const httpServer = new http.Server(expressServer)
  186. expressServer.get('/REST_ping', (req, res) => {
  187. return res
  188. .send('REST_pong')
  189. .status(200)
  190. })
  191. server = new RPCServer(
  192. RPCExporters,
  193. )
  194. server.attach(httpServer)
  195. httpServer.listen(8080)
  196. client = new RPCSocket(8080, 'localhost')
  197. done()
  198. })
  199. after(done => {
  200. client.close()
  201. server.close()
  202. done()
  203. })
  204. it('should serve REST', async () => {
  205. const response = await fetch('http://localhost:8080/REST_ping')
  206. const text = await response.text()
  207. expect(text).to.be.equal("REST_pong")
  208. })
  209. it('should be able to use all kinds of RPC definitions', async () => {
  210. await client.connect()
  211. const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
  212. const r1 = await client['HelloWorldRPCGroup'].echof('World')
  213. const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
  214. expect(r0).to.be.equal('Hello')
  215. expect(r1).to.be.equal('World')
  216. expect(r2.join('')).to.be.equal('RPC!')
  217. })
  218. })
  219. describe('should be able to attach to non-standard path', () => {
  220. let client: RPCSocket, server: RPCServer
  221. const echo = (x) => x
  222. before(done => {
  223. server = new RPCServer([{
  224. name: 'HelloWorldRPCGroup',
  225. RPCs: () => [
  226. echo, //named function variable
  227. function echof(x) { return x }, //named function
  228. {
  229. name: 'echoExplicit', //describing object
  230. call: async (x, y, z) => [x, y, z]
  231. }
  232. ]
  233. }])
  234. server.listen(21003, { path: '/test' })
  235. client = new RPCSocket(21003, 'localhost', { path: '/test' })
  236. done()
  237. })
  238. after(done => {
  239. client.close()
  240. server.close()
  241. done()
  242. })
  243. it('should be able to use all kinds of RPC definitions', async () => {
  244. await client.connect()
  245. const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
  246. const r1 = await client['HelloWorldRPCGroup'].echof('World')
  247. const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
  248. expect(r0).to.be.equal('Hello')
  249. expect(r1).to.be.equal('World')
  250. expect(r2.join('')).to.be.equal('RPC!')
  251. })
  252. })
  253. describe('can attach multiple RPCServers to same http server', () => {
  254. const echo = (x) => x
  255. const RPCs = [
  256. echo, //named function variable
  257. function echof(x) { return x }, //named function
  258. {
  259. name: 'echoExplicit', //describing object
  260. call: async (x, y, z) => [x, y, z]
  261. }
  262. ]
  263. const RPCExporters = [
  264. {
  265. name: 'HelloWorldRPCGroup',
  266. RPCs: RPCs,
  267. }
  268. ]
  269. const RPCExporters2 = [
  270. {
  271. name: 'Grp2',
  272. RPCs: [
  273. function test() { return "/test" }
  274. ],
  275. }
  276. ]
  277. const callTimeout = 100;
  278. let client: RPCSocket, client2: RPCSocket, server: RPCServer, server2: RPCServer
  279. before(done => {
  280. const expressServer = express()
  281. const httpServer = new http.Server(expressServer)
  282. server = new RPCServer(
  283. RPCExporters,
  284. )
  285. server2 = new RPCServer(
  286. RPCExporters2
  287. )
  288. server.attach(httpServer)
  289. server2.attach(httpServer, {
  290. path: "test"
  291. })
  292. httpServer.listen(8080)
  293. new RPCSocket(8080, 'localhost').connect().then(sock => {
  294. client = sock
  295. new RPCSocket(8080, 'localhost', { path: "test", callTimeoutMs: callTimeout }).connect().then(sock2 => {
  296. client2 = sock2
  297. done()
  298. })
  299. })
  300. })
  301. after(done => {
  302. client.close()
  303. client2.close()
  304. server.close()
  305. server2.close()
  306. done()
  307. })
  308. it('both servers should answer', async () => {
  309. const res = await client['HelloWorldRPCGroup'].echo("test")
  310. expect(res).to.equal('test')
  311. const res2 = await client2['Grp2'].test()
  312. expect(res2).to.equal('/test')
  313. })
  314. it('server1 should answer after server2 is closed', async () => {
  315. server2.close()
  316. const res = await client['HelloWorldRPCGroup'].echo("test")
  317. expect(res).to.equal('test')
  318. return client2['Grp2'].test().should.eventually.be.rejectedWith(USER_DEFINED_TIMEOUT(callTimeout))
  319. })
  320. })
  321. describe("can attach second RPCServer if first is already running", () => {
  322. const RPCExporters = [
  323. {
  324. name: 'HelloWorldRPCGroup',
  325. RPCs: [
  326. function echo(x) { return x }, //named function variable
  327. function echof(x) { return x }, //named function
  328. {
  329. name: 'echoExplicit', //describing object
  330. call: async (x, y, z) => [x, y, z]
  331. }
  332. ],
  333. }
  334. ]
  335. const RPCExporters2 = [
  336. {
  337. name: 'Grp2',
  338. RPCs: [
  339. function test() { return "/test" }
  340. ],
  341. }
  342. ]
  343. it("attaches correctly", async () => {
  344. const expressServer = express()
  345. const httpServer = new http.Server(expressServer)
  346. const server = new RPCServer(
  347. RPCExporters,
  348. )
  349. const server2 = new RPCServer(
  350. RPCExporters2
  351. )
  352. server.attach(httpServer)
  353. httpServer.listen(8080)
  354. server2.attach(httpServer, {
  355. path: "test"
  356. })
  357. const sock = await new RPCSocket(8080, 'localhost').connect()
  358. const sock2 = await new RPCSocket(8080, 'localhost', { path: "test" }).connect()
  359. const resp = await sock2.Grp2.test()
  360. expect(resp).to.be.equal("/test")
  361. server.close()
  362. server2.close()
  363. sock.close()
  364. sock2.close()
  365. })
  366. })
  367. describe('Serverside Triggers', () => {
  368. let server, client
  369. const closerFunction = (done) => () => {
  370. client.close()
  371. server.close()
  372. done()
  373. }
  374. it('trigger onCallback', (done) => {
  375. server = makeServer(closerFunction(done))
  376. client = new RPCSocket(21010, "localhost")
  377. client.connect().then(_ => {
  378. client['test'].subscribe(noop).then(_ => client['test'].triggerCallback())
  379. })
  380. })
  381. /* testing framework has trouble terminating on this one
  382. it('trigger connectionHandler', (done) => {
  383. server = makeServer(undefined, closerFunction(done))
  384. client = new RPCSocket(21010, "localhost")
  385. client.connect()
  386. })
  387. */
  388. it('trigger hook closeHandler', (done) => {
  389. server = makeServer(undefined, undefined, closerFunction(done))
  390. client = new RPCSocket(21010, "localhost")
  391. client.connect().then(_ => {
  392. client['test'].subscribe(function cb() {
  393. cb['destroy']()
  394. }).then(_ => client['test'].triggerCallback())
  395. })
  396. })
  397. it('trigger global closeHandler', (done) => {
  398. server = makeServer(undefined, undefined, undefined, () => {
  399. server.close()
  400. done()
  401. })
  402. client = new RPCSocket(21010, "localhost")
  403. client.connect().then(_ => {
  404. client['test'].subscribe(noop).then(_ => client.close())
  405. })
  406. })
  407. })
  408. describe('RPCSocket', () => {
  409. let client: RPCSocket
  410. let server: RPCServer
  411. before(async () => {
  412. server = makeServer()
  413. client = new RPCSocket(21010, "localhost")
  414. return await client.connect()
  415. })
  416. after(() => {
  417. client.close()
  418. server.close()
  419. })
  420. it('should have rpc echo', async () => {
  421. const x = await client['test'].echo("x")
  422. expect(x).to.be.equal('x')
  423. })
  424. it('should add up to 6', async () => {
  425. const sum = await client['test'].add(1, 2, 3)
  426. expect(sum).to.be.equal(6)
  427. })
  428. it('should subscribe with success', async () => {
  429. const res = await client['test'].simpleSubscribe(noop)
  430. expect(res.topic).to.be.equal('test')
  431. })
  432. it('subscribe should call back', (done) => {
  433. client['test'].subscribe((...args: any) => {
  434. if (args[0] === "test" && args[1] === "callback")
  435. done()
  436. else
  437. done(new Error("Bad callback value " + args))
  438. }).then(async () => {
  439. await client['test'].triggerCallback("test", "callback")
  440. })
  441. })
  442. it('simpleSubscribe should call back', (done) => {
  443. client['test'].simpleSubscribe((...args: any) => {
  444. if (args[0] === "test_" && args[1] === "callback_")
  445. done()
  446. else
  447. done(new Error("Bad callback value " + args))
  448. }).then(async () => {
  449. await client['test'].triggerCallback("test_", "callback_")
  450. })
  451. })
  452. })
  453. describe('It should do unhook', () => {
  454. const yesCandy = "OK"
  455. const noCandy = "stolen"
  456. let candy = yesCandy
  457. let cb: Function
  458. let cb2: Function
  459. let client: RPCSocket
  460. let server: RPCServer
  461. before(async () => {
  462. server = new RPCServer([{
  463. name: "test",
  464. RPCs: () => [{
  465. name: 'subscribe',
  466. hook: async (callback): Promise<void> => {
  467. cb = <Function>callback
  468. return
  469. }
  470. },
  471. {
  472. name: 'subscribeWithParam',
  473. hook: async (callback, param): Promise<{ uuid: string }> => {
  474. if (param != "OK") {
  475. console.log("param was" + param);
  476. return {
  477. uuid: "no",
  478. }
  479. }
  480. cb2 = <Function>callback
  481. return {
  482. uuid: "OK",
  483. }
  484. }
  485. },
  486. function publish(): string { cb(candy); return candy },
  487. function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
  488. ]
  489. }],
  490. {
  491. connectionHandler: noop,
  492. closeHandler: noop,
  493. errorHandler: (socket, err) => { throw err }
  494. })
  495. server.listen(21010)
  496. client = new RPCSocket(21010, "localhost")
  497. return await client.connect()
  498. })
  499. after(() => {
  500. client.close()
  501. server.close()
  502. })
  503. it('Subscribe with param', async () => {
  504. const res = await client['test'].subscribeWithParam(noop, "OK")
  505. expect(res.uuid).to.be.equal(candy)
  506. })
  507. let run = 0
  508. const expected = [yesCandy, noCandy, noCandy, noCandy]
  509. it('Unhook+unsubscribe should stop callbacks', async () => {
  510. await client['test'].subscribe(function myCallback(c) {
  511. if (run == 1)
  512. (myCallback as any).destroy()
  513. expect(c).to.equal(expected[run++])
  514. })
  515. const r1 = await client['test'].publish()
  516. const r3 = await client['test'].unsubscribe()
  517. const r2 = await client['test'].publish()
  518. const r4 = await client['test'].publish()
  519. expect(r1).to.be.equal(yesCandy)
  520. expect(r2).to.be.equal(noCandy)
  521. expect(r3).to.be.equal(noCandy)
  522. expect(r4).to.be.equal(noCandy)
  523. })
  524. })
  525. type topicDTO = { topic: string; }
  526. type SesameTestIfc = {
  527. test: {
  528. checkCandy: () => Promise<string>
  529. subscribe: (callback: Callback<[string]>) => Promise<topicDTO>
  530. manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
  531. }
  532. }
  533. describe('Sesame should unlock the socket', () => {
  534. let candy = "OK"
  535. let client: ConnectedSocket<SesameTestIfc>
  536. let server: RPCServer<SesameTestIfc>
  537. let cb: Function = (...args) => { }
  538. before((done) => {
  539. server = new RPCServer<SesameTestIfc>([{
  540. name: "test",
  541. RPCs: () => [
  542. {
  543. name: 'subscribe',
  544. hook: async (callback) => {
  545. cb = callback
  546. return {
  547. topic: 'test'
  548. }
  549. },
  550. onDestroy: noop
  551. },
  552. async function checkCandy() { cb(candy); cb = noop; return candy },
  553. async function manyParams(a, b, c, d) { return [a, b, c, d] }
  554. ],
  555. }], {
  556. sesame: (_sesame) => _sesame === 'sesame!'
  557. })
  558. server.listen(21004)
  559. const sock = new RPCSocket<SesameTestIfc>(21004, "localhost")
  560. sock.connect('sesame!').then(cli => {
  561. client = cli
  562. done()
  563. })
  564. })
  565. after(() => {
  566. client.close()
  567. server.close()
  568. })
  569. it('should work with sesame', async () => {
  570. const c = client.test.checkCandy()
  571. expect(c).to.exist
  572. })
  573. it('should work with multiple params', async () => {
  574. const c = await client.test['manyParams']('a', 'b', 'c', 'd')
  575. expect(c[0]).to.be.equal('a')
  576. expect(c[1]).to.be.equal('b')
  577. expect(c[2]).to.be.equal('c')
  578. expect(c[3]).to.be.equal('d')
  579. })
  580. it('should not work without sesame', async () => {
  581. const sock = new RPCSocket(21004, "localhost")
  582. const cli = await sock.connect()
  583. expect(cli.test).to.not.exist
  584. cli.close()
  585. sock.close()
  586. })
  587. it('should fail with wrong sesame', async () => {
  588. const sock = new RPCSocket(21004, "localhost")
  589. const cli = await sock.connect('iamwrong')
  590. expect(cli.test).to.not.exist
  591. cli.close()
  592. sock.close()
  593. })
  594. it('callback should work with sesame', (done) => {
  595. client.test.subscribe(function (c) {
  596. if (c === candy) {
  597. done()
  598. }
  599. }).then(d => {
  600. if (d.topic !== 'test')
  601. done('unexpected invalid response')
  602. client.test.checkCandy()
  603. })
  604. })
  605. })
  606. describe('Error handling', () => {
  607. const errtxt = "BAD BAD BAD"
  608. let createUser = async (user: { a: any, b: any }) => {
  609. throw new Error(errtxt)
  610. }
  611. it("RPC throws on client without handler", (done) => {
  612. let server = new RPCServer([{
  613. name: "createUser",
  614. RPCs: () => [{
  615. name: 'createUser' as 'createUser',
  616. call: createUser
  617. }]
  618. }])
  619. server.listen(21004)
  620. let sock = new RPCSocket(21004, 'localhost')
  621. sock.connect().then((cli) => {
  622. cli["createUser"]["createUser"]({
  623. a: 'a',
  624. b: 'b'
  625. })
  626. .then(r => {
  627. if (r != null)
  628. done(new Error("UNEXPECTED RESULT " + r))
  629. })
  630. .catch((e) => {
  631. if (e.message === errtxt)
  632. done()
  633. else
  634. done(e)
  635. })
  636. .finally(() => {
  637. cli.close()
  638. sock.close()
  639. server.close()
  640. })
  641. })
  642. })
  643. it("RPC throws on server with handler", (done) => {
  644. let server = new RPCServer([{
  645. name: "createUser",
  646. RPCs: () => [{
  647. name: 'createUser' as 'createUser',
  648. call: createUser
  649. }]
  650. }], {
  651. errorHandler: (socket, e, rpcName, args) => {
  652. done()
  653. }
  654. })
  655. server.listen(21004)
  656. let sock = new RPCSocket(21004, 'localhost')
  657. sock.connect().then((cli) => {
  658. cli["createUser"]["createUser"]({
  659. a: 'a',
  660. b: 'b'
  661. })
  662. .then(r => {
  663. if (r != null)
  664. done("UNEXPECTED RESULT " + r)
  665. })
  666. .catch((e) => {
  667. done("UNEXPECTED CLIENT ERROR " + e)
  668. done(e)
  669. })
  670. .finally(() => {
  671. cli.close()
  672. sock.close()
  673. server.close()
  674. })
  675. })
  676. })
  677. })
  678. describe("Errorhandler functionality", () => {
  679. const errtxt = "BAD BAD BAD"
  680. let createUser = async (user: { a: any, b: any }) => {
  681. throw new Error(errtxt)
  682. }
  683. it("correct values are passed to the handler", (done) => {
  684. let server = new RPCServer([{
  685. name: "createUser",
  686. RPCs: () => [{
  687. name: 'createUser' as 'createUser',
  688. call: createUser
  689. }]
  690. }], {
  691. errorHandler: (socket, e, rpcName, args) => {
  692. if (e.message === errtxt && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b')
  693. done()
  694. }
  695. })
  696. server.listen(21004)
  697. let sock = new RPCSocket(21004, 'localhost')
  698. sock.connect().then((cli) => {
  699. cli["createUser"]["createUser"]({
  700. a: 'a',
  701. b: 'b'
  702. })
  703. .then(r => {
  704. if (r != null)
  705. done(new Error("UNEXPECTED RESULT " + r))
  706. })
  707. .catch((e) => {
  708. done(new Error("UNEXPECTED CLIENT ERROR " + e.message))
  709. })
  710. .finally(() => {
  711. cli.close()
  712. sock.close()
  713. server.close()
  714. })
  715. })
  716. })
  717. it("handler sees sesame", (done) => {
  718. let sesame = "AAAAAAAAAAAAAAA"
  719. let server = new RPCServer([{
  720. name: "createUser" as "createUser",
  721. RPCs: () => [{
  722. name: 'createUser' as 'createUser',
  723. call: createUser
  724. }]
  725. }], {
  726. sesame: sesame,
  727. errorHandler: (socket, e, rpcName, args) => {
  728. if (e.message === errtxt && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b')
  729. done()
  730. }
  731. })
  732. server.listen(21004)
  733. let sock = new RPCSocket(21004, 'localhost')
  734. sock.connect(sesame).then((cli) => {
  735. cli["createUser"]["createUser"]({
  736. a: 'a',
  737. b: 'b'
  738. })
  739. .then(r => {
  740. if (r != null)
  741. done(new Error("UNEXPECTED RESULT " + r))
  742. })
  743. .catch((e) => {
  744. done(new Error("UNEXPECTED CLIENT ERROR " + e))
  745. })
  746. .finally(() => {
  747. cli.close()
  748. sock.close()
  749. server.close()
  750. })
  751. })
  752. })
  753. })
  754. type myExporterIfc = {
  755. MyExporter: {
  756. myRPC: () => Promise<string>
  757. }
  758. }
  759. describe("Class binding", () => {
  760. let exporter1: MyExporter
  761. let serv: RPCServer<myExporterIfc>
  762. let sock: RPCSocket & myExporterIfc
  763. let allowed = true
  764. const SESAME = 'xyz'
  765. class MyExporter implements RPCExporter<myExporterIfc>{
  766. name = "MyExporter" as "MyExporter"
  767. RPCs = () => [
  768. this.myRPC
  769. ]
  770. myRPC = async () => {
  771. //serv.setExporters([new MyOtherExporter])
  772. return "Hello World"
  773. }
  774. }
  775. class MyOtherExporter implements RPCExporter<myExporterIfc>{
  776. name = "MyExporter" as "MyExporter"
  777. RPCs = () => [
  778. this.myRPC
  779. ]
  780. myRPC = async () => {
  781. return "Hello Borld"
  782. }
  783. }
  784. before(done => {
  785. exporter1 = new MyExporter()
  786. serv = new RPCServer<myExporterIfc>(
  787. [exporter1],
  788. {
  789. accessFilter: async (sesame, exporter) => {
  790. if (exporter.name === 'MyExporter') {
  791. if (!allowed) return false
  792. allowed = false
  793. return sesame === SESAME;
  794. } else {
  795. return false
  796. }
  797. },
  798. sesame: SESAME
  799. })
  800. serv.listen(21004)
  801. done()
  802. })
  803. beforeEach((done) => {
  804. const s = new RPCSocket<myExporterIfc>(21004, 'localhost')
  805. s.connect(SESAME).then(conn => {
  806. sock = conn
  807. done()
  808. })
  809. })
  810. afterEach((done) => {
  811. sock.close()
  812. done()
  813. })
  814. after(() => {
  815. serv.close()
  816. })
  817. it("use sesameFilter for available", (done) => {
  818. if (sock['MyExporter']) {
  819. allowed = false
  820. done()
  821. }
  822. else done(new Error("RPC supposed to be here"))
  823. })
  824. it("use sesameFilter", (done) => {
  825. if (!sock['MyExporter']) done()
  826. else done(new Error("RPC supposed to be gone"))
  827. })
  828. })
  829. /*
  830. describe('finally', () => {
  831. it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
  832. //log(console)
  833. })
  834. })
  835. */
  836. describe("attaching handlers before connecting", () => {
  837. it("fires error if server is unreachable", (done) => {
  838. const sock = new RPCSocket<myExporterIfc>(21004, 'localhost')
  839. let errorHandleCount = 0
  840. sock.on('error', (err) => {
  841. //attached listener fires first
  842. if (errorHandleCount != 0) {
  843. console.log("Error handler didn't fire first");
  844. } else {
  845. errorHandleCount++
  846. }
  847. })
  848. sock.connect().then(_ => {
  849. console.log("Unexpected successful connect")
  850. }).catch(e => {
  851. //catch clause fires second
  852. if (errorHandleCount != 1) {
  853. console.log("catch clause didn't fire second", errorHandleCount);
  854. } else {
  855. sock.close()
  856. done()
  857. }
  858. })
  859. })
  860. it("fires error if call is unknown", (done) => {
  861. const serv = new RPCServer().listen(21004)
  862. const sock = new RPCSocket(21004, 'localhost')
  863. sock.on('error', (err) => {
  864. sock.close()
  865. serv.close()
  866. done()
  867. })
  868. sock.connect().then(_ => {
  869. sock.call("unknownRPC123", "AAAAA").catch(e => { }).then(x => {
  870. console.log("X", x);
  871. })
  872. }).catch(e => {
  873. console.log("unexpected connect catch clause");
  874. done(e)
  875. })
  876. })
  877. })