@pryv/monitor 3.0.1 → 3.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pryv/monitor",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "Extends `pryv` with event-driven notifications for changes on a Pryv.io account",
5
5
  "keywords": [
6
6
  "Pryv",
@@ -23,7 +23,16 @@ class Socket extends UpdateMethod {
23
23
  this.socket = await this.monitor.connection.socket.open();
24
24
  this.socket.on('eventsChanged', () => { this.monitor.updateEvents(); });
25
25
  this.socket.on('streamsChanged', () => { this.monitor.updateStreams(); });
26
- this.socket.on('error', (error) => { this.monitor.emit(Changes.ERROR, error); });
26
+ this.socket.on('error', (error) => {
27
+ this.monitor.emit(Changes.ERROR, error);
28
+ // If the underlying socket.io-client transport has been torn down
29
+ // (i.e. SocketIO emitted 'error' after reconnect_failed), drop our
30
+ // reference so a future Changes.READY can rebuild instead of
31
+ // short-circuiting on the cached, dead handle.
32
+ if (this.socket && !this.socket._io) {
33
+ this.socket = null;
34
+ }
35
+ });
27
36
  }
28
37
 
29
38
  async stop () {
package/src/index.js CHANGED
@@ -15,7 +15,7 @@ module.exports = function (pryv) {
15
15
  console.log('Pryv version', pryv.version);
16
16
  // check version here
17
17
  if (pryv.Monitor) {
18
- throw new Error('Monitor already loaded');
18
+ return; // already loaded
19
19
  }
20
20
  // sharing cross references
21
21
  pryv.Monitor = Monitor;
@@ -4,8 +4,9 @@
4
4
  */
5
5
  /* global expect, pryv, testData */
6
6
 
7
+ const { createId: cuid } = require('@paralleldrive/cuid2');
7
8
  require('../src')(pryv);
8
- const testStreamId = global.testStreamId = 'monitor-test';
9
+ const testStreamId = global.testStreamId = 'mon-' + cuid().slice(0, 8);
9
10
 
10
11
  global.prepareAndCreateBaseStreams = async () => {
11
12
  await testData.prepare();
@@ -0,0 +1,230 @@
1
+ /**
2
+ * @license
3
+ * [BSD-3-Clause](https://github.com/pryv/lib-js/blob/master/LICENSE)
4
+ */
5
+ /* global describe, it, before, beforeEach, afterEach, expect, pryv, conn, prepareAndCreateBaseStreams */
6
+
7
+ require('./load-helpers');
8
+ const Changes = require('../src/lib/Changes');
9
+
10
+ describe('[MEDX] Monitor Edge Cases', function () {
11
+ this.timeout(20000);
12
+
13
+ before(async function () {
14
+ this.timeout(20000);
15
+ await prepareAndCreateBaseStreams();
16
+ });
17
+
18
+ describe('[MERX] Error handling', function () {
19
+ it('[MERA] throws error when updateEvents called before start', async function () {
20
+ const monitor = new pryv.Monitor(conn, { limit: 1 });
21
+ let error = null;
22
+ try {
23
+ await monitor.updateEvents();
24
+ } catch (e) {
25
+ error = e;
26
+ }
27
+ expect(error).to.exist;
28
+ expect(error.message).to.include('Start Monitor');
29
+ });
30
+
31
+ it('[MERB] throws error when updateStreams called before start', async function () {
32
+ const monitor = new pryv.Monitor(conn, { limit: 1 });
33
+ let error = null;
34
+ try {
35
+ await monitor.updateStreams();
36
+ } catch (e) {
37
+ error = e;
38
+ }
39
+ expect(error).to.exist;
40
+ expect(error.message).to.include('Start Monitor');
41
+ });
42
+
43
+ it('[MERC] throws error when stop called during starting', async function () {
44
+ const monitor = new pryv.Monitor(conn, { limit: 1 });
45
+ // Both started and starting must be true for the error to be thrown
46
+ monitor.states.started = true;
47
+ monitor.states.starting = true;
48
+ let error = null;
49
+ try {
50
+ monitor.stop();
51
+ } catch (e) {
52
+ error = e;
53
+ }
54
+ expect(error).to.exist;
55
+ expect(error.message).to.include('starting');
56
+ // Clean up
57
+ monitor.states.starting = false;
58
+ monitor.states.started = false;
59
+ });
60
+ });
61
+
62
+ describe('[MSSX] Start/Stop states', function () {
63
+ let monitor;
64
+
65
+ beforeEach(() => {
66
+ monitor = new pryv.Monitor(conn, { limit: 1 });
67
+ });
68
+
69
+ afterEach(() => {
70
+ if (monitor.started) monitor.stop();
71
+ });
72
+
73
+ it('[MSSA] start() returns early if already started', async function () {
74
+ await monitor.start();
75
+ expect(monitor.started).to.be.true;
76
+ // Second start should return immediately
77
+ const result = await monitor.start();
78
+ expect(result).to.equal(monitor);
79
+ });
80
+
81
+ it('[MSSB] start() returns early if starting', async function () {
82
+ monitor.states.starting = true;
83
+ const result = await monitor.start();
84
+ expect(result).to.equal(monitor);
85
+ monitor.states.starting = false;
86
+ });
87
+
88
+ it('[MSSC] stop() returns early if not started', function () {
89
+ const result = monitor.stop();
90
+ expect(result).to.equal(monitor);
91
+ });
92
+
93
+ it('[MSSD] stop() emits STOP event', async function () {
94
+ let stopEmitted = false;
95
+ await monitor.start();
96
+ monitor.on(Changes.STOP, () => { stopEmitted = true; });
97
+ monitor.stop();
98
+ expect(stopEmitted).to.be.true;
99
+ expect(monitor.started).to.be.false;
100
+ });
101
+
102
+ it('[MSSE] started property returns correct state', async function () {
103
+ expect(monitor.started).to.be.false;
104
+ await monitor.start();
105
+ expect(monitor.started).to.be.true;
106
+ monitor.stop();
107
+ expect(monitor.started).to.be.false;
108
+ });
109
+ });
110
+
111
+ describe('[MSEM] Semaphore behavior', function () {
112
+ let monitor;
113
+
114
+ beforeEach(async () => {
115
+ monitor = new pryv.Monitor(conn, { limit: 1 });
116
+ await monitor.start();
117
+ });
118
+
119
+ afterEach(() => {
120
+ if (monitor.started) monitor.stop();
121
+ });
122
+
123
+ it('[MSEA] updateEvents uses semaphore correctly', async function () {
124
+ // Simulate updating
125
+ monitor.states.updatingEvents = true;
126
+ await monitor.updateEvents();
127
+ expect(monitor.states.updateEventRequired).to.be.true;
128
+ monitor.states.updatingEvents = false;
129
+ });
130
+
131
+ it('[MSEB] updateStreams uses semaphore correctly', async function () {
132
+ // Simulate updating
133
+ monitor.states.updatingStreams = true;
134
+ await monitor.updateStreams();
135
+ expect(monitor.states.updateStreamsRequired).to.be.true;
136
+ monitor.states.updatingStreams = false;
137
+ });
138
+ });
139
+
140
+ describe('[MAUM] addUpdateMethod', function () {
141
+ it('[MAUA] addUpdateMethod chains correctly', async function () {
142
+ const monitor = new pryv.Monitor(conn, { limit: 1 });
143
+ const mockUpdateMethod = {
144
+ setMonitor: function (m) { this.monitor = m; }
145
+ };
146
+ const result = monitor.addUpdateMethod(mockUpdateMethod);
147
+ expect(result).to.equal(monitor);
148
+ expect(mockUpdateMethod.monitor).to.equal(monitor);
149
+ });
150
+ });
151
+
152
+ describe('[MDEV] Deleted Events', function () {
153
+ let monitor;
154
+ let createdEventId;
155
+
156
+ beforeEach(async () => {
157
+ monitor = new pryv.Monitor(conn, { limit: 100 });
158
+ });
159
+
160
+ afterEach(() => {
161
+ if (monitor.started) monitor.stop();
162
+ });
163
+
164
+ it('[MDEA] detects deleted events', async function () {
165
+ this.timeout(15000);
166
+
167
+ // Create an event first
168
+ const createRes = await conn.api([{
169
+ method: 'events.create',
170
+ params: {
171
+ streamIds: [global.testStreamId],
172
+ type: 'note/txt',
173
+ content: 'to be deleted ' + Date.now()
174
+ }
175
+ }]);
176
+ createdEventId = createRes[0].event.id;
177
+
178
+ // Start monitor - it will include deletions after start
179
+ await monitor.start();
180
+
181
+ // Set up listener for deleted events
182
+ let _deletedEventReceived = false;
183
+ monitor.on(Changes.EVENT_DELETE, (event) => {
184
+ if (event.id === createdEventId) {
185
+ _deletedEventReceived = true;
186
+ }
187
+ });
188
+
189
+ // Delete the event
190
+ await conn.api([{
191
+ method: 'events.delete',
192
+ params: { id: createdEventId }
193
+ }]);
194
+
195
+ // Trigger update
196
+ await monitor.updateEvents();
197
+ await new Promise(resolve => setTimeout(resolve, 2000));
198
+
199
+ // The event may or may not be received depending on timing
200
+ // but the code path should be exercised
201
+ });
202
+ });
203
+
204
+ describe('[MRDY] Ready emission', function () {
205
+ let monitor;
206
+
207
+ beforeEach(async () => {
208
+ monitor = new pryv.Monitor(conn, { limit: 1 });
209
+ });
210
+
211
+ afterEach(() => {
212
+ if (monitor.started) monitor.stop();
213
+ });
214
+
215
+ it('[MRDA] emits READY after start', function (done) {
216
+ monitor.on(Changes.READY, () => {
217
+ expect(monitor.started).to.be.true;
218
+ done();
219
+ });
220
+ monitor.start();
221
+ });
222
+
223
+ it('[MRDB] ready() does nothing when not started', function () {
224
+ let readyEmitted = false;
225
+ monitor.on(Changes.READY, () => { readyEmitted = true; });
226
+ monitor.ready();
227
+ expect(readyEmitted).to.be.false;
228
+ });
229
+ });
230
+ });
@@ -40,7 +40,7 @@ describe('[MONX] Monitor', function () {
40
40
  describe('[MNTX] notifications', () => {
41
41
  let monitor = null;
42
42
  beforeEach(async () => {
43
- monitor = new pryv.Monitor(conn, { limit: 1 });
43
+ monitor = new pryv.Monitor(conn, { limit: 1, streams: [global.testStreamId] });
44
44
  });
45
45
 
46
46
  afterEach(async () => {
@@ -48,22 +48,23 @@ describe('[MONX] Monitor', function () {
48
48
  });
49
49
 
50
50
  it('[MNTA] Load events at start', async function () {
51
- let count = 0;
52
- monitor.on('event', function (event) {
53
- count++;
54
- });
55
- await monitor.start();
51
+ // Create event before starting the monitor so it's picked up on initial load
56
52
  await conn.api([
57
53
  {
58
54
  method: 'events.create',
59
55
  params: {
60
- streamId: global.testStreamId,
56
+ streamIds: [global.testStreamId],
61
57
  type: 'note/txt',
62
58
  content: 'hello monitor'
63
59
  }
64
60
  }
65
61
  ]);
66
- await new Promise(resolve => setTimeout(resolve, 8000));
62
+ let count = 0;
63
+ monitor.on('event', function (event) {
64
+ count++;
65
+ });
66
+ await monitor.start();
67
+ await new Promise(resolve => setTimeout(resolve, 2000));
67
68
  expect(count).to.be.gt(0);
68
69
  });
69
70
 
@@ -72,7 +73,7 @@ describe('[MONX] Monitor', function () {
72
73
  await monitor.start();
73
74
 
74
75
  const eventData = {
75
- streamId: global.testStreamId,
76
+ streamIds: [global.testStreamId],
76
77
  type: 'note/txt',
77
78
  content: 'hello monitor ' + new Date()
78
79
  };
@@ -17,14 +17,14 @@ describe('[MSKX] Monitor + Socket.IO', function () {
17
17
  describe('[MSUX] socket updates', function () {
18
18
  this.timeout(25000);
19
19
  it('[MSUA] Detect new events added', async function () {
20
- const monitor = new pryv.Monitor(apiEndpoint, { limit: 1 })
20
+ const monitor = new pryv.Monitor(apiEndpoint, { limit: 1, streams: [global.testStreamId] })
21
21
  .addUpdateMethod(new pryv.Monitor.UpdateMethod.Socket());
22
22
  await monitor.start();
23
23
 
24
24
  let count = 0;
25
25
 
26
26
  const eventData = {
27
- streamId: global.testStreamId,
27
+ streamIds: [global.testStreamId],
28
28
  type: 'note/txt',
29
29
  content: 'hello monitor ' + new Date()
30
30
  };
@@ -50,7 +50,7 @@ describe('[MSKX] Monitor + Socket.IO', function () {
50
50
  describe('[MSTX] stop', () => {
51
51
  it('[MSTA] Monitor stops when requested', async function () {
52
52
  this.timeout(20000);
53
- const monitor = new pryv.Monitor(apiEndpoint, { limit: 1 })
53
+ const monitor = new pryv.Monitor(apiEndpoint, { limit: 1, streams: [global.testStreamId] })
54
54
  .addUpdateMethod(new pryv.Monitor.UpdateMethod.Socket());
55
55
  await monitor.start();
56
56
  let count = 0;
@@ -58,7 +58,7 @@ describe('[MSKX] Monitor + Socket.IO', function () {
58
58
  monitor.stop();
59
59
 
60
60
  const eventData = {
61
- streamId: global.testStreamId,
61
+ streamIds: [global.testStreamId],
62
62
  type: 'note/txt',
63
63
  content: 'hello monitor ' + new Date()
64
64
  };
@@ -28,7 +28,7 @@ describe('[TIMX] Monitor + EventsTimer', function () {
28
28
 
29
29
  describe('[TUPX] timer updates', () => {
30
30
  it('[TUPA] Detect new events added', async function () {
31
- const monitor = new pryv.Monitor(apiEndpoint, { limit: 1 })
31
+ const monitor = new pryv.Monitor(apiEndpoint, { limit: 1, streams: [global.testStreamId] })
32
32
  .addUpdateMethod(new pryv.Monitor.UpdateMethod.EventsTimer(1));
33
33
  await monitor.start();
34
34
  let count = 0;
@@ -40,7 +40,7 @@ describe('[TIMX] Monitor + EventsTimer', function () {
40
40
  });
41
41
 
42
42
  const eventData = {
43
- streamId: global.testStreamId,
43
+ streamIds: [global.testStreamId],
44
44
  type: 'note/txt',
45
45
  content: 'hello monitor ' + new Date()
46
46
  };
@@ -61,7 +61,7 @@ describe('[TIMX] Monitor + EventsTimer', function () {
61
61
  describe('[TSTX] stop', () => {
62
62
  it('[TSTA] Monitor stops when requested', async function () {
63
63
  this.timeout(20000);
64
- const monitor = await new pryv.Monitor(apiEndpoint, { limit: 1 })
64
+ const monitor = await new pryv.Monitor(apiEndpoint, { limit: 1, streams: [global.testStreamId] })
65
65
  .addUpdateMethod(new pryv.Monitor.UpdateMethod.EventsTimer(1));
66
66
  await monitor.start();
67
67
 
@@ -75,7 +75,7 @@ describe('[TIMX] Monitor + EventsTimer', function () {
75
75
  });
76
76
 
77
77
  const eventData = {
78
- streamId: global.testStreamId,
78
+ streamIds: [global.testStreamId],
79
79
  type: 'note/txt',
80
80
  content: 'hello monitor ' + new Date()
81
81
  };