@weave-js/redis-transport 0.13.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [0.14.0](https://github.com/weave-microservices/weave/compare/@weave-js/redis-transport@0.12.1...@weave-js/redis-transport@0.14.0) (2023-05-04)
7
+
8
+
9
+ * Release Version 0.14.0 (#279) ([12a003c](https://github.com/weave-microservices/weave/commit/12a003c05b2960b20810fbbad496258b5d4b0606)), closes [#279](https://github.com/weave-microservices/weave/issues/279) [#276](https://github.com/weave-microservices/weave/issues/276) [#278](https://github.com/weave-microservices/weave/issues/278)
10
+
11
+
12
+ ### BREAKING CHANGES
13
+
14
+ * Removed http error codes from errors and added correct codes
15
+
16
+ * chore: published 0.13.0
17
+
18
+
19
+
20
+
21
+
6
22
  # [0.13.0](https://github.com/weave-microservices/weave/compare/@weave-js/redis-transport@0.12.1...@weave-js/redis-transport@0.13.0) (2022-08-16)
7
23
 
8
24
  **Note:** Version bump only for package @weave-js/redis-transport
package/lib/index.js CHANGED
@@ -3,102 +3,139 @@
3
3
  * -----
4
4
  * Copyright 2021 Fachwerk
5
5
  */
6
-
7
- const redis = require('redis');
8
- const { defaultsDeep, promiseDelay } = require('@weave-js/utils');
9
- const { TransportAdapters } = require('@weave-js/core');
6
+ const redis = require('redis')
7
+ const { defaultsDeep } = require('@weave-js/utils')
8
+ const { TransportAdapters } = require('@weave-js/core')
10
9
 
11
10
  const defaultOptions = {
12
11
  port: 6379,
13
12
  host: '127.0.0.1'
14
- };
13
+ }
15
14
 
16
15
  const RedisTransportAdapter = (adapterOptions) => {
17
- let clientSub;
18
- let clientPub;
16
+ let clientSub
17
+ let clientPub
19
18
 
20
- // Merge options with default options.
21
- adapterOptions = defaultsDeep(adapterOptions, defaultOptions);
19
+ adapterOptions = defaultsDeep(adapterOptions, defaultOptions)
22
20
 
23
21
  return Object.assign(TransportAdapters.BaseAdapter(adapterOptions), {
24
22
  name: 'REDIS',
23
+
25
24
  connect () {
26
- return new Promise((resolve, reject) => {
27
- clientSub = redis.createClient(adapterOptions);
28
-
29
- clientSub.on('connect', () => {
30
- clientPub = redis.createClient(adapterOptions);
31
-
32
- this.log.info('Redis SUB client connected.');
33
-
34
- clientPub.on('connect', () => {
35
- if (this.interruptionCount > 0 && !this.isConnected) {
36
- this.bus.emit('adapter.connected', true);
37
- }
38
- this.log.info('Redis PUB client connected.');
39
- this.isConnected = true;
40
- resolve();
41
- });
42
-
43
- clientPub.on('error', error => {
44
- this.log.error('Redis PUB error:', error.message);
45
- this.isConnected = false;
46
- reject(error);
47
- });
48
-
49
- clientPub.on('close', () => {
50
- if (this.isConnected) {
51
- this.isConnected = false;
52
- this.interruptionCount++;
53
- this.log.warn('Redis PUB disconnected.');
54
- this.disconnected();
55
- }
56
- });
57
- });
58
-
59
- clientSub.on('error', error => {
60
- this.log.error('Redis PUB error:', error.message);
61
- reject(error);
62
- });
63
-
64
- clientSub.on('message', (topic, message) => {
65
- const type = topic.split('.')[1];
66
- this.incomingMessage(type, message);
67
- });
68
-
69
- clientSub.on('close', () => {
70
- this.log.warn('Redis SUB disconnected.');
71
- });
25
+ if (clientSub || clientPub) {
26
+ return Promise.reject(new Error('Adapter already connected.'))
27
+ }
28
+
29
+ // Ohne explizite Strategie gibt v3 je nach Fehlerbild auf,
30
+ // dann bleibt 'reconnecting' aus. Solange eine Zahl kommt, wird retried.
31
+ const retryStrategy = ({ attempt }) => Math.min(attempt * 100, 3000)
32
+
33
+ clientPub = redis.createClient({ ...adapterOptions, retry_strategy: retryStrategy })
34
+ clientSub = redis.createClient({
35
+ ...adapterOptions,
36
+ retry_strategy: retryStrategy,
37
+ return_buffers: true // binärsichere Payload + korrekte Byte-Statistik
72
38
  })
39
+
40
+ const isUp = () =>
41
+ Boolean(clientSub && clientSub.ready && clientPub && clientPub.ready)
42
+
43
+ const syncState = () => {
44
+ const up = isUp()
45
+ if (up === this.isConnected) return
46
+
47
+ this.isConnected = up
48
+
49
+ if (up) {
50
+ const attempts = this.repeatAttemptCounter
51
+ this.repeatAttemptCounter = 0
52
+ this.log.info(`Redis reconnected${attempts ? ` after ${attempts} attempts` : ''}.`)
53
+ this.connected({ wasReconnect: true })
54
+ } else {
55
+ this.interruptCounter++
56
+ this.log.warn(`Redis disconnected (SUB: ${clientSub.ready}, PUB: ${clientPub.ready}).`)
57
+ this.disconnected()
58
+ }
59
+ }
60
+
61
+ const waitForReady = (client) => new Promise((resolve, reject) => {
62
+ const onReady = () => { client.removeListener('error', onError); resolve() }
63
+ const onError = (error) => { client.removeListener('ready', onReady); reject(error) }
64
+ client.once('ready', onReady)
65
+ client.once('error', onError)
66
+ })
67
+
68
+ return Promise.all([waitForReady(clientPub), waitForReady(clientSub)])
73
69
  .then(() => {
74
- this.connected();
75
- });
70
+ this.isConnected = true
71
+
72
+ for (const [client, label] of [[clientSub, 'SUB'], [clientPub, 'PUB']]) {
73
+ client.on('ready', syncState)
74
+ client.on('end', syncState)
75
+
76
+ client.on('reconnecting', () => {
77
+ this.repeatAttemptCounter++
78
+ this.log.debug(`Redis ${label} reconnecting (attempt ${this.repeatAttemptCounter}).`)
79
+ syncState()
80
+ })
81
+
82
+ // Dauerhaft nötig: EventEmitter ohne error-Listener wirft.
83
+ client.on('error', (error) => {
84
+ const detail = !error
85
+ ? '(no error object)'
86
+ : error.message || error.code || error.constructor.name
87
+ this.log.error(`Redis ${label} error: ${detail}`)
88
+ syncState()
89
+ })
90
+ }
91
+
92
+ clientSub.on('message', (topic, message) => {
93
+ const type = topic.toString().split('.')[1]
94
+ this.incomingMessage(type, message)
95
+ })
96
+
97
+ this.log.info('Redis SUB and PUB clients connected.')
98
+ this.connected()
99
+ })
100
+ .catch((error) => {
101
+ if (clientSub) clientSub.end(true)
102
+ if (clientPub) clientPub.end(true)
103
+ clientSub = clientPub = null
104
+ throw error
105
+ })
76
106
  },
107
+
77
108
  subscribe (type, nodeId) {
78
- return new Promise(resolve => {
79
- const topic = this.getTopic(type, nodeId);
80
- clientSub.subscribe(topic, () => {
81
- return resolve();
82
- });
83
- });
109
+ return new Promise((resolve, reject) => {
110
+ const topic = this.getTopic(type, nodeId)
111
+ clientSub.subscribe(topic, (error) => error ? reject(error) : resolve())
112
+ })
84
113
  },
114
+
85
115
  send (message) {
86
- const data = this.serialize(message);
87
- if (this.isConnected) {
88
- this.updateStatisticSent(data.length);
89
- const topic = this.getTopic(message.type, message.targetNodeId);
90
- clientPub.publish(topic, data);
116
+ if (!this.isConnected) {
117
+ this.log.debug('Message dropped, adapter not connected.', { type: message.type })
118
+ return Promise.resolve()
91
119
  }
92
- return Promise.resolve();
120
+
121
+ const data = this.serialize(message)
122
+ if (!data) return Promise.resolve()
123
+
124
+ this.updateStatisticSent(data.length)
125
+ clientPub.publish(this.getTopic(message.type, message.targetNodeId), data)
126
+
127
+ return Promise.resolve()
93
128
  },
129
+
94
130
  close () {
95
- if (clientPub && clientSub) {
96
- clientPub.quit();
97
- clientSub.quit();
98
- }
99
- return promiseDelay(Promise.resolve(), 500);
131
+ const quit = (client) => client
132
+ ? new Promise((resolve) => client.quit(() => resolve()))
133
+ : Promise.resolve()
134
+
135
+ return Promise.all([quit(clientPub), quit(clientSub)])
136
+ .then(() => { clientSub = clientPub = null })
100
137
  }
101
- });
102
- };
138
+ })
139
+ }
103
140
 
