anear-js-api 0.4.21 → 0.4.23
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/models/JsonApiResource.js +0 -49
- package/package.json +2 -6
- package/lib/utils/Participants.js +0 -181
- package/lib/utils/Persist.js +0 -52
- package/tests/Participants.test.js +0 -234
- package/tests/Persist.test.js +0 -102
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
|
|
3
|
-
const Persist = require('../utils/Persist')
|
|
4
|
-
|
|
5
|
-
const Storage = new Persist()
|
|
6
|
-
|
|
7
3
|
class JsonApiResource {
|
|
8
4
|
|
|
9
5
|
constructor(json) {
|
|
@@ -25,10 +21,6 @@ class JsonApiResource {
|
|
|
25
21
|
return null // override in subclass if desired
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
async onLoad() {
|
|
29
|
-
return // called each time the event is loaded from Storage
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
get attributes() {
|
|
33
25
|
return this.data.attributes
|
|
34
26
|
}
|
|
@@ -52,47 +44,6 @@ class JsonApiResource {
|
|
|
52
44
|
return inc.type === data.type && inc.id === data.id
|
|
53
45
|
})
|
|
54
46
|
}
|
|
55
|
-
|
|
56
|
-
static async getFromStorage(modelId, ...args) {
|
|
57
|
-
const json = await this.getJsonFor(modelId)
|
|
58
|
-
if (!json) return null
|
|
59
|
-
const model = new this(json, ...args)
|
|
60
|
-
await model.onLoad()
|
|
61
|
-
return model
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async persist() {
|
|
65
|
-
return await Storage.create(this)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async update() {
|
|
69
|
-
return await Storage.update(this)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async exists() {
|
|
73
|
-
return await Storage.exists(this)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async remove() {
|
|
77
|
-
return await Storage.remove(this)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static async close() {
|
|
81
|
-
await Storage.close()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static persistKey(id) {
|
|
85
|
-
return `${this.className()}:${id}`
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get key() {
|
|
89
|
-
return this.constructor.persistKey(this.id)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
static async getJsonFor(id) {
|
|
93
|
-
const key = this.persistKey(id)
|
|
94
|
-
return await Storage.fetch(key)
|
|
95
|
-
}
|
|
96
47
|
}
|
|
97
48
|
|
|
98
49
|
module.exports = JsonApiResource
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anear-js-api",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.23",
|
|
4
4
|
"description": "Javascript Developer API for Anear Apps",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -18,9 +18,6 @@
|
|
|
18
18
|
"homepage": "https://github.com/machvee/anear-js-api#readme",
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"ably": "^2.7.0",
|
|
21
|
-
"archiver": "^6.0.1",
|
|
22
|
-
"async-mutex": "^0.3.2",
|
|
23
|
-
"async-redis": "^2.0.0",
|
|
24
21
|
"clean-css": "^5.3.3",
|
|
25
22
|
"cross-fetch": "^3.1.5",
|
|
26
23
|
"pug": "^3.0.2",
|
|
@@ -31,8 +28,7 @@
|
|
|
31
28
|
},
|
|
32
29
|
"devDependencies": {
|
|
33
30
|
"dotenv": "^16.0.3",
|
|
34
|
-
"
|
|
35
|
-
"jest": "^26.6.3",
|
|
31
|
+
"jest": "^29.7.0",
|
|
36
32
|
"node-notifier": "^10.0.1"
|
|
37
33
|
}
|
|
38
34
|
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
"use strict"
|
|
2
|
-
|
|
3
|
-
const ActiveState = "active"
|
|
4
|
-
const IdleState = "idle"
|
|
5
|
-
|
|
6
|
-
const MINUTES = (60 * 1000)
|
|
7
|
-
const HOURS = (60 * MINUTES)
|
|
8
|
-
const DefaultIdleMsecs = (30 * MINUTES)
|
|
9
|
-
const DefaultPurgeMsecs = (2 * HOURS) // after Idle
|
|
10
|
-
|
|
11
|
-
class Participants {
|
|
12
|
-
|
|
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
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
toJSON() {
|
|
28
|
-
return {
|
|
29
|
-
ids: this.all.map(p => p.id),
|
|
30
|
-
idleMsecs: this.idleMsecs,
|
|
31
|
-
purgeMsecs: this.purgeMsecs
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get ids() {
|
|
36
|
-
return Object.keys(this._participants)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
indexedById() {
|
|
40
|
-
// returns an object that has AnearParticipant.id as key
|
|
41
|
-
return this._participants
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getById(participantId) {
|
|
45
|
-
return this._participants[participantId]
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
exists({id}) {
|
|
49
|
-
const emptyObject = (obj) => {
|
|
50
|
-
return Object.keys(obj).length === 0 && Object.getPrototypeOf(obj) === Object.prototype
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const participant = this.getById(id)
|
|
54
|
-
|
|
55
|
-
if (!participant) { return false } // id not found
|
|
56
|
-
|
|
57
|
-
return !emptyObject(participant)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get({id}) {
|
|
61
|
-
return this.getById(id)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get host() {
|
|
65
|
-
return this._host
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
set host(h) {
|
|
69
|
-
this._host = h
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
get all() {
|
|
73
|
-
return Object.values(this._participants).
|
|
74
|
-
sort((ca, cb) => ca.timestamp - cb.timestamp)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
get active() {
|
|
78
|
-
return Object.values(this._participants).filter(c => c.state === ActiveState)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
get idle() {
|
|
82
|
-
return Object.values(this._participants).filter(c => c.state === IdleState)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
get count() {
|
|
86
|
-
return this.ids.length
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
get numActive() {
|
|
90
|
-
return this.active.length
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
get numIdle() {
|
|
94
|
-
return this.idle.length
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
isIdle(c, currentTimestamp) {
|
|
98
|
-
if (!this.idleMsecs) return false
|
|
99
|
-
return (currentTimestamp - c.timestamp) >= this.idleMsecs
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
isActive(c, currentTimestamp) {
|
|
103
|
-
if (!this.idleMsecs) return true
|
|
104
|
-
return (currentTimestamp - c.timestamp) < this.idleMsecs
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
isPurge(c, currentTimestamp) {
|
|
108
|
-
if (!this.purgeMsecs) return false
|
|
109
|
-
return (currentTimestamp - c.timestamp) >= this.purgeMsecs
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
updateState(currentTimestamp) {
|
|
113
|
-
const sweeper = participant => {
|
|
114
|
-
if (participant.state === ActiveState) {
|
|
115
|
-
if (this.isIdle(participant, currentTimestamp)) {
|
|
116
|
-
participant.state = IdleState
|
|
117
|
-
}
|
|
118
|
-
} else if (participant.state === IdleState) {
|
|
119
|
-
if (this.isPurge(participant, currentTimestamp)) {
|
|
120
|
-
this.purge(participant)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.ids.forEach(
|
|
126
|
-
k => {
|
|
127
|
-
const c = this._participants[k]
|
|
128
|
-
sweeper(c)
|
|
129
|
-
}
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
add(anearParticipant, withTimestamp = this.currentTimestamp) {
|
|
134
|
-
if (anearParticipant.isHost() && this.anearEvent.hosted) {
|
|
135
|
-
// the host is not an eligible participant and isn't active nor idle
|
|
136
|
-
this.host = anearParticipant
|
|
137
|
-
} else {
|
|
138
|
-
this._participants[anearParticipant.id] = anearParticipant
|
|
139
|
-
this.markActive(anearParticipant, withTimestamp)
|
|
140
|
-
}
|
|
141
|
-
return anearParticipant
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
markActive(anearParticipant, withTimestamp = this.currentTimestamp) {
|
|
145
|
-
anearParticipant.timestamp = withTimestamp
|
|
146
|
-
anearParticipant.state = ActiveState
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
purge(participant) {
|
|
150
|
-
if (!participant) return
|
|
151
|
-
|
|
152
|
-
const {id} = participant
|
|
153
|
-
|
|
154
|
-
if (this.host && (id === this.host.id)) {
|
|
155
|
-
this.host = null
|
|
156
|
-
} else {
|
|
157
|
-
if (this._participants[id]) {
|
|
158
|
-
delete this._participants[id]
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
get currentTimestamp() {
|
|
164
|
-
return new Date().getTime()
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
load(anearParticipants) {
|
|
168
|
-
// used for tests only
|
|
169
|
-
anearParticipants.forEach(
|
|
170
|
-
p => {
|
|
171
|
-
if (p.isHost() && this.anearEvent.hosted) {
|
|
172
|
-
this.host = p
|
|
173
|
-
} else {
|
|
174
|
-
this._participants[p.id] = p
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
module.exports = Participants
|
package/lib/utils/Persist.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict"
|
|
2
|
-
|
|
3
|
-
const redis = require('async-redis')
|
|
4
|
-
|
|
5
|
-
class Persist {
|
|
6
|
-
|
|
7
|
-
constructor(options={}) {
|
|
8
|
-
this.init(options)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
init(options={}) {
|
|
12
|
-
this.client = redis.createClient() // TODO: createClient redis options passed via options.redis_config
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async close() {
|
|
16
|
-
await this.client.quit()
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async store(model) {
|
|
20
|
-
const jsonString = JSON.stringify(model.toJSON())
|
|
21
|
-
return await this.client.set(model.key, jsonString)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async fetch(key) {
|
|
25
|
-
const model = await this.client.get(key)
|
|
26
|
-
return JSON.parse(model)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async exists(model) {
|
|
30
|
-
const result = await this.client.exists(model.key)
|
|
31
|
-
return result == 1
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async create(model) {
|
|
35
|
-
const alreadyPersisted = await this.exists(model)
|
|
36
|
-
|
|
37
|
-
if (alreadyPersisted) {
|
|
38
|
-
throw new Error(`model ${model.id} already exists`)
|
|
39
|
-
}
|
|
40
|
-
return await this.store(model)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async update(model) {
|
|
44
|
-
return await this.store(model)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async remove(model) {
|
|
48
|
-
return await this.client.del(model.key)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = Persist
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
"use strict"
|
|
2
|
-
|
|
3
|
-
const Participants = require('../lib/utils/Participants')
|
|
4
|
-
const { ParticipantsFixture: participantsJSON,
|
|
5
|
-
AnearParticipantFixture2: visitor2JSON,
|
|
6
|
-
AnearHostFixture: hostJSON } = require('./fixtures')
|
|
7
|
-
const AnearParticipant = require('../lib/models/AnearParticipant')
|
|
8
|
-
const AnearParticipantJSONBuilder = require('./utils/AnearParticipantJSONBuilder')
|
|
9
|
-
|
|
10
|
-
const user1Id = "e053977c-dcb6-40e0-b7b8-e3dbd70ec8fd"
|
|
11
|
-
const idleId = "f1056e6c-c393-4617-8a06-67ba9d2f4b8a"
|
|
12
|
-
const activeId = user1Id
|
|
13
|
-
const hours24 = (24 * 60 * 60 * 1000)
|
|
14
|
-
const GeoLocation = {lat: 25.8348343, lng: -80.38438434}
|
|
15
|
-
|
|
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))
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
|
-
return participants
|
|
33
|
-
}
|
|
34
|
-
|
|
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)
|
|
48
|
-
} else {
|
|
49
|
-
withTimestamp = timestamp - participants.idleMsecs
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
participant.state = attrs.state
|
|
53
|
-
participant.timestamp = withTimestamp
|
|
54
|
-
participant.host = attrs.isHost
|
|
55
|
-
return participant
|
|
56
|
-
}
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
participants.load(anearParticipants)
|
|
60
|
-
|
|
61
|
-
return participants
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const now = new Date().getTime()
|
|
65
|
-
|
|
66
|
-
test('constructor with JSON provided', () => {
|
|
67
|
-
const ids = ['123', '345', '456']
|
|
68
|
-
const args = {idleMsecs: 23400, purgeMsecs: 678900, ids}
|
|
69
|
-
const p = new Participants(MockHostedEvent, args)
|
|
70
|
-
expect(p).toBeDefined()
|
|
71
|
-
expect(p.idleMsecs).toBe(args.idleMsecs)
|
|
72
|
-
expect(p.purgeMsecs).toBe(args.purgeMsecs)
|
|
73
|
-
expect(p.ids).toStrictEqual(ids)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
test('constructor with NO JSON provided has default idle and purge Msecs', () => {
|
|
77
|
-
const p = new Participants(MockHostedEvent)
|
|
78
|
-
expect(p).toBeDefined()
|
|
79
|
-
expect(p.idleMsecs).toBe(1800000)
|
|
80
|
-
expect(p.purgeMsecs).toBe(7200000)
|
|
81
|
-
expect(p.all).toHaveLength(0)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('constructor with null idle and purge msecs avoids idle purge', () => {
|
|
85
|
-
const p = newActiveParticipants(now, {idleMsecs: null, purgeMsecs: null})
|
|
86
|
-
expect(p.idleMsecs).toBe(null)
|
|
87
|
-
expect(p.purgeMsecs).toBe(null)
|
|
88
|
-
expect(p.idle).toHaveLength(0)
|
|
89
|
-
expect(p.active).toHaveLength(10)
|
|
90
|
-
|
|
91
|
-
const c = p.getById(idleId)
|
|
92
|
-
expect(p.isIdle(c, now)).toBeFalsy()
|
|
93
|
-
c.timestamp = c.timestamp - hours24
|
|
94
|
-
expect(p.isPurge(c, now)).toBeFalsy()
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
test('getById', () => {
|
|
98
|
-
const p = newActiveParticipants(now)
|
|
99
|
-
expect(p.getById(user1Id)).toHaveProperty("name", "user1")
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('exists success', () => {
|
|
103
|
-
const p = newActiveParticipants(now)
|
|
104
|
-
expect(p.exists({id: user1Id})).toBeTruthy()
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('exists fail', () => {
|
|
108
|
-
const p = newActiveParticipants(now)
|
|
109
|
-
expect(p.exists({id: "abcd"})).toBeFalsy()
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('get success', () => {
|
|
113
|
-
const p = newActiveParticipants(now)
|
|
114
|
-
expect(p.get({id: user1Id})).toHaveProperty("name", "user1")
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test('getParticipant fail', () => {
|
|
118
|
-
const p = newActiveParticipants(now)
|
|
119
|
-
expect(p.get({id: "abcd"})).toBeUndefined()
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
test('getParticipant success', () => {
|
|
123
|
-
const p = newActiveParticipants(now)
|
|
124
|
-
expect(p.get({id: user1Id}).name).toBe("user1")
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
test('add() participant user', async () => {
|
|
128
|
-
const p = newCurrentParticipants(now, MockNonHostedEvent)
|
|
129
|
-
const participant = new AnearParticipant(visitor2JSON, MockNonHostedEvent)
|
|
130
|
-
participant.geoLocation = GeoLocation
|
|
131
|
-
|
|
132
|
-
p.add(participant)
|
|
133
|
-
const part = p.get(participant)
|
|
134
|
-
expect(part.name).toBe("bbondfl93")
|
|
135
|
-
expect(part.avatarUrl).toBe("https://s3.amazonaws.com/anearassets/barbara_bond.png")
|
|
136
|
-
expect(part.userId).toBe("d280da7c-1baf-4607-a286-4b5faa03eaa7")
|
|
137
|
-
|
|
138
|
-
await AnearParticipant.close()
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
test('add() host user', async () => {
|
|
142
|
-
const p = newCurrentParticipants(now)
|
|
143
|
-
const host = new AnearParticipant(hostJSON, MockNonHostedEvent)
|
|
144
|
-
host.geoLocation = GeoLocation
|
|
145
|
-
|
|
146
|
-
p.add(host)
|
|
147
|
-
expect(p.get(host)).toBeUndefined()
|
|
148
|
-
expect(p.host.name).toBe('foxhole_host')
|
|
149
|
-
expect(p.host.avatarUrl).toBe("https://s3.amazonaws.com/anearassets/foxhole.png")
|
|
150
|
-
|
|
151
|
-
await AnearParticipant.close()
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test('active', () => {
|
|
155
|
-
const p = newActiveParticipants(now)
|
|
156
|
-
expect(p.active).toHaveLength(10)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test('idle', () => {
|
|
160
|
-
const p = newCurrentParticipants(now)
|
|
161
|
-
expect(p.idle).toHaveLength(2)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
test('toJSON', () => {
|
|
165
|
-
const p = newActiveParticipants(now)
|
|
166
|
-
const j = p.toJSON()
|
|
167
|
-
expect(j).toHaveProperty("ids")
|
|
168
|
-
expect(j).toHaveProperty("idleMsecs")
|
|
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)
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
test('isIdle', () => {
|
|
177
|
-
const p = newCurrentParticipants(now)
|
|
178
|
-
const c = p.getById(idleId)
|
|
179
|
-
expect(p.isIdle(c, now)).toBeTruthy()
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
test('isActive', () => {
|
|
183
|
-
const p = newCurrentParticipants(now)
|
|
184
|
-
const c = p.getById(activeId)
|
|
185
|
-
expect(p.isActive(c, now)).toBeTruthy()
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
test('isPurge', () => {
|
|
189
|
-
const p = newCurrentParticipants(now)
|
|
190
|
-
const c = p.getById(idleId)
|
|
191
|
-
c.timestamp = c.timestamp - hours24
|
|
192
|
-
expect(p.isPurge(c, now)).toBeTruthy()
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
test('purgeParticipant participant user-type', () => {
|
|
196
|
-
const p = newCurrentParticipants(now)
|
|
197
|
-
const c = p.getById(idleId)
|
|
198
|
-
p.purge(c)
|
|
199
|
-
expect(p.exists(c)).toBeFalsy()
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
test('purge host user-type', () => {
|
|
203
|
-
const p = newActiveParticipants(now)
|
|
204
|
-
const c = p.host
|
|
205
|
-
p.purge(c)
|
|
206
|
-
expect(p.host).toStrictEqual(null)
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
test('updateState will leave state unchanged when timeout criteria not met', () => {
|
|
210
|
-
const p = newCurrentParticipants(now)
|
|
211
|
-
p.updateState(now)
|
|
212
|
-
expect(p.active).toHaveLength(8)
|
|
213
|
-
expect(p.idle).toHaveLength(2)
|
|
214
|
-
expect(p.numActive).toBe(8)
|
|
215
|
-
expect(p.numIdle).toBe(2)
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
test('updateState will mark active to idle when timeout reached', () => {
|
|
219
|
-
const current = now
|
|
220
|
-
const p = newCurrentParticipants(current)
|
|
221
|
-
expect(p.active).toHaveLength(8)
|
|
222
|
-
p.updateState(current + p.idleMsecs + 1000)
|
|
223
|
-
expect(Object.values(p.all).
|
|
224
|
-
filter(p => p.state === 'active')).toHaveLength(0)
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
test('updateState will purge idle participants', () => {
|
|
228
|
-
const current = now
|
|
229
|
-
const p = newCurrentParticipants(current)
|
|
230
|
-
const idlers = p.idle
|
|
231
|
-
expect(idlers).toHaveLength(2)
|
|
232
|
-
p.updateState(current + p.purgeMsecs + 1000)
|
|
233
|
-
idlers.forEach(c => expect(p.getById(c.id)).toBeUndefined())
|
|
234
|
-
})
|
package/tests/Persist.test.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
const Persist = require('../lib/utils/Persist')
|
|
2
|
-
|
|
3
|
-
const p = new Persist()
|
|
4
|
-
const key = "myModel"
|
|
5
|
-
|
|
6
|
-
afterAll(
|
|
7
|
-
async () => {
|
|
8
|
-
await p.close()
|
|
9
|
-
}
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
const barb = "Barbie"
|
|
13
|
-
const dave = "Dave"
|
|
14
|
-
|
|
15
|
-
class TestModel {
|
|
16
|
-
constructor(keyArg, nameArg) {
|
|
17
|
-
this.id = 1
|
|
18
|
-
this.key = keyArg
|
|
19
|
-
this.name = nameArg
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
toJSON() {
|
|
23
|
-
return {name: this.name, age: 27, state: 'FL'}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const testModel = new TestModel(key, barb)
|
|
28
|
-
const daveModel = new TestModel(key, dave)
|
|
29
|
-
|
|
30
|
-
test('constructor', () => {
|
|
31
|
-
expect(p).not.toBe(null)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
test('store a model', async () => {
|
|
35
|
-
try {
|
|
36
|
-
const result = await p.store(testModel)
|
|
37
|
-
|
|
38
|
-
expect(testModel.key).toBe(key)
|
|
39
|
-
expect(result).toBe('OK')
|
|
40
|
-
} catch(error) {
|
|
41
|
-
console.error(error)
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
test('fetch a model', async () => {
|
|
46
|
-
try {
|
|
47
|
-
const model = await p.fetch(key)
|
|
48
|
-
expect(model.name).toBe(barb)
|
|
49
|
-
} catch(error) {
|
|
50
|
-
console.error(error)
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('remove a model', async () => {
|
|
55
|
-
try {
|
|
56
|
-
const result = await p.remove(testModel)
|
|
57
|
-
} catch(error) {
|
|
58
|
-
console.error(error)
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test('create a model only once, and exists only when it does', async () => {
|
|
63
|
-
try {
|
|
64
|
-
const result = await p.create(testModel)
|
|
65
|
-
expect(result).toBe('OK')
|
|
66
|
-
|
|
67
|
-
let isThere = await p.exists(testModel)
|
|
68
|
-
expect(isThere).toBe(true)
|
|
69
|
-
|
|
70
|
-
await expect(p.create(testModel))
|
|
71
|
-
.rejects
|
|
72
|
-
.toThrow('model 1 already exists');
|
|
73
|
-
|
|
74
|
-
await p.remove(testModel)
|
|
75
|
-
|
|
76
|
-
isThere = await p.exists(testModel)
|
|
77
|
-
expect(isThere).toBe(false)
|
|
78
|
-
|
|
79
|
-
} catch(error) {
|
|
80
|
-
console.error(error)
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('update a model', async () => {
|
|
85
|
-
try {
|
|
86
|
-
const result = await p.create(testModel)
|
|
87
|
-
expect(result).toBe('OK')
|
|
88
|
-
|
|
89
|
-
let model = await p.fetch(key)
|
|
90
|
-
expect(model.name).toBe(barb)
|
|
91
|
-
|
|
92
|
-
const ures = await p.update(daveModel)
|
|
93
|
-
expect(ures).toBe('OK')
|
|
94
|
-
|
|
95
|
-
model = await p.fetch(key)
|
|
96
|
-
expect(model.name).toBe(dave)
|
|
97
|
-
|
|
98
|
-
await p.remove(testModel)
|
|
99
|
-
} catch(error) {
|
|
100
|
-
console.error(error)
|
|
101
|
-
}
|
|
102
|
-
})
|