anear-js-api 0.3.30 → 0.3.32
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/lib/messaging/AnearMessaging.js +16 -23
- package/lib/models/AnearEvent.js +26 -8
- package/lib/models/AnearParticipant.js +25 -4
- package/lib/utils/Participants.js +63 -53
- package/package.json +1 -1
- package/tests/AnearEvent.test.js +57 -34
- package/tests/AnearParticipant.test.js +9 -4
- package/tests/Participants.test.js +71 -53
- package/tests/fixtures/ParticipantsFixture.js +100 -105
- package/tests/utils/AnearParticipantJSONBuilder.js +66 -0
|
@@ -155,11 +155,11 @@ class AnearMessaging {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
async getAnearEventFromStorage(eventId) {
|
|
158
|
-
return await this.AnearEventClass.getFromStorage(eventId, this)
|
|
158
|
+
return await this.AnearEventClass.getFromStorage(eventId, this.AnearParticipantClass, this)
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
async getAnearParticipantFromStorage(participantId) {
|
|
162
|
-
return await this.AnearParticipantClass.getFromStorage(participantId)
|
|
161
|
+
async getAnearParticipantFromStorage(participantId, anearEvent) {
|
|
162
|
+
return await this.AnearParticipantClass.getFromStorage(participantId, anearEvent)
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
async initEventRealtimeMessaging(anearEvent) {
|
|
@@ -194,7 +194,7 @@ class AnearMessaging {
|
|
|
194
194
|
|
|
195
195
|
try {
|
|
196
196
|
const eventJson = JSON.parse(message.data)
|
|
197
|
-
const anearEvent = new this.AnearEventClass(eventJson, this)
|
|
197
|
+
const anearEvent = new this.AnearEventClass(eventJson, this.AnearParticipantClass, this)
|
|
198
198
|
|
|
199
199
|
//
|
|
200
200
|
// if we are getting this event create message from history after a quick restart,
|
|
@@ -246,12 +246,13 @@ class AnearMessaging {
|
|
|
246
246
|
|
|
247
247
|
for (const eventData of events) {
|
|
248
248
|
const eventJson = await this.api.getEvent(eventData.id)
|
|
249
|
-
const anearEvent = new this.AnearEventClass(eventJson, this)
|
|
250
|
-
//
|
|
251
|
-
//
|
|
249
|
+
const anearEvent = new this.AnearEventClass(eventJson, this.AnearParticipantClass, this)
|
|
250
|
+
// This needs work!!
|
|
251
|
+
// loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
|
|
252
|
+
// await this.refreshActiveParticipants(loadedEvent)
|
|
253
|
+
// await this.initEventRealtimeMessaging(loadedEvent)
|
|
254
|
+
// loadedEvent.startStateMachine()
|
|
252
255
|
//
|
|
253
|
-
// const loadedEvent = await this.loadOrPersistEventAndInitialize(anearEvent)
|
|
254
|
-
// await this.refreshActiveParticipants(loadedEvent) DOES NOT WORK YET
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
258
|
} catch (err) {
|
|
@@ -260,16 +261,8 @@ class AnearMessaging {
|
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
async refreshActiveParticipants(anearEvent) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return Promise.all(
|
|
266
|
-
allParticipants.map(
|
|
267
|
-
participant => this.processParticipantEnter(
|
|
268
|
-
anearEvent,
|
|
269
|
-
participant.id,
|
|
270
|
-
participant.geoLocation
|
|
271
|
-
)
|
|
272
|
-
)
|
|
264
|
+
await this.participants.reloadFromStorage(
|
|
265
|
+
p => this.processParticipantEnter(anearEvent, p.id, p.geoLocation)
|
|
273
266
|
)
|
|
274
267
|
}
|
|
275
268
|
|
|
@@ -416,11 +409,11 @@ class AnearMessaging {
|
|
|
416
409
|
//
|
|
417
410
|
try {
|
|
418
411
|
const participantJson = await this.api.getEventParticipantJson(participantId)
|
|
419
|
-
const participant = new this.AnearParticipantClass(participantJson)
|
|
412
|
+
const participant = new this.AnearParticipantClass(participantJson, anearEvent)
|
|
420
413
|
|
|
421
414
|
participant.geoLocation = geoLocation
|
|
422
415
|
|
|
423
|
-
const persistedAnearParticipant = await this.
|
|
416
|
+
const persistedAnearParticipant = await this.getAnearParticipantFromStorage(participantId, anearEvent)
|
|
424
417
|
|
|
425
418
|
if (persistedAnearParticipant) {
|
|
426
419
|
participant.context = persistedAnearParticipant.context
|
|
@@ -471,7 +464,7 @@ class AnearMessaging {
|
|
|
471
464
|
|
|
472
465
|
this.destroyParticipantTimer(participantId)
|
|
473
466
|
|
|
474
|
-
const participant = await this.getAnearParticipantFromStorage(participantId)
|
|
467
|
+
const participant = await this.getAnearParticipantFromStorage(participantId, anearEvent)
|
|
475
468
|
|
|
476
469
|
if (participant) {
|
|
477
470
|
await this.detachParticipantPrivateChannel(anearEvent.id, participant)
|
|
@@ -515,7 +508,7 @@ class AnearMessaging {
|
|
|
515
508
|
const actionJSON = JSON.parse(payload)
|
|
516
509
|
const [actionEventName, actionPayload] = Object.entries(actionJSON)[0]
|
|
517
510
|
|
|
518
|
-
const participant = await this.getAnearParticipantFromStorage(participantId)
|
|
511
|
+
const participant = await this.getAnearParticipantFromStorage(participantId, anearEvent)
|
|
519
512
|
|
|
520
513
|
await this.runExclusive("participantActionCallback", async () => {
|
|
521
514
|
await anearEvent.participantAction(participant, actionEventName, actionPayload)
|
package/lib/models/AnearEvent.js
CHANGED
|
@@ -11,21 +11,21 @@ const PrivateDisplayMessageType = 'private_display'
|
|
|
11
11
|
|
|
12
12
|
class AnearEvent extends JsonApiResource {
|
|
13
13
|
|
|
14
|
-
constructor(json, messaging) {
|
|
14
|
+
constructor(json, anearParticipantClass, messaging) {
|
|
15
15
|
super(json)
|
|
16
|
-
|
|
17
|
-
this.messaging = messaging
|
|
18
16
|
this.zone = this.findIncluded(this.relationships.zone)
|
|
19
17
|
this.app = this.findIncluded(this.zone.relationships.app)
|
|
20
|
-
this.
|
|
18
|
+
this.anearParticipantClass = anearParticipantClass
|
|
19
|
+
this.messaging = messaging
|
|
21
20
|
this.anearStateMachine = this.initStateMachine(json.previousState)
|
|
21
|
+
this.participants = new Participants(this, json.participants)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
toJSON() {
|
|
25
25
|
return {
|
|
26
26
|
...super.toJSON(),
|
|
27
|
-
context: this.stateMachineContext,
|
|
28
27
|
participants: this.participants.toJSON(),
|
|
28
|
+
context: this.stateMachineContext,
|
|
29
29
|
previousState: this.anearStateMachine.currentState
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -207,13 +207,13 @@ class AnearEvent extends JsonApiResource {
|
|
|
207
207
|
if (this.participants.exists(participant)) {
|
|
208
208
|
logger.info(`AnearEvent: participant ${participant.id} exists. Refreshing...`)
|
|
209
209
|
|
|
210
|
-
this.participants.add(
|
|
210
|
+
this.participants.add(participant) // update the participants entry
|
|
211
211
|
|
|
212
212
|
this.anearStateMachine.sendRefreshEvent({ participant })
|
|
213
213
|
|
|
214
214
|
await participant.update()
|
|
215
215
|
} else {
|
|
216
|
-
this.participants.add(
|
|
216
|
+
this.participants.add(participant) // add the participants entry
|
|
217
217
|
|
|
218
218
|
this.anearStateMachine.sendJoinEvent({ participant })
|
|
219
219
|
|
|
@@ -301,7 +301,7 @@ class AnearEvent extends JsonApiResource {
|
|
|
301
301
|
return Promise.all(
|
|
302
302
|
this.participants.all.map(
|
|
303
303
|
async p => {
|
|
304
|
-
const participant = await this.getAnearParticipantFromStorage(p.id)
|
|
304
|
+
const participant = await this.messaging.getAnearParticipantFromStorage(p.id)
|
|
305
305
|
await this.messaging.closeParticipant(
|
|
306
306
|
this,
|
|
307
307
|
participant.id,
|
|
@@ -314,6 +314,24 @@ class AnearEvent extends JsonApiResource {
|
|
|
314
314
|
)
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
async purgeParticipants() {
|
|
318
|
+
// remove participants from Participants class and from Storage
|
|
319
|
+
const all = this.participants.all
|
|
320
|
+
if (this.participants.host) all.push(this.participants.host)
|
|
321
|
+
|
|
322
|
+
await Promise.all(
|
|
323
|
+
all.map(p => this.participantPurge(p))
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async reloadParticipantsFromStorage() {
|
|
328
|
+
// rehydrate the anearEvent participants and host from storage
|
|
329
|
+
const participants = await Promise.all(
|
|
330
|
+
this.participants.ids.map(pid => this.anearParticipantClass.getFromStorage(pid, this))
|
|
331
|
+
)
|
|
332
|
+
this.participants.load(participants)
|
|
333
|
+
}
|
|
334
|
+
|
|
317
335
|
eventChannelName () {
|
|
318
336
|
return this.getChannelName('event')
|
|
319
337
|
}
|
|
@@ -3,13 +3,38 @@ const JsonApiResource = require('./JsonApiResource')
|
|
|
3
3
|
const HostUserType = "host"
|
|
4
4
|
|
|
5
5
|
class AnearParticipant extends JsonApiResource {
|
|
6
|
+
constructor(json, anearEvent) {
|
|
7
|
+
super(json)
|
|
8
|
+
this.anearEvent = anearEvent
|
|
9
|
+
this._state = json.state
|
|
10
|
+
this._timestamp = json.timestamp
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
toJSON() {
|
|
7
14
|
return {
|
|
8
15
|
...super.toJSON(),
|
|
9
16
|
geoLocation: this._geoLocation || null,
|
|
17
|
+
state: this.state,
|
|
18
|
+
timestamp: this.timestamp
|
|
10
19
|
}
|
|
11
20
|
}
|
|
12
21
|
|
|
22
|
+
get state() {
|
|
23
|
+
return this._state
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
set state(s) {
|
|
27
|
+
this._state = s
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get timestamp() {
|
|
31
|
+
return this._timestamp
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set timestamp(t) {
|
|
35
|
+
this._timestamp = t
|
|
36
|
+
}
|
|
37
|
+
|
|
13
38
|
set geoLocation(loc) {
|
|
14
39
|
this._geoLocation = loc
|
|
15
40
|
}
|
|
@@ -18,10 +43,6 @@ class AnearParticipant extends JsonApiResource {
|
|
|
18
43
|
return this._geoLocation
|
|
19
44
|
}
|
|
20
45
|
|
|
21
|
-
get identity() {
|
|
22
|
-
return (({ id, userId, name, avatarUrl, geoLocation}) => ({ id, userId, name, avatarUrl, geoLocation}))(this)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
46
|
get userId() {
|
|
26
47
|
return this.relationships.user.data.id
|
|
27
48
|
}
|
|
@@ -6,41 +6,44 @@ const IdleState = "idle"
|
|
|
6
6
|
const MINUTES = (60 * 1000)
|
|
7
7
|
const HOURS = (60 * MINUTES)
|
|
8
8
|
const DefaultIdleMsecs = (30 * MINUTES)
|
|
9
|
-
const DefaultPurgeMsecs = (2 * HOURS)
|
|
10
|
-
|
|
11
|
-
const DefaultSettings = {
|
|
12
|
-
participants: {},
|
|
13
|
-
host: {},
|
|
14
|
-
idleMsecs: DefaultIdleMsecs,
|
|
15
|
-
purgeMsecs: DefaultPurgeMsecs
|
|
16
|
-
}
|
|
9
|
+
const DefaultPurgeMsecs = (2 * HOURS) // after Idle
|
|
17
10
|
|
|
18
11
|
class Participants {
|
|
19
12
|
|
|
20
|
-
constructor(
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
|
|
13
|
+
constructor(anearEvent, {idleMsecs = DefaultIdleMsecs, purgeMsecs = DefaultPurgeMsecs, ids = []} = {}) {
|
|
14
|
+
this.anearEvent = anearEvent
|
|
15
|
+
this.idleMsecs = idleMsecs
|
|
16
|
+
this.purgeMsecs = purgeMsecs
|
|
17
|
+
this._participants = {}
|
|
18
|
+
for (const id of ids) {
|
|
19
|
+
// app restart logic ...
|
|
20
|
+
// seeds from ids with empty objects awaiting full
|
|
21
|
+
// anearParticipant rehydration from redis
|
|
22
|
+
this._participants[id] = {}
|
|
23
|
+
}
|
|
24
|
+
this._host = null
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
toJSON() {
|
|
29
28
|
return {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
// only output the active participant ids.
|
|
30
|
+
ids: this.all.map(p => p.id),
|
|
32
31
|
idleMsecs: this.idleMsecs,
|
|
33
32
|
purgeMsecs: this.purgeMsecs
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
get ids() {
|
|
37
|
+
return Object.keys(this._participants)
|
|
38
|
+
}
|
|
39
|
+
|
|
37
40
|
indexedById() {
|
|
38
41
|
// returns an object that has AnearParticipant.id as key
|
|
39
42
|
return this._participants
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
getById(
|
|
43
|
-
return this._participants[
|
|
45
|
+
getById(participantId) {
|
|
46
|
+
return this._participants[participantId]
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
exists({id}) {
|
|
@@ -55,29 +58,33 @@ class Participants {
|
|
|
55
58
|
return this._host
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
set host(h) {
|
|
62
|
+
this._host = h
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
get all() {
|
|
59
66
|
return Object.values(this._participants).
|
|
60
67
|
sort((ca, cb) => ca.timestamp - cb.timestamp)
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
active() {
|
|
70
|
+
get active() {
|
|
64
71
|
return Object.values(this._participants).filter(c => c.state === ActiveState)
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
idle() {
|
|
74
|
+
get idle() {
|
|
68
75
|
return Object.values(this._participants).filter(c => c.state === IdleState)
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
get count() {
|
|
72
|
-
return
|
|
79
|
+
return this.ids.length
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
numActive() {
|
|
76
|
-
return this.active
|
|
82
|
+
get numActive() {
|
|
83
|
+
return this.active.length
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
numIdle() {
|
|
80
|
-
return this.idle
|
|
86
|
+
get numIdle() {
|
|
87
|
+
return this.idle.length
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
isIdle(c, currentTimestamp) {
|
|
@@ -108,8 +115,7 @@ class Participants {
|
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
|
|
111
|
-
|
|
112
|
-
keys.forEach(
|
|
118
|
+
this.ids.forEach(
|
|
113
119
|
k => {
|
|
114
120
|
const c = this._participants[k]
|
|
115
121
|
sweeper(c)
|
|
@@ -117,34 +123,33 @@ class Participants {
|
|
|
117
123
|
)
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
add(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (anearParticipant.isHost() && anearEvent.hosted) {
|
|
126
|
+
add(anearParticipant, withTimestamp = this.currentTimestamp) {
|
|
127
|
+
if (anearParticipant.isHost() && this.anearEvent.hosted) {
|
|
124
128
|
// the host is not an eligible participant and isn't active nor idle
|
|
125
|
-
this.
|
|
129
|
+
this.host = anearParticipant
|
|
126
130
|
} else {
|
|
127
|
-
this.
|
|
131
|
+
this._participants[anearParticipant.id] = anearParticipant
|
|
132
|
+
this.markActive(anearParticipant, withTimestamp)
|
|
128
133
|
}
|
|
129
|
-
return
|
|
134
|
+
return anearParticipant
|
|
130
135
|
}
|
|
131
136
|
|
|
132
|
-
markActive(anearParticipant) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (participant) {
|
|
136
|
-
participant.timestamp = this.currentTimestamp
|
|
137
|
-
participant.state = ActiveState
|
|
138
|
-
} else {
|
|
139
|
-
this._participants[anearParticipant.id] = this.participantRec(anearParticipant)
|
|
140
|
-
}
|
|
137
|
+
markActive(anearParticipant, withTimestamp = this.currentTimestamp) {
|
|
138
|
+
anearParticipant.timestamp = withTimestamp
|
|
139
|
+
anearParticipant.state = ActiveState
|
|
141
140
|
}
|
|
142
141
|
|
|
143
|
-
purge(
|
|
144
|
-
if (
|
|
145
|
-
|
|
142
|
+
purge(participant) {
|
|
143
|
+
if (!participant) return
|
|
144
|
+
|
|
145
|
+
const {id} = participant
|
|
146
|
+
|
|
147
|
+
if (this.host && (id === this.host.id)) {
|
|
148
|
+
this.host = null
|
|
146
149
|
} else {
|
|
147
|
-
if (this._participants[id])
|
|
150
|
+
if (this._participants[id]) {
|
|
151
|
+
delete this._participants[id]
|
|
152
|
+
}
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
|
|
@@ -152,12 +157,17 @@ class Participants {
|
|
|
152
157
|
return new Date().getTime()
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
load(anearParticipants) {
|
|
161
|
+
// used for tests only
|
|
162
|
+
anearParticipants.forEach(
|
|
163
|
+
p => {
|
|
164
|
+
if (p.isHost() && this.anearEvent.hosted) {
|
|
165
|
+
this.host = p
|
|
166
|
+
} else {
|
|
167
|
+
this._participants[p.id] = p
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
)
|
|
161
171
|
}
|
|
162
172
|
}
|
|
163
173
|
|
package/package.json
CHANGED
package/tests/AnearEvent.test.js
CHANGED
|
@@ -142,14 +142,14 @@ afterAll(async () => await TestEvent.close())
|
|
|
142
142
|
afterEach(() => {jest.clearAllMocks()})
|
|
143
143
|
|
|
144
144
|
const newTestEvent = (hosted = false) => {
|
|
145
|
-
const t = new TestEvent(chatEvent, MessagingStub)
|
|
145
|
+
const t = new TestEvent(chatEvent, TestPlayer, MessagingStub)
|
|
146
146
|
t.attributes.hosted = hosted
|
|
147
147
|
t.startStateMachine()
|
|
148
148
|
return t
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
const newTestEventWithDefaultXState = testEvent => {
|
|
152
|
-
const t = new TestEventWithDefaultXState(testEvent, MessagingStub)
|
|
152
|
+
const t = new TestEventWithDefaultXState(testEvent, TestPlayer, MessagingStub)
|
|
153
153
|
t.startStateMachine()
|
|
154
154
|
return t
|
|
155
155
|
}
|
|
@@ -162,7 +162,7 @@ test('participant enter with Default Xstate Config', async () => {
|
|
|
162
162
|
expect(t.relationships.user.data.type).toBe("users")
|
|
163
163
|
expect(t.anearStateMachine.currentState.value).toBe("eventActive")
|
|
164
164
|
expect(t.stateMachineContext.playerScores[0]).toBe(83)
|
|
165
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
165
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
166
166
|
|
|
167
167
|
await t.participantEnter(p1)
|
|
168
168
|
await t.persist()
|
|
@@ -171,7 +171,7 @@ test('participant enter with Default Xstate Config', async () => {
|
|
|
171
171
|
expect(p1.userType).toBe("participant")
|
|
172
172
|
expect(mockParticipantEnterHandler).toHaveBeenCalledTimes(1)
|
|
173
173
|
expect(mockParticipantEnterHandler).toHaveBeenCalledWith(p1)
|
|
174
|
-
expect(t.participants.numActive
|
|
174
|
+
expect(t.participants.numActive).toBe(1)
|
|
175
175
|
|
|
176
176
|
await p1.remove()
|
|
177
177
|
})
|
|
@@ -179,21 +179,21 @@ test('participant enter with Default Xstate Config', async () => {
|
|
|
179
179
|
test('participant close with Default Xstate Config', async () => {
|
|
180
180
|
const t = newTestEventWithDefaultXState(chatEvent)
|
|
181
181
|
|
|
182
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
182
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
183
183
|
|
|
184
184
|
await t.participantExit(p1)
|
|
185
185
|
await t.update()
|
|
186
186
|
|
|
187
187
|
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p1)
|
|
188
188
|
expect(mockParticipantExitHandler).toHaveBeenCalledTimes(1)
|
|
189
|
-
expect(t.participants.numActive
|
|
189
|
+
expect(t.participants.numActive).toBe(0)
|
|
190
190
|
|
|
191
191
|
await t.remove()
|
|
192
192
|
})
|
|
193
193
|
|
|
194
194
|
test('participant refresh with Default Xstate Config', async () => {
|
|
195
195
|
const t = newTestEventWithDefaultXState(chatEvent)
|
|
196
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
196
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
197
197
|
|
|
198
198
|
await t.refreshParticipant(p1)
|
|
199
199
|
await t.update()
|
|
@@ -206,7 +206,7 @@ test('participant refresh with Default Xstate Config', async () => {
|
|
|
206
206
|
|
|
207
207
|
test('participant action with Default Xstate Config', async () => {
|
|
208
208
|
const t = newTestEventWithDefaultXState(chatEvent)
|
|
209
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
209
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
210
210
|
const eventName = "TEST_ACTION"
|
|
211
211
|
const payload = {x: 1, y: 99}
|
|
212
212
|
|
|
@@ -230,8 +230,8 @@ test('can be persisted and removed repeatedly in storage', async () => {
|
|
|
230
230
|
|
|
231
231
|
test('can add participants, not hosted', async () => {
|
|
232
232
|
let t = newTestEvent(false)
|
|
233
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
234
|
-
const p2 = new TestPlayer(chatParticipant2)
|
|
233
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
234
|
+
const p2 = new TestPlayer(chatParticipant2, t)
|
|
235
235
|
const id = t.id
|
|
236
236
|
|
|
237
237
|
await t.participantEnter(p1)
|
|
@@ -240,15 +240,15 @@ test('can add participants, not hosted', async () => {
|
|
|
240
240
|
expect(p1.userType).toBe("participant")
|
|
241
241
|
expect(mockParticipantEnterHandler).toHaveBeenCalledTimes(1)
|
|
242
242
|
expect(mockParticipantEnterHandler).toHaveBeenCalledWith(p1)
|
|
243
|
-
expect(t.participants.numActive
|
|
244
|
-
expect(t.participants.host).
|
|
243
|
+
expect(t.participants.numActive).toBe(1)
|
|
244
|
+
expect(t.participants.host).toBe(null)
|
|
245
245
|
|
|
246
246
|
await t.participantEnter(p2)
|
|
247
247
|
await t.update()
|
|
248
248
|
|
|
249
249
|
expect(mockParticipantEnterHandler).toHaveBeenCalledTimes(2)
|
|
250
250
|
expect(mockParticipantEnterHandler).toHaveBeenCalledWith(p2)
|
|
251
|
-
expect(t.participants.numActive
|
|
251
|
+
expect(t.participants.numActive).toBe(2)
|
|
252
252
|
expect(t.participants.get(p2).name).toBe("bbondfl93")
|
|
253
253
|
expect(p2.userType).toBe("participant")
|
|
254
254
|
|
|
@@ -260,7 +260,27 @@ test('can add participants, not hosted', async () => {
|
|
|
260
260
|
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p1)
|
|
261
261
|
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p2)
|
|
262
262
|
expect(mockParticipantExitHandler).toHaveBeenCalledTimes(2)
|
|
263
|
-
expect(t.participants.numActive
|
|
263
|
+
expect(t.participants.numActive).toBe(0)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
test('purge all participants', async () => {
|
|
267
|
+
let t = newTestEvent(true)
|
|
268
|
+
const host = new TestPlayer(chatHost, t)
|
|
269
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
270
|
+
const p2 = new TestPlayer(chatParticipant2, t)
|
|
271
|
+
await t.participantEnter(host)
|
|
272
|
+
await t.participantEnter(p1)
|
|
273
|
+
await t.participantEnter(p2)
|
|
274
|
+
await t.update()
|
|
275
|
+
|
|
276
|
+
expect(t.participants.host).toBe(host)
|
|
277
|
+
expect(t.participants.ids).toStrictEqual([p1.id, p2.id])
|
|
278
|
+
|
|
279
|
+
await t.purgeParticipants()
|
|
280
|
+
|
|
281
|
+
expect(t.participants.all).toHaveLength(0)
|
|
282
|
+
|
|
283
|
+
await t.remove()
|
|
264
284
|
})
|
|
265
285
|
|
|
266
286
|
|
|
@@ -268,10 +288,10 @@ test('can add participant, hosted', async () => {
|
|
|
268
288
|
let t = newTestEvent(true)
|
|
269
289
|
|
|
270
290
|
expect(t.hosted).toBe(true)
|
|
271
|
-
expect(t.participants.numActive
|
|
291
|
+
expect(t.participants.numActive).toBe(0)
|
|
272
292
|
|
|
273
|
-
const host = new TestPlayer(chatHost)
|
|
274
|
-
const p2 = new TestPlayer(chatParticipant2)
|
|
293
|
+
const host = new TestPlayer(chatHost, t)
|
|
294
|
+
const p2 = new TestPlayer(chatParticipant2, t)
|
|
275
295
|
const id = t.id
|
|
276
296
|
|
|
277
297
|
await t.participantEnter(host)
|
|
@@ -281,14 +301,14 @@ test('can add participant, hosted', async () => {
|
|
|
281
301
|
expect(mockParticipantEnterHandler).toHaveBeenCalledTimes(1)
|
|
282
302
|
expect(mockParticipantEnterHandler).toHaveBeenCalledWith(host)
|
|
283
303
|
expect(t.participants.host.name).toBe('foxhole_host')
|
|
284
|
-
expect(t.participants.numActive
|
|
304
|
+
expect(t.participants.numActive).toBe(0) // event creator when hosted isn't active participant
|
|
285
305
|
|
|
286
306
|
await t.participantEnter(p2)
|
|
287
307
|
await t.update()
|
|
288
308
|
|
|
289
309
|
expect(mockParticipantEnterHandler).toHaveBeenCalledTimes(2)
|
|
290
310
|
expect(mockParticipantEnterHandler).toHaveBeenCalledWith(p2)
|
|
291
|
-
expect(t.participants.numActive
|
|
311
|
+
expect(t.participants.numActive).toBe(1)
|
|
292
312
|
expect(t.participants.get(p2).name).toBe('bbondfl93')
|
|
293
313
|
|
|
294
314
|
await t.participantExit(host)
|
|
@@ -299,43 +319,46 @@ test('can add participant, hosted', async () => {
|
|
|
299
319
|
expect(mockParticipantExitHandler).toHaveBeenCalledWith(host)
|
|
300
320
|
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p2)
|
|
301
321
|
expect(mockParticipantExitHandler).toHaveBeenCalledTimes(2)
|
|
302
|
-
expect(t.participants.numActive
|
|
322
|
+
expect(t.participants.numActive).toBe(0)
|
|
303
323
|
})
|
|
304
324
|
|
|
305
325
|
test('can be retrieved back from storage with participants, not hosted', async () => {
|
|
306
326
|
const testEvent = newTestEvent(false)
|
|
307
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
308
|
-
const p2 = new TestPlayer(chatParticipant2)
|
|
327
|
+
const p1 = new TestPlayer(chatParticipant1, testEvent)
|
|
328
|
+
const p2 = new TestPlayer(chatParticipant2, testEvent)
|
|
309
329
|
|
|
310
330
|
await testEvent.participantEnter(p1)
|
|
311
331
|
await testEvent.participantEnter(p2)
|
|
312
332
|
await testEvent.persist()
|
|
313
333
|
|
|
314
|
-
const rehydratedTestEvent = await TestEvent.getFromStorage(testEvent.id, MessagingStub)
|
|
315
|
-
|
|
316
|
-
const rehydratedPlayer2 = await TestPlayer.getFromStorage(p2.id)
|
|
334
|
+
const rehydratedTestEvent = await TestEvent.getFromStorage(testEvent.id, TestPlayer, MessagingStub)
|
|
335
|
+
await rehydratedTestEvent.reloadParticipantsFromStorage()
|
|
317
336
|
|
|
318
337
|
rehydratedTestEvent.startStateMachine()
|
|
319
338
|
|
|
320
|
-
expect(rehydratedTestEvent.participants.numActive
|
|
339
|
+
expect(rehydratedTestEvent.participants.numActive).toBe(2)
|
|
321
340
|
expect(rehydratedTestEvent.id).toBe(testEvent.id)
|
|
322
341
|
expect(rehydratedTestEvent.relationships['user'].data.type).toBe("users")
|
|
323
342
|
expect(rehydratedTestEvent.relationships['zone'].data.type).toBe("zones")
|
|
324
343
|
expect(rehydratedTestEvent.participantTimeout).toBe(32000)
|
|
325
344
|
expect(rehydratedTestEvent.stateMachineContext.score).toBe(90)
|
|
326
345
|
expect(rehydratedTestEvent.included[0].relationships.app.data.id).toBe("5b9d9838-17de-4a80-8a64-744c222ba722")
|
|
327
|
-
expect(rehydratedPlayer1.context.name).toBe('machvee')
|
|
328
|
-
expect(rehydratedPlayer2.context.name).toBe('bbondfl93')
|
|
329
346
|
|
|
330
|
-
|
|
331
|
-
|
|
347
|
+
const rp1 = rehydratedTestEvent.participants.getById(p1.id)
|
|
348
|
+
const rp2 = rehydratedTestEvent.participants.getById(p2.id)
|
|
349
|
+
|
|
350
|
+
expect(rehydratedTestEvent.participants.getById(rp1.id).context.name).toBe('machvee')
|
|
351
|
+
expect(rehydratedTestEvent.participants.getById(rp2.id).context.name).toBe('bbondfl93')
|
|
352
|
+
|
|
353
|
+
await rehydratedTestEvent.participantExit(rp1)
|
|
354
|
+
await rehydratedTestEvent.participantExit(rp2)
|
|
332
355
|
await rehydratedTestEvent.remove()
|
|
333
356
|
})
|
|
334
357
|
|
|
335
358
|
test('can update state machine context via Action events', async () => {
|
|
336
359
|
const t = newTestEvent(false)
|
|
337
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
338
|
-
const p2 = new TestPlayer(chatParticipant2)
|
|
360
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
361
|
+
const p2 = new TestPlayer(chatParticipant2, t)
|
|
339
362
|
|
|
340
363
|
await t.participantEnter(p1)
|
|
341
364
|
await t.participantEnter(p2)
|
|
@@ -362,8 +385,8 @@ test('can update state machine context via Action events', async () => {
|
|
|
362
385
|
|
|
363
386
|
test('can reset All ParticipantTimers', async () => {
|
|
364
387
|
const t = newTestEvent(false)
|
|
365
|
-
const p1 = new TestPlayer(chatParticipant1)
|
|
366
|
-
const p2 = new TestPlayer(chatParticipant2)
|
|
388
|
+
const p1 = new TestPlayer(chatParticipant1, t)
|
|
389
|
+
const p2 = new TestPlayer(chatParticipant2, t)
|
|
367
390
|
|
|
368
391
|
const resetMock = jest.spyOn(MessagingStub, "resetAllParticipantTimers");
|
|
369
392
|
|
|
@@ -2,6 +2,8 @@ const AnearParticipant = require('../lib/models/AnearParticipant')
|
|
|
2
2
|
|
|
3
3
|
const { AnearParticipantFixture1: player1 } = require('./fixtures')
|
|
4
4
|
|
|
5
|
+
const MockEvent = {}
|
|
6
|
+
|
|
5
7
|
class TestParticipant extends AnearParticipant {
|
|
6
8
|
initContext() {
|
|
7
9
|
return {score: 97, responses: ['A', 'C', 'D', 'A']}
|
|
@@ -11,26 +13,29 @@ class TestParticipant extends AnearParticipant {
|
|
|
11
13
|
afterAll(async () => await TestParticipant.close())
|
|
12
14
|
|
|
13
15
|
test('constructor', () => {
|
|
14
|
-
const t = new TestParticipant(player1)
|
|
16
|
+
const t = new TestParticipant(player1, MockEvent)
|
|
15
17
|
expect(t.id).toBe(player1.data.id)
|
|
16
18
|
expect(t.relationships.user.data.type).toBe("users")
|
|
17
19
|
expect(t.context.score).toBe(97)
|
|
20
|
+
expect(t.anearEvent).toBe(MockEvent)
|
|
18
21
|
})
|
|
19
22
|
|
|
20
23
|
test('participant can be repeatedly rehydrated and updated', async () => {
|
|
21
24
|
try {
|
|
22
|
-
const participant = new TestParticipant(player1)
|
|
25
|
+
const participant = new TestParticipant(player1, MockEvent)
|
|
23
26
|
await participant.persist()
|
|
24
27
|
|
|
25
|
-
let p = await TestParticipant.getFromStorage(player1.data.id)
|
|
28
|
+
let p = await TestParticipant.getFromStorage(player1.data.id, MockEvent)
|
|
26
29
|
|
|
27
30
|
expect(p.context.responses).toStrictEqual(['A', 'C', 'D', 'A'])
|
|
31
|
+
expect(p.anearEvent).toBe(MockEvent)
|
|
28
32
|
p.context.responses.push('B')
|
|
29
33
|
|
|
30
34
|
await p.update()
|
|
31
35
|
|
|
32
|
-
p = await TestParticipant.getFromStorage(player1.data.id)
|
|
36
|
+
p = await TestParticipant.getFromStorage(player1.data.id, MockEvent)
|
|
33
37
|
expect(p.context.responses[4]).toBe('B')
|
|
38
|
+
expect(p.anearEvent).toBe(MockEvent)
|
|
34
39
|
|
|
35
40
|
await p.remove()
|
|
36
41
|
|
|
@@ -5,6 +5,7 @@ const { ParticipantsFixture: participantsJSON,
|
|
|
5
5
|
AnearParticipantFixture2: visitor2JSON,
|
|
6
6
|
AnearHostFixture: hostJSON } = require('./fixtures')
|
|
7
7
|
const AnearParticipant = require('../lib/models/AnearParticipant')
|
|
8
|
+
const AnearParticipantJSONBuilder = require('./utils/AnearParticipantJSONBuilder')
|
|
8
9
|
|
|
9
10
|
const user1Id = "e053977c-dcb6-40e0-b7b8-e3dbd70ec8fd"
|
|
10
11
|
const idleId = "f1056e6c-c393-4617-8a06-67ba9d2f4b8a"
|
|
@@ -12,61 +13,80 @@ const activeId = user1Id
|
|
|
12
13
|
const hours24 = (24 * 60 * 60 * 1000)
|
|
13
14
|
const GeoLocation = {lat: 25.8348343, lng: -80.38438434}
|
|
14
15
|
|
|
15
|
-
const MockHostedEvent = {hosted: true}
|
|
16
|
-
const MockNonHostedEvent = {hosted: false}
|
|
17
|
-
|
|
18
|
-
const newActiveParticipants = (timestamp,
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
const MockHostedEvent = {hosted: true, anearParticipantClass: AnearParticipant}
|
|
17
|
+
const MockNonHostedEvent = {hosted: false, anearParticipantClass: AnearParticipant}
|
|
18
|
+
|
|
19
|
+
const newActiveParticipants = (timestamp, args = {}) => {
|
|
20
|
+
const copyParticipantsFixture = JSON.parse(JSON.stringify(participantsJSON))
|
|
21
|
+
const activeParticipants = Object.values(copyParticipantsFixture).filter(p => !p.isHost)
|
|
22
|
+
|
|
23
|
+
const participants = new Participants(MockHostedEvent, args)
|
|
24
|
+
|
|
25
|
+
activeParticipants.forEach(
|
|
26
|
+
(attrs, i) => {
|
|
27
|
+
const participant = new AnearParticipant(AnearParticipantJSONBuilder(attrs), MockHostedEvent)
|
|
28
|
+
participant.host = attrs.isHost
|
|
29
|
+
participants.add(participant, timestamp - (i*2000))
|
|
26
30
|
}
|
|
27
31
|
)
|
|
28
|
-
return
|
|
32
|
+
return participants
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
const newCurrentParticipants = (timestamp,
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const newCurrentParticipants = (timestamp, anearEvent = MockHostedEvent) => {
|
|
36
|
+
const copyParticipantsFixture = JSON.parse(JSON.stringify(participantsJSON))
|
|
37
|
+
const currentParticipants= Object.values(copyParticipantsFixture).filter(p => !p.isHost)
|
|
38
|
+
|
|
39
|
+
const participants = new Participants(anearEvent)
|
|
40
|
+
|
|
41
|
+
const anearParticipants = currentParticipants.map(
|
|
42
|
+
(attrs, i) => {
|
|
43
|
+
const participant = new AnearParticipant(AnearParticipantJSONBuilder(attrs), anearEvent)
|
|
44
|
+
|
|
45
|
+
let withTimestamp
|
|
46
|
+
if (attrs.state === 'active') {
|
|
47
|
+
withTimestamp = timestamp - (i*2000)
|
|
39
48
|
} else {
|
|
40
|
-
|
|
41
|
-
p.timestamp = timestamp - copy.idleMsecs
|
|
49
|
+
withTimestamp = timestamp - participants.idleMsecs
|
|
42
50
|
}
|
|
51
|
+
|
|
52
|
+
participant.state = attrs.state
|
|
53
|
+
participant.timestamp = withTimestamp
|
|
54
|
+
participant.host = attrs.isHost
|
|
55
|
+
return participant
|
|
43
56
|
}
|
|
44
57
|
)
|
|
45
|
-
|
|
58
|
+
|
|
59
|
+
participants.load(anearParticipants)
|
|
60
|
+
|
|
61
|
+
return participants
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
const now = new Date().getTime()
|
|
49
65
|
|
|
50
66
|
test('constructor with JSON provided', () => {
|
|
51
|
-
const
|
|
67
|
+
const ids = ['123', '345', '456']
|
|
68
|
+
const args = {idleMsecs: 23400, purgeMsecs: 678900, ids}
|
|
69
|
+
const p = new Participants(MockHostedEvent, args)
|
|
52
70
|
expect(p).toBeDefined()
|
|
53
|
-
expect(p.idleMsecs).toBe(
|
|
71
|
+
expect(p.idleMsecs).toBe(args.idleMsecs)
|
|
72
|
+
expect(p.purgeMsecs).toBe(args.purgeMsecs)
|
|
73
|
+
expect(p.ids).toStrictEqual(ids)
|
|
54
74
|
})
|
|
55
75
|
|
|
56
|
-
test('constructor with NO JSON provided', () => {
|
|
57
|
-
const p = new Participants()
|
|
76
|
+
test('constructor with NO JSON provided has default idle and purge Msecs', () => {
|
|
77
|
+
const p = new Participants(MockHostedEvent)
|
|
58
78
|
expect(p).toBeDefined()
|
|
59
79
|
expect(p.idleMsecs).toBe(1800000)
|
|
60
80
|
expect(p.purgeMsecs).toBe(7200000)
|
|
61
81
|
expect(p.all).toHaveLength(0)
|
|
62
82
|
})
|
|
63
83
|
|
|
64
|
-
test('constructor with null idle and purge msecs', () => {
|
|
65
|
-
const p = newActiveParticipants(now, {
|
|
84
|
+
test('constructor with null idle and purge msecs avoids idle purge', () => {
|
|
85
|
+
const p = newActiveParticipants(now, {idleMsecs: null, purgeMsecs: null})
|
|
66
86
|
expect(p.idleMsecs).toBe(null)
|
|
67
87
|
expect(p.purgeMsecs).toBe(null)
|
|
68
|
-
expect(p.idle
|
|
69
|
-
expect(p.active
|
|
88
|
+
expect(p.idle).toHaveLength(0)
|
|
89
|
+
expect(p.active).toHaveLength(10)
|
|
70
90
|
|
|
71
91
|
const c = p.getById(idleId)
|
|
72
92
|
expect(p.isIdle(c, now)).toBeFalsy()
|
|
@@ -99,23 +119,17 @@ test('getParticipant fail', () => {
|
|
|
99
119
|
expect(p.get({id: "abcd"})).toBeUndefined()
|
|
100
120
|
})
|
|
101
121
|
|
|
102
|
-
test('host', () => {
|
|
103
|
-
const p = newActiveParticipants(now)
|
|
104
|
-
const host = p.host
|
|
105
|
-
expect(host.name).toBe("the_host")
|
|
106
|
-
})
|
|
107
|
-
|
|
108
122
|
test('getParticipant success', () => {
|
|
109
123
|
const p = newActiveParticipants(now)
|
|
110
124
|
expect(p.get({id: user1Id}).name).toBe("user1")
|
|
111
125
|
})
|
|
112
126
|
|
|
113
127
|
test('add() participant user', async () => {
|
|
114
|
-
const p = newCurrentParticipants(now)
|
|
115
|
-
const participant = new AnearParticipant(visitor2JSON)
|
|
128
|
+
const p = newCurrentParticipants(now, MockNonHostedEvent)
|
|
129
|
+
const participant = new AnearParticipant(visitor2JSON, MockNonHostedEvent)
|
|
116
130
|
participant.geoLocation = GeoLocation
|
|
117
131
|
|
|
118
|
-
p.add(
|
|
132
|
+
p.add(participant)
|
|
119
133
|
const part = p.get(participant)
|
|
120
134
|
expect(part.name).toBe("bbondfl93")
|
|
121
135
|
expect(part.avatarUrl).toBe("https://s3.amazonaws.com/anearassets/barbara_bond.png")
|
|
@@ -126,10 +140,10 @@ test('add() participant user', async () => {
|
|
|
126
140
|
|
|
127
141
|
test('add() host user', async () => {
|
|
128
142
|
const p = newCurrentParticipants(now)
|
|
129
|
-
const host = new AnearParticipant(hostJSON)
|
|
143
|
+
const host = new AnearParticipant(hostJSON, MockNonHostedEvent)
|
|
130
144
|
host.geoLocation = GeoLocation
|
|
131
145
|
|
|
132
|
-
p.add(
|
|
146
|
+
p.add(host)
|
|
133
147
|
expect(p.get(host)).toBeUndefined()
|
|
134
148
|
expect(p.host.name).toBe('foxhole_host')
|
|
135
149
|
expect(p.host.avatarUrl).toBe("https://s3.amazonaws.com/anearassets/foxhole.png")
|
|
@@ -139,20 +153,24 @@ test('add() host user', async () => {
|
|
|
139
153
|
|
|
140
154
|
test('active', () => {
|
|
141
155
|
const p = newActiveParticipants(now)
|
|
142
|
-
expect(p.active
|
|
156
|
+
expect(p.active).toHaveLength(10)
|
|
143
157
|
})
|
|
144
158
|
|
|
145
159
|
test('idle', () => {
|
|
146
160
|
const p = newCurrentParticipants(now)
|
|
147
|
-
expect(p.idle
|
|
161
|
+
expect(p.idle).toHaveLength(2)
|
|
148
162
|
})
|
|
149
163
|
|
|
150
164
|
test('toJSON', () => {
|
|
151
|
-
const p =
|
|
165
|
+
const p = newActiveParticipants(now)
|
|
152
166
|
const j = p.toJSON()
|
|
153
|
-
expect(j).toHaveProperty("
|
|
167
|
+
expect(j).toHaveProperty("ids")
|
|
154
168
|
expect(j).toHaveProperty("idleMsecs")
|
|
155
169
|
expect(j).toHaveProperty("purgeMsecs")
|
|
170
|
+
expect(j.ids.sort()).toEqual(p.ids.sort())
|
|
171
|
+
expect(j.ids.length).toBe(10)
|
|
172
|
+
expect(j.idleMsecs).toBe(1800000)
|
|
173
|
+
expect(j.purgeMsecs).toBe(7200000)
|
|
156
174
|
})
|
|
157
175
|
|
|
158
176
|
test('isIdle', () => {
|
|
@@ -185,22 +203,22 @@ test('purge host user-type', () => {
|
|
|
185
203
|
const p = newActiveParticipants(now)
|
|
186
204
|
const c = p.host
|
|
187
205
|
p.purge(c)
|
|
188
|
-
expect(p.host).toStrictEqual(
|
|
206
|
+
expect(p.host).toStrictEqual(null)
|
|
189
207
|
})
|
|
190
208
|
|
|
191
209
|
test('updateState will leave state unchanged when timeout criteria not met', () => {
|
|
192
210
|
const p = newCurrentParticipants(now)
|
|
193
211
|
p.updateState(now)
|
|
194
|
-
expect(p.active
|
|
195
|
-
expect(p.idle
|
|
196
|
-
expect(p.numActive
|
|
197
|
-
expect(p.numIdle
|
|
212
|
+
expect(p.active).toHaveLength(8)
|
|
213
|
+
expect(p.idle).toHaveLength(2)
|
|
214
|
+
expect(p.numActive).toBe(8)
|
|
215
|
+
expect(p.numIdle).toBe(2)
|
|
198
216
|
})
|
|
199
217
|
|
|
200
218
|
test('updateState will mark active to idle when timeout reached', () => {
|
|
201
219
|
const current = now
|
|
202
220
|
const p = newCurrentParticipants(current)
|
|
203
|
-
expect(p.active
|
|
221
|
+
expect(p.active).toHaveLength(8)
|
|
204
222
|
p.updateState(current + p.idleMsecs + 1000)
|
|
205
223
|
expect(Object.values(p.all).
|
|
206
224
|
filter(p => p.state === 'active')).toHaveLength(0)
|
|
@@ -209,7 +227,7 @@ test('updateState will mark active to idle when timeout reached', () => {
|
|
|
209
227
|
test('updateState will purge idle participants', () => {
|
|
210
228
|
const current = now
|
|
211
229
|
const p = newCurrentParticipants(current)
|
|
212
|
-
const idlers = p.idle
|
|
230
|
+
const idlers = p.idle
|
|
213
231
|
expect(idlers).toHaveLength(2)
|
|
214
232
|
p.updateState(current + p.purgeMsecs + 1000)
|
|
215
233
|
idlers.forEach(c => expect(p.getById(c.id)).toBeUndefined())
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
2
|
module.exports = {
|
|
4
|
-
|
|
3
|
+
"aa396e6c-2193-cd17-eea6-0413eefff893": {
|
|
5
4
|
id: "aa396e6c-2193-cd17-eea6-0413eefff893",
|
|
6
5
|
userId: "044f4dfc-c4d0-4c58-a2fa-9e1e43913dab",
|
|
7
6
|
name: "the_host",
|
|
@@ -11,108 +10,104 @@ module.exports = {
|
|
|
11
10
|
state: "active",
|
|
12
11
|
isHost: true
|
|
13
12
|
},
|
|
14
|
-
|
|
15
|
-
"e053977c-dcb6-40e0-b7b8-e3dbd70ec8fd"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"0f6ab786-fcae-4e81-87ac-a3930f496d71"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"af9c7ff6-c8c9-4f4a-ad04-bcf86ef57d15"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"777b11b5-2b3b-4420-93d2-eff65127c925"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"6405094a-0ac4-4476-9fd8-5d2edc6cf5a1"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
isHost: false
|
|
64
|
-
},
|
|
65
|
-
"dcaabc23-b7dc-47cd-ad11-43022750dc0e": {
|
|
66
|
-
id: "dcaabc23-b7dc-47cd-ad11-43022750dc0e",
|
|
67
|
-
userId: "ad6a1e91-6460-4eea-b215-4af4de4e021e",
|
|
68
|
-
name: "user6",
|
|
69
|
-
avatarUrl: "https://s3.amazonaws.com/path6",
|
|
70
|
-
geoLocation: {},
|
|
71
|
-
timestamp: 1615649132502,
|
|
72
|
-
state: "active",
|
|
73
|
-
isHost: false
|
|
74
|
-
},
|
|
75
|
-
"78127e6c-c1b8-490d-ace6-afc798fdf4f6": {
|
|
76
|
-
id: "78127e6c-c1b8-490d-ace6-afc798fdf4f6",
|
|
77
|
-
userId: "d8584429-4651-4d63-a886-1622941f9b87",
|
|
78
|
-
name: "user7",
|
|
79
|
-
avatarUrl: "https://s3.amazonaws.com/path7",
|
|
80
|
-
geoLocation: {},
|
|
81
|
-
timestamp: 1615649132502,
|
|
82
|
-
state: "active",
|
|
83
|
-
isHost: false
|
|
84
|
-
},
|
|
85
|
-
"7166a301-aad9-4bc9-b9f7-66f56bfb16d7": {
|
|
86
|
-
id: "7166a301-aad9-4bc9-b9f7-66f56bfb16d7",
|
|
87
|
-
userId: "0fe5311c-42c4-425b-b7a4-0338dc08048f",
|
|
88
|
-
name: "user8",
|
|
89
|
-
avatarUrl: "https://s3.amazonaws.com/path8",
|
|
90
|
-
geoLocation: {},
|
|
91
|
-
timestamp: 1615649132502,
|
|
92
|
-
state: "active",
|
|
93
|
-
isHost: false
|
|
94
|
-
},
|
|
95
|
-
"1e168941-4050-4731-8c27-f2464c717c4a": {
|
|
96
|
-
id: "1e168941-4050-4731-8c27-f2464c717c4a",
|
|
97
|
-
userId: "69308515-a3af-4ce6-b687-139ef0b87e74",
|
|
98
|
-
name: "user9",
|
|
99
|
-
avatarUrl: "https://s3.amazonaws.com/path9",
|
|
100
|
-
geoLocation: {},
|
|
101
|
-
timestamp: 1615642032502,
|
|
102
|
-
state: "idle",
|
|
103
|
-
isHost: false
|
|
104
|
-
},
|
|
105
|
-
"f1056e6c-c393-4617-8a06-67ba9d2f4b8a": {
|
|
106
|
-
id: "f1056e6c-c393-4617-8a06-67ba9d2f4b8a",
|
|
107
|
-
userId: "0cc39ce5-f00f-4b81-ab8b-40e630b3a58c",
|
|
108
|
-
name: "user10",
|
|
109
|
-
avatarUrl: "https://s3.amazonaws.com/path10",
|
|
110
|
-
geoLocation: {},
|
|
111
|
-
timestamp: 1615642032502,
|
|
112
|
-
state: "idle",
|
|
113
|
-
isHost: false
|
|
114
|
-
},
|
|
13
|
+
"e053977c-dcb6-40e0-b7b8-e3dbd70ec8fd": {
|
|
14
|
+
id: "e053977c-dcb6-40e0-b7b8-e3dbd70ec8fd",
|
|
15
|
+
userId: "09f3cbf9-0c73-44c6-b6ad-a03b3607e66a",
|
|
16
|
+
name: "user1",
|
|
17
|
+
avatarUrl: "https://s3.amazonaws.com/path1",
|
|
18
|
+
geoLocation: {},
|
|
19
|
+
timestamp: 1615649132502,
|
|
20
|
+
state: "active",
|
|
21
|
+
isHost: false
|
|
22
|
+
},
|
|
23
|
+
"0f6ab786-fcae-4e81-87ac-a3930f496d71": {
|
|
24
|
+
id: "0f6ab786-fcae-4e81-87ac-a3930f496d71",
|
|
25
|
+
userId: "819635b2-c70d-474c-a0a6-88a420744cb6",
|
|
26
|
+
name: "user2",
|
|
27
|
+
avatarUrl: "https://s3.amazonaws.com/path2",
|
|
28
|
+
geoLocation: {},
|
|
29
|
+
timestamp: 1615649132502,
|
|
30
|
+
state: "active",
|
|
31
|
+
isHost: false
|
|
32
|
+
},
|
|
33
|
+
"af9c7ff6-c8c9-4f4a-ad04-bcf86ef57d15": {
|
|
34
|
+
id: "af9c7ff6-c8c9-4f4a-ad04-bcf86ef57d15",
|
|
35
|
+
userId: "7966b0b6-1c94-4f2d-abf0-857ccf91119e",
|
|
36
|
+
name: "user3",
|
|
37
|
+
avatarUrl: "https://s3.amazonaws.com/path3",
|
|
38
|
+
geoLocation: {},
|
|
39
|
+
timestamp: 1615649132502,
|
|
40
|
+
state: "active",
|
|
41
|
+
isHost: false
|
|
42
|
+
},
|
|
43
|
+
"777b11b5-2b3b-4420-93d2-eff65127c925": {
|
|
44
|
+
id: "777b11b5-2b3b-4420-93d2-eff65127c925",
|
|
45
|
+
userId: "0efde18e-39ff-4e26-9aa5-2f7b8d61b2f7",
|
|
46
|
+
name: "user4",
|
|
47
|
+
avatarUrl: "https://s3.amazonaws.com/path4",
|
|
48
|
+
geoLocation: {},
|
|
49
|
+
timestamp: 1615649132502,
|
|
50
|
+
state: "active",
|
|
51
|
+
isHost: false
|
|
52
|
+
},
|
|
53
|
+
"6405094a-0ac4-4476-9fd8-5d2edc6cf5a1": {
|
|
54
|
+
id: "6405094a-0ac4-4476-9fd8-5d2edc6cf5a1",
|
|
55
|
+
userId: "cdcbbb6a-0a3b-4222-bcde-4a626555f44d",
|
|
56
|
+
name: "user5",
|
|
57
|
+
avatarUrl: "https://s3.amazonaws.com/path5",
|
|
58
|
+
geoLocation: {},
|
|
59
|
+
timestamp: 1615649132502,
|
|
60
|
+
state: "active",
|
|
61
|
+
isHost: false
|
|
115
62
|
},
|
|
116
|
-
|
|
117
|
-
|
|
63
|
+
"dcaabc23-b7dc-47cd-ad11-43022750dc0e": {
|
|
64
|
+
id: "dcaabc23-b7dc-47cd-ad11-43022750dc0e",
|
|
65
|
+
userId: "ad6a1e91-6460-4eea-b215-4af4de4e021e",
|
|
66
|
+
name: "user6",
|
|
67
|
+
avatarUrl: "https://s3.amazonaws.com/path6",
|
|
68
|
+
geoLocation: {},
|
|
69
|
+
timestamp: 1615649132502,
|
|
70
|
+
state: "active",
|
|
71
|
+
isHost: false
|
|
72
|
+
},
|
|
73
|
+
"78127e6c-c1b8-490d-ace6-afc798fdf4f6": {
|
|
74
|
+
id: "78127e6c-c1b8-490d-ace6-afc798fdf4f6",
|
|
75
|
+
userId: "d8584429-4651-4d63-a886-1622941f9b87",
|
|
76
|
+
name: "user7",
|
|
77
|
+
avatarUrl: "https://s3.amazonaws.com/path7",
|
|
78
|
+
geoLocation: {},
|
|
79
|
+
timestamp: 1615649132502,
|
|
80
|
+
state: "active",
|
|
81
|
+
isHost: false
|
|
82
|
+
},
|
|
83
|
+
"7166a301-aad9-4bc9-b9f7-66f56bfb16d7": {
|
|
84
|
+
id: "7166a301-aad9-4bc9-b9f7-66f56bfb16d7",
|
|
85
|
+
userId: "0fe5311c-42c4-425b-b7a4-0338dc08048f",
|
|
86
|
+
name: "user8",
|
|
87
|
+
avatarUrl: "https://s3.amazonaws.com/path8",
|
|
88
|
+
geoLocation: {},
|
|
89
|
+
timestamp: 1615649132502,
|
|
90
|
+
state: "active",
|
|
91
|
+
isHost: false
|
|
92
|
+
},
|
|
93
|
+
"1e168941-4050-4731-8c27-f2464c717c4a": {
|
|
94
|
+
id: "1e168941-4050-4731-8c27-f2464c717c4a",
|
|
95
|
+
userId: "69308515-a3af-4ce6-b687-139ef0b87e74",
|
|
96
|
+
name: "user9",
|
|
97
|
+
avatarUrl: "https://s3.amazonaws.com/path9",
|
|
98
|
+
geoLocation: {},
|
|
99
|
+
timestamp: 1615642032502,
|
|
100
|
+
state: "idle",
|
|
101
|
+
isHost: false
|
|
102
|
+
},
|
|
103
|
+
"f1056e6c-c393-4617-8a06-67ba9d2f4b8a": {
|
|
104
|
+
id: "f1056e6c-c393-4617-8a06-67ba9d2f4b8a",
|
|
105
|
+
userId: "0cc39ce5-f00f-4b81-ab8b-40e630b3a58c",
|
|
106
|
+
name: "user10",
|
|
107
|
+
avatarUrl: "https://s3.amazonaws.com/path10",
|
|
108
|
+
geoLocation: {},
|
|
109
|
+
timestamp: 1615642032502,
|
|
110
|
+
state: "idle",
|
|
111
|
+
isHost: false
|
|
112
|
+
}
|
|
118
113
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const AnearParticipantJSONBuilder = ({eventId, id, userId, name, avatarUrl}) => {
|
|
3
|
+
return {
|
|
4
|
+
data: {
|
|
5
|
+
"id": id,
|
|
6
|
+
"type": "participants",
|
|
7
|
+
"attributes": {
|
|
8
|
+
"created-at": "2019-06-22T08:41:34.257Z",
|
|
9
|
+
"name": name,
|
|
10
|
+
"user-type": "participant",
|
|
11
|
+
"private-channel-name": "anear:a:6i4GPGg7YiE81jxE65vpov:e:51nriTFWJYwiZRVfhaTmOM:private:4aih3BnWiRXLHKupFFkKHO"
|
|
12
|
+
},
|
|
13
|
+
"relationships": {
|
|
14
|
+
"event": {
|
|
15
|
+
"data": {
|
|
16
|
+
"id": eventId,
|
|
17
|
+
"type": "events"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"user": {
|
|
21
|
+
"data": {
|
|
22
|
+
"id": "2d08adc7-b1af-4607-2a86-b45faa03eaa7",
|
|
23
|
+
"type": "users"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
included: [
|
|
29
|
+
{
|
|
30
|
+
"id": userId,
|
|
31
|
+
"type": "users",
|
|
32
|
+
"attributes": {
|
|
33
|
+
"name": "dave_mcvicar",
|
|
34
|
+
"created-at": "2019-06-22T08:41:33.877Z"
|
|
35
|
+
},
|
|
36
|
+
"relationships": {
|
|
37
|
+
"profile": {
|
|
38
|
+
"data": {
|
|
39
|
+
"id": "a04976a9-1c08-4bc6-b381-7f0d0637b919",
|
|
40
|
+
"type": "profiles"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"links": {
|
|
45
|
+
"self": `/v1/users/${userId}`
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "a04976a9-1c08-4bc6-b381-7f0d0637b919",
|
|
50
|
+
"type": "profiles",
|
|
51
|
+
"attributes": {
|
|
52
|
+
"first-name": "Dave",
|
|
53
|
+
"last-name": "McVicar",
|
|
54
|
+
"bio": "Repellendus ut neque. Est autem cupiditate. In omnis dolore.",
|
|
55
|
+
"homepage": "http://hodkiewicz.name/frankie",
|
|
56
|
+
"avatar-url": avatarUrl
|
|
57
|
+
},
|
|
58
|
+
"links": {
|
|
59
|
+
"self": "/v1/profiles/b04976a9-1c08-4bc6-b381-7f0d0637b979"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = AnearParticipantJSONBuilder
|