comq 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.github/labels.yaml +15 -0
  2. package/.github/workflows/labels.yaml +18 -0
  3. package/.github/workflows/release.yaml +20 -8
  4. package/docker-compose.yaml +20 -2
  5. package/docs/notes.md +14 -8
  6. package/examples/const.js +1 -3
  7. package/examples/events/consumer.js +5 -6
  8. package/examples/events/producer.js +3 -3
  9. package/examples/package.json +3 -0
  10. package/examples/rpc/consumer.js +3 -3
  11. package/examples/rpc/producer.js +3 -3
  12. package/features/connection.feature +3 -0
  13. package/features/events.feature +1 -1
  14. package/features/shards.feature +61 -0
  15. package/features/steps/.test/backpressure.test.js +2 -2
  16. package/features/steps/.test/broker.test.js +32 -6
  17. package/features/steps/.test/connection.test.js +105 -1
  18. package/features/steps/.test/context.mock.js +2 -2
  19. package/features/steps/.test/context.test.js +13 -5
  20. package/features/steps/.test/events.test.js +66 -3
  21. package/features/steps/.test/rpc.test.js +99 -1
  22. package/features/steps/.types/context.d.ts +11 -1
  23. package/features/steps/backpressure.js +1 -1
  24. package/features/steps/broker.js +26 -9
  25. package/features/steps/connection.js +69 -2
  26. package/features/steps/context.js +28 -12
  27. package/features/steps/events.js +41 -2
  28. package/features/steps/rpc.js +58 -1
  29. package/lerna.json +1 -1
  30. package/package.json +5 -5
  31. package/readme.md +75 -25
  32. package/source/channel.js +56 -32
  33. package/source/connect.js +28 -2
  34. package/source/connection.js +24 -17
  35. package/source/decode.js +1 -1
  36. package/source/events.js +7 -0
  37. package/source/io.js +21 -14
  38. package/source/shards/channel.js +219 -0
  39. package/source/shards/connection.js +56 -0
  40. package/source/shards/index.js +5 -0
  41. package/test/channel.test.js +240 -145
  42. package/test/connect.test.js +39 -11
  43. package/test/connection.mock.js +17 -13
  44. package/test/connection.test.js +65 -12
  45. package/test/decode.test.js +5 -5
  46. package/test/io.close.test.js +2 -2
  47. package/test/io.consume.test.js +1 -1
  48. package/test/io.reply.test.js +7 -7
  49. package/test/io.request.test.js +27 -1
  50. package/test/presets.test.js +1 -1
  51. package/test/shards/channel.test.js +367 -0
  52. package/test/shards/connection.test.js +106 -0
  53. package/types/amqp.d.ts +3 -1
  54. package/types/channel.d.ts +8 -5
  55. package/types/connection.d.ts +3 -1
  56. package/types/diagnostic.d.ts +1 -1
  57. package/types/topology.d.ts +4 -0
  58. /package/source/{presets → topology}/event.json +0 -0
  59. /package/source/{presets → topology}/index.js +0 -0
  60. /package/source/{presets → topology}/reply.json +0 -0
  61. /package/source/{presets → topology}/request.json +0 -0
