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 29KB

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