104
- module.exports = RedisTransportAdapter;
141
+ module.exports = RedisTransportAdapter
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weave-js/redis-transport",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "weave redis transport adapter module",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -20,10 +20,10 @@
20
20
  },
21
21
  "license": "MIT",
22
22
  "peerDependencies": {
23
- "@weave-js/core": ">=0.9.0"
23
+ "@weave-js/core": "0.16.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@weave-js/core": "^0.13.0"
26
+ "@weave-js/core": "^0.16.0"
27
27
  },
28
28
  "jest": {
29
29
  "testEnvironment": "node",
@@ -33,8 +33,8 @@
33
33
  ]
34
34
  },
35
35
  "dependencies": {
36
- "@weave-js/utils": "^0.12.0",
36
+ "@weave-js/utils": "^0.13.0",
37
37
  "redis": "^3.1.2"
38
38
  },
39
- "gitHead": "6772c8bfb00be1da32c17cff621a8abcd98a08fc"
39
+ "gitHead": "84fd5e4af3af96e8c004bcfba40bb3cb35f23fa7"
40
40
  }
@@ -1,19 +1,43 @@
1
1
  const { Weave } = require('@weave-js/core');
2
2
  const REDISTransport = require('../lib/index');
3
3
 
4
+ const waitUntil = (predicate, timeout = 5000, interval = 25) =>
5
+ new Promise((resolve, reject) => {
6
+ const startedAt = Date.now();
7
+
8
+ const check = () => {
9
+ if (predicate()) {
10
+ return resolve();
11
+ }
12
+
13
+ if (Date.now() - startedAt > timeout) {
14
+ return reject(new Error('Timeout while waiting for condition'));
15
+ }
16
+
17
+ setTimeout(check, interval);
18
+ };
19
+
20
+ check();
21
+ });
22
+
4
23
  describe('REDIS transport adapter', () => {
5
- let broker1;
6
- let broker2;
24
+ const namespace = `redis-test-${process.pid}-${Date.now()}`;
7
25
  const startedHook1 = jest.fn();
8
26
  const startedHook2 = jest.fn();
27
+ const eventHandler = jest.fn();
28
+ const broadcastHandler1 = jest.fn();
29
+ const broadcastHandler2 = jest.fn();
9
30
 
10
- beforeEach(() => {
31
+ let broker1;
32
+ let broker2;
33
+
34
+ beforeAll(async () => {
11
35
  broker1 = Weave({
12
36
  nodeId: 'node1',
13
37
  logger: {
14
38
  enabled: false
15
39
  },
16
- namespace: 'redis-test',
40
+ namespace,
17
41
  transport: {
18
42
  adapter: REDISTransport()
19
43
  },
@@ -25,7 +49,13 @@ describe('REDIS transport adapter', () => {
25
49
  actions: {
26
50
  hello (context) {
27
51
  return 'Hello from ' + context.nodeId;
52
+ },
53
+ greet (context) {
54
+ return `Hello ${context.data.name}!`;
28
55
  }
56
+ },
57
+ events: {
58
+ 'user.broadcasted': broadcastHandler1
29
59
  }
30
60
  });
31
61
 
@@ -34,7 +64,7 @@ describe('REDIS transport adapter', () => {
34
64
  logger: {
35
65
  enabled: false
36
66
  },
37
- namespace: 'redis-test',
67
+ namespace,
38
68
  transport: {
39
69
  adapter: REDISTransport()
40
70
  },
@@ -46,34 +76,123 @@ describe('REDIS transport adapter', () => {
46
76
  actions: {
47
77
  hello () {
48
78
  return 'Hello from ' + this.broker.nodeId;
79
+ },
80
+ echo (context) {
81
+ return context.data;
82
+ },
83
+ fail () {
84
+ throw new Error('Expected failure');
49
85
  }
86
+ },
87
+ events: {
88
+ 'user.created': eventHandler,
89
+ 'user.broadcasted': broadcastHandler2
50
90
  }
51
91
  });
52
92
 
53
- return Promise.all([
54
- broker1.start(),
55
- broker2.start()
93
+ await Promise.all([broker1.start(), broker2.start()]);
94
+
95
+ await Promise.all([
96
+ broker1.waitForServices(['testService2']),
97
+ broker2.waitForServices(['testService1'])
56
98
  ]);
57
99
  });
58
100
 
59
- afterEach(() => {
101
+ afterAll(() => {
60
102
  return Promise.all([broker1.stop(), broker2.stop()]);
61
103
  });
62
104
 
63
- it('should connect', () => {
105
+ it('should connect and call the started hooks exactly once', () => {
64
106
  expect(startedHook1).toBeCalledTimes(1);
65
107
  expect(startedHook2).toBeCalledTimes(1);
66
108
  });
67
109
 
68
- it('should get node info', (done) => {
69
- broker1
70
- .waitForServices(['testService2'])
71
- .then(() => {
72
- return broker1.call('testService2.hello');
73
- })
74
- .then(result => {
75
- expect(result).toBe('Hello from node2');
76
- done();
77
- });
110
+ it('should discover the remote node', () => {
111
+ const node = broker1.runtime.registry.nodeCollection.get('node2');
112
+ expect(node).toBeDefined();
113
+ expect(node.isAvailable).toBe(true);
114
+ });
115
+
116
+ it('should call a remote action (node1 -> node2)', async () => {
117
+ const result = await broker1.call('testService2.hello');
118
+ expect(result).toBe('Hello from node2');
119
+ });
120
+
121
+ it('should call a remote action (node2 -> node1)', async () => {
122
+ const result = await broker2.call('testService1.hello');
123
+ expect(result).toBe('Hello from node1');
124
+ });
125
+
126
+ it('should pass parameters to a remote action', async () => {
127
+ const result = await broker2.call('testService1.greet', { name: 'Weave' });
128
+ expect(result).toBe('Hello Weave!');
129
+ });
130
+
131
+ it('should transfer complex payloads without losing data', async () => {
132
+ const payload = {
133
+ text: 'Hänsel & Grätel äöüß',
134
+ number: 42.5,
135
+ flag: true,
136
+ nothing: null,
137
+ nested: { list: [1, 2, 3], deep: { key: 'value' } }
138
+ };
139
+
140
+ const result = await broker1.call('testService2.echo', payload);
141
+ expect(result).toEqual(payload);
142
+ });
143
+
144
+ it('should propagate errors of remote actions to the caller', async () => {
145
+ await expect(broker1.call('testService2.fail'))
146
+ .rejects
147
+ .toThrow('Expected failure');
148
+ });
149
+
150
+ it('should reject calls to unknown actions', async () => {
151
+ await expect(broker1.call('testService2.unknownAction')).rejects.toThrow();
152
+ });
153
+
154
+ it('should deliver emitted events to the remote node', async () => {
155
+ eventHandler.mockClear();
156
+
157
+ broker1.emit('user.created', { id: 1 });
158
+
159
+ await waitUntil(() => eventHandler.mock.calls.length === 1);
160
+
161
+ const context = eventHandler.mock.calls[0][0];
162
+ expect(context.data).toEqual({ id: 1 });
163
+ expect(context.callerNodeId).toBe('node1');
164
+ });
165
+
166
+ it('should deliver broadcast events to all nodes', async () => {
167
+ broadcastHandler1.mockClear();
168
+ broadcastHandler2.mockClear();
169
+
170
+ broker1.broadcast('user.broadcasted', { id: 2 });
171
+
172
+ await waitUntil(() =>
173
+ broadcastHandler1.mock.calls.length === 1 &&
174
+ broadcastHandler2.mock.calls.length === 1
175
+ );
176
+
177
+ const remoteContext = broadcastHandler2.mock.calls[0][0];
178
+ expect(remoteContext.data).toEqual({ id: 2 });
179
+ });
180
+
181
+ it('should handle multiple parallel remote calls', async () => {
182
+ const results = await Promise.all(
183
+ Array.from({ length: 25 }, (_, index) =>
184
+ broker1.call('testService2.echo', { index })
185
+ )
186
+ );
187
+
188
+ results.forEach((result, index) => {
189
+ expect(result).toEqual({ index });
190
+ });
191
+ });
192
+
193
+ it('should respond to ping messages of remote nodes', async () => {
194
+ const result = await broker1.ping('node2');
195
+ expect(result.nodeId).toBe('node2');
196
+ expect(typeof result.elapsedTime).toBe('number');
78
197
  });
79
198
  });
@@ -0,0 +1,203 @@
1
+ const RedisTransportAdapter = require('../lib/index');
2
+
3
+ const createMockLog = () => ({
4
+ info: jest.fn(),
5
+ debug: jest.fn(),
6
+ warn: jest.fn(),
7
+ error: jest.fn()
8
+ });
9
+
10
+ const createMockRuntime = ({ namespace, nodeId = 'unit-node' } = {}) => {
11
+ const log = createMockLog();
12
+
13
+ const broker = {
14
+ nodeId,
15
+ options: { namespace },
16
+ handleError (error) {
17
+ throw error;
18
+ }
19
+ };
20
+
21
+ const transport = {
22
+ log,
23
+ statistics: {
24
+ sent: { packages: 0 },
25
+ received: { packages: 0 }
26
+ }
27
+ };
28
+
29
+ return { broker, transport, log };
30
+ };
31
+
32
+ const initAdapter = async (adapter, runtimeOptions) => {
33
+ const runtime = createMockRuntime(runtimeOptions);
34
+ await adapter.init(runtime.broker, runtime.transport, () => {});
35
+ return runtime;
36
+ };
37
+
38
+ const waitForBusEvent = (adapter, eventName, timeout = 5000) =>
39
+ new Promise((resolve, reject) => {
40
+ const timer = setTimeout(
41
+ () => reject(new Error(`Timeout waiting for bus event "${eventName}"`)),
42
+ timeout
43
+ );
44
+
45
+ adapter.bus.once(eventName, (...args) => {
46
+ clearTimeout(timer);
47
+ resolve(args);
48
+ });
49
+ });
50
+
51
+ // The connect() promise of the adapter is resolved through the
52
+ // "$adapter.connected" bus event, so we wait for the event instead.
53
+ const connectAdapter = (adapter) => {
54
+ const connected = waitForBusEvent(adapter, '$adapter.connected');
55
+ adapter.connect();
56
+ return connected;
57
+ };
58
+
59
+ describe('REDIS adapter (unit)', () => {
60
+ let adapter;
61
+
62
+ afterEach(async () => {
63
+ if (adapter) {
64
+ await adapter.close();
65
+ adapter = null;
66
+ }
67
+ });
68
+
69
+ it('should expose the adapter name "REDIS"', () => {
70
+ adapter = RedisTransportAdapter();
71
+ expect(adapter.name).toBe('REDIS');
72
+ });
73
+
74
+ it('should not be connected initially', () => {
75
+ adapter = RedisTransportAdapter();
76
+ expect(adapter.isConnected).toBe(false);
77
+ expect(adapter.interruptCounter).toBe(0);
78
+ expect(adapter.repeatAttemptCounter).toBe(0);
79
+ });
80
+
81
+ it('should build topics with the namespace prefix', async () => {
82
+ adapter = RedisTransportAdapter();
83
+ await initAdapter(adapter, { namespace: 'unit-ns' });
84
+
85
+ expect(adapter.getTopic('INFO', 'node1')).toBe('weave-unit-ns.INFO.node1');
86
+ expect(adapter.getTopic('DISCOVERY')).toBe('weave-unit-ns.DISCOVERY');
87
+ });
88
+
89
+ it('should build topics without a namespace', async () => {
90
+ adapter = RedisTransportAdapter();
91
+ await initAdapter(adapter, {});
92
+
93
+ expect(adapter.getTopic('INFO', 'node1')).toBe('weave.INFO.node1');
94
+ });
95
+
96
+ it('should resolve and drop the message when sending while disconnected', async () => {
97
+ adapter = RedisTransportAdapter();
98
+ const { log } = await initAdapter(adapter, { namespace: 'unit-send-disconnected' });
99
+
100
+ await adapter.send({ type: 'INFO', targetNodeId: 'node2', payload: {} });
101
+
102
+ expect(log.debug).toBeCalledTimes(1);
103
+ expect(log.debug).toBeCalledWith(
104
+ 'Message dropped, adapter is not connected',
105
+ { type: 'INFO' }
106
+ );
107
+ });
108
+
109
+ it('should resolve close() even if the adapter was never connected', async () => {
110
+ adapter = RedisTransportAdapter();
111
+ await initAdapter(adapter, { namespace: 'unit-close' });
112
+ await adapter.close();
113
+ adapter = null;
114
+ });
115
+
116
+ it('should connect to a running redis server and emit "$adapter.connected"', async () => {
117
+ adapter = RedisTransportAdapter();
118
+ await initAdapter(adapter, { namespace: 'unit-connect' });
119
+
120
+ await connectAdapter(adapter);
121
+
122
+ expect(adapter.isConnected).toBe(true);
123
+ });
124
+
125
+ it('should merge the adapter options with the default options', async () => {
126
+ // Explicit options matching the defaults must work the same way.
127
+ adapter = RedisTransportAdapter({ port: 6379, host: '127.0.0.1' });
128
+ await initAdapter(adapter, { namespace: 'unit-options' });
129
+
130
+ await connectAdapter(adapter);
131
+
132
+ expect(adapter.isConnected).toBe(true);
133
+ });
134
+
135
+ it('should deliver published messages to subscribers of the topic', async () => {
136
+ adapter = RedisTransportAdapter();
137
+ const { broker } = await initAdapter(adapter, { namespace: `unit-roundtrip-${Date.now()}` });
138
+
139
+ await connectAdapter(adapter);
140
+ await adapter.subscribe('EVENT', 'node2');
141
+
142
+ const incoming = waitForBusEvent(adapter, '$adapter.message');
143
+ await adapter.send({
144
+ type: 'EVENT',
145
+ targetNodeId: 'node2',
146
+ payload: { eventName: 'user.created' }
147
+ });
148
+
149
+ const [messageType, data] = await incoming;
150
+
151
+ expect(messageType).toBe('EVENT');
152
+ expect(data.payload.eventName).toBe('user.created');
153
+ expect(data.payload.sender).toBe(broker.nodeId);
154
+ });
155
+
156
+ it('should update the sent and received statistics', async () => {
157
+ adapter = RedisTransportAdapter();
158
+ const { transport } = await initAdapter(adapter, { namespace: `unit-stats-${Date.now()}` });
159
+
160
+ await connectAdapter(adapter);
161
+ await adapter.subscribe('HEARTBEAT');
162
+
163
+ const incoming = waitForBusEvent(adapter, '$adapter.message');
164
+ await adapter.send({ type: 'HEARTBEAT', payload: {} });
165
+ await incoming;
166
+
167
+ expect(transport.statistics.sent.packages).toBeGreaterThan(0);
168
+ expect(transport.statistics.received.packages).toBeGreaterThan(0);
169
+ });
170
+
171
+ it('should not deliver messages of topics without a subscription', async () => {
172
+ adapter = RedisTransportAdapter();
173
+ await initAdapter(adapter, { namespace: `unit-no-sub-${Date.now()}` });
174
+
175
+ await connectAdapter(adapter);
176
+ await adapter.subscribe('INFO');
177
+
178
+ const received = [];
179
+ adapter.bus.on('$adapter.message', (type) => received.push(type));
180
+
181
+ await adapter.send({ type: 'HEARTBEAT', payload: {} });
182
+
183
+ const incoming = waitForBusEvent(adapter, '$adapter.message');
184
+ await adapter.send({ type: 'INFO', payload: {} });
185
+ await incoming;
186
+
187
+ expect(received).toEqual(['INFO']);
188
+ });
189
+
190
+ it('should not receive its own messages after close()', async () => {
191
+ adapter = RedisTransportAdapter();
192
+ await initAdapter(adapter, { namespace: `unit-closed-${Date.now()}` });
193
+
194
+ await connectAdapter(adapter);
195
+ await adapter.subscribe('INFO');
196
+ await adapter.close();
197
+
198
+ // After close the clients are gone; sending must still resolve without throwing.
199
+ adapter.isConnected = false;
200
+ await adapter.send({ type: 'INFO', payload: {} });
201
+ adapter = null;
202
+ });
203
+ });
@@ -1,35 +0,0 @@
1
- {
2
- // Verwendet IntelliSense zum Ermitteln möglicher Attribute.
3
- // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
4
- // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
5
- "version": "0.2.0",
6
- "configurations": [
7
-
8
- {
9
- "type": "node",
10
- "request": "launch",
11
- "name": "Jest All",
12
- "program": "${workspaceFolder}/node_modules/.bin/jest",
13
- "args": ["--runInBand"],
14
- "console": "integratedTerminal",
15
- "internalConsoleOptions": "neverOpen",
16
- "disableOptimisticBPs": true,
17
- "windows": {
18
- "program": "${workspaceFolder}/node_modules/jest/bin/jest",
19
- }
20
- },
21
- {
22
- "type": "node",
23
- "request": "launch",
24
- "name": "Jest Current File",
25
- "program": "${workspaceFolder}/node_modules/.bin/jest",
26
- "args": ["${relativeFile}"],
27
- "console": "integratedTerminal",
28
- "internalConsoleOptions": "neverOpen",
29
- "disableOptimisticBPs": true,
30
- "windows": {
31
- "program": "${workspaceFolder}/node_modules/jest/bin/jest",
32
- }
33
- }
34
- ]
35
- }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2020 Fachwerk Software
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.