@@ -0,0 +1,106 @@
1
+ 'use strict'
2
+
3
+ const { generate } = require('randomstring')
4
+ const { random, promex, sample } = require('@toa.io/generic')
5
+
6
+ const { Connection } = require('../../source/shards')
7
+
8
+ const mock = require('../connection.mock')
9
+
10
+ jest.mock('../../source/shards/channel')
11
+
12
+ const { create } = require('../../source/shards/channel')
13
+
14
+ it('should be', async () => {
15
+ expect(Connection).toBeDefined()
16
+ })
17
+
18
+ /** @type {jest.MockedObject<comq.Connection>[]} */
19
+ const connections = [mock.connection(), mock.connection()]
20
+
21
+ /** @type {comq.Connection} */
22
+ let connection
23
+
24
+ beforeEach(() => {
25
+ jest.clearAllMocks()
26
+
27
+ connection = new Connection(connections)
28
+ })
29
+
30
+ describe('open', () => {
31
+ it('should resolve when one of connections is established', async () => {
32
+ expect.assertions(1)
33
+
34
+ /** @type {toa.generic.Promex[]} */
35
+ const promises = []
36
+
37
+ for (const conn of connections) {
38
+ const promise = promex()
39
+
40
+ conn.open.mockImplementation(() => promise)
41
+ promises.push(promise)
42
+ }
43
+
44
+ let connected = false
45
+
46
+ setImmediate(() => {
47
+ expect(connected).toStrictEqual(false)
48
+
49
+ const any = sample(promises)
50
+
51
+ any.resolve()
52
+ })
53
+
54
+ await connection.open()
55
+
56
+ connected = true
57
+ })
58
+ })
59
+
60
+ describe('close', () => {
61
+ it('should close all connections', async () => {
62
+ await connection.close()
63
+
64
+ for (const conn of connections) {
65
+ expect(conn.close).toHaveBeenCalled()
66
+ }
67
+ })
68
+ })
69
+
70
+ describe('createChannel', () => {
71
+ const type = generate()
72
+
73
+ beforeEach(() => {
74
+ connection.createChannel(type)
75
+ })
76
+
77
+ it('should create channel', async () => {
78
+ expect(create).toHaveBeenCalledWith(connections, type)
79
+ })
80
+ })
81
+
82
+ describe.each(/** @type {comq.diagnostics.event[]} */ ['open', 'close'])('diagnose %s event',
83
+ (event) => {
84
+ const index = random(connections.length)
85
+
86
+ it('should re-emit event', async () => {
87
+ for (const conn of connections) {
88
+ expect(conn.diagnose).toHaveBeenCalledWith(event, expect.any(Function))
89
+ }
90
+
91
+ const listener = /** @type {Function} */ jest.fn()
92
+
93
+ connection.diagnose(event, listener)
94
+
95
+ const call = connections[index].diagnose.mock.calls.find(
96
+ (call) => call[0] === event)
97
+
98
+ const emit = call[1]
99
+ const args = [generate(), generate()]
100
+
101
+ emit(event, ...args)
102
+
103
+ expect(listener).toHaveBeenCalled()
104
+ expect(listener).toHaveBeenCalledWith(event, ...args, index)
105
+ })
106
+ })
package/types/amqp.d.ts CHANGED
@@ -5,11 +5,13 @@ declare namespace comq.amqp {
5
5
  namespace options {
6
6
  type Consume = amqplib.Options.Consume
7
7
  type Publish = amqplib.Options.Publish
8
+ type Exchange = amqplib.Options.AssertExchange
9
+ type Queue = amqplib.Options.AssertQueue
8
10
  }
9
11
 
10
12
  type Connection = amqplib.Connection
11
13
  type Channel = amqplib.Channel | amqplib.ConfirmChannel
12
-
14
+ type Message = amqplib.Message
13
15
  }
14
16
 
15
17
  export type Connection = comq.amqp.Connection
@@ -1,16 +1,19 @@
1
- import { ConsumeMessage, Options, Connection } from 'amqplib'
1
+ import { Message, Options, Connection } from 'amqplib'
2
2
  import * as _diagnostics from './diagnostic'
3
3
 
4
4
  declare namespace comq {
5
5
 
6
6
  namespace channels {
7
7
 
8
- type consumer = (message: ConsumeMessage) => void | Promise<void>
8
+ type consumer = (message: Message) => void | Promise<void>
9
9
 
10
10
  }
11
11
 
12
12
  interface Channel {
13
- create(connection: Connection): Promise<void>
13
+ index?: number
14
+ sharded?: boolean
15
+
16
+ create(): Promise<void>
14
17
 
15
18
  consume(queue: string, consumer: channels.consumer): Promise<void>
16
19
 
@@ -18,10 +21,10 @@ declare namespace comq {
18
21
 
19
22
  send(queue: string, buffer: Buffer, options?: Options.Publish): Promise<void>
20
23
 
21
- throw(queue: string, buffer: Buffer, options?: Options.Publish): Promise<void>
22
-
23
24
  publish(exchange: string, buffer: Buffer, options?: Options.Publish): Promise<void>
24
25
 
26
+ throw(queue: string, buffer: Buffer, options?: Options.Publish): Promise<void>
27
+
25
28
  seal(): Promise<void>
26
29
 
27
30
  diagnose(event: _diagnostics.event, listener: Function): void
@@ -12,10 +12,12 @@ declare namespace comq {
12
12
 
13
13
  createChannel(type: _topology.type): Promise<_channel.Channel>
14
14
 
15
+ createChannel(type: _topology.type, index: number): Promise<_channel.Channel>
16
+
15
17
  diagnose(event: _diagnostics.event, listener: Function): void
16
18
  }
17
19
 
18
- type connect = (url: string) => Promise<_io.IO>
20
+ type connect = (...urls: string[]) => Promise<_io.IO>
19
21
 
20
22
  }
21
23
 
@@ -1,6 +1,6 @@
1
1
  declare namespace comq.diagnostics {
2
2
 
3
- type event = 'open' | 'close' | 'flow' | 'drain' | 'recover' | 'discard'
3
+ type event = 'open' | 'close' | 'flow' | 'drain' | 'remove' | 'recover' | 'discard'
4
4
 
5
5
  }
6
6
 
@@ -4,6 +4,10 @@ declare namespace comq {
4
4
 
5
5
  type type = 'request' | 'reply' | 'event'
6
6
 
7
+ type Set = {
8
+ [K in type]: Topology
9
+ }
10
+
7
11
  }
8
12
 
9
13
  type Topology = {
File without changes
File without changes
File without changes
File without changes