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

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