@subiz/wsclient 4.0.30 → 4.0.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/README.md +13 -10
- package/common.js +2 -2
- package/package.json +1 -1
- package/realtime.js +117 -137
- package/realtime.test.js +51 -5
package/README.md
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
# Subiz
|
|
1
|
+
# Subiz wsclient
|
|
2
2
|
|
|
3
3
|
This library implements subiz long polling protocol
|
|
4
4
|
|
|
5
5
|
## Method
|
|
6
|
+
|
|
6
7
|
### subscribe
|
|
7
|
-
|
|
8
|
-
###
|
|
8
|
+
|
|
9
|
+
### onEvent
|
|
10
|
+
|
|
9
11
|
### stop
|
|
10
12
|
|
|
11
13
|
## Example
|
|
12
|
-
|
|
14
|
+
|
|
15
|
+
```js
|
|
13
16
|
var Realtime = require('@subiz/wsclient')
|
|
14
17
|
var realtime = new Realtime('https://realtime-0.subiz.net/', {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
account_id: 'ac1234',
|
|
19
|
+
getAccessToken: () => '333344',
|
|
20
|
+
user_mask: 'mask123',
|
|
18
21
|
})
|
|
19
22
|
|
|
20
|
-
realtime.onEvent(ev => console.log(ev))
|
|
21
|
-
realtime.onInterrupted(() => console.log('some message may have been losted'))
|
|
23
|
+
realtime.onEvent((ev) => console.log(ev))
|
|
22
24
|
|
|
23
25
|
// subscribe events
|
|
24
|
-
realtime.subscribe([
|
|
26
|
+
await realtime.subscribe(['message_sent', 'user_info_updated'])
|
|
27
|
+
console.log(realtime.getStatus()) // 'active'
|
|
25
28
|
|
|
26
29
|
realtime.stop()
|
|
27
30
|
```
|
package/common.js
CHANGED
|
@@ -64,8 +64,8 @@ var xhrsend = function (method, url, body, cb) {
|
|
|
64
64
|
request.send(body)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// JSON.parse without
|
|
68
|
-
// return undefined when
|
|
67
|
+
// JSON.parse without exception
|
|
68
|
+
// return undefined when input is an invalid JSON
|
|
69
69
|
function parseJSON(str) {
|
|
70
70
|
try {
|
|
71
71
|
return JSON.parse(str)
|
package/package.json
CHANGED
package/realtime.js
CHANGED
|
@@ -17,174 +17,163 @@ var {xhrsend, parseJSON, Pubsub} = require('./common.js')
|
|
|
17
17
|
function Conn(apiUrl, credential, onDead, onEvents, callAPI, accid) {
|
|
18
18
|
callAPI = callAPI || xhrsend // allow hook
|
|
19
19
|
credential = credential || {}
|
|
20
|
-
// tell if connection is dead
|
|
21
|
-
|
|
22
20
|
var lastToken = ''
|
|
23
21
|
if (!accid) accid = ''
|
|
24
|
-
|
|
25
|
-
var
|
|
22
|
+
var is_dead = false // tell if the connection is dead
|
|
23
|
+
var is_polling_stuck = false // tell if we've having trouble doing /poll
|
|
24
|
+
var is_sub_stuck = true // tell if we've having trouble doing /subscribe
|
|
26
25
|
|
|
27
26
|
// The long polling loop runs sequentially, with each poll starting immediately
|
|
28
27
|
// after the previous one completes. If an error occurs, the loop will pause
|
|
29
28
|
// using an exponential backoff strategy. It will terminate if an unretryable
|
|
30
|
-
// error is encountered.
|
|
31
|
-
// call completes successfully
|
|
29
|
+
// error is encountered.
|
|
30
|
+
// The first loop begins after the first subscription call completes successfully
|
|
32
31
|
var polling = function (backoff) {
|
|
33
|
-
if (
|
|
32
|
+
if (is_dead) return
|
|
34
33
|
callAPI('get', apiUrl + 'poll?token=' + lastToken + '&account-id=' + accid, undefined, function (body, code) {
|
|
35
|
-
if (
|
|
36
|
-
|
|
34
|
+
if (is_dead) return
|
|
37
35
|
if (retryable(code)) {
|
|
38
|
-
|
|
36
|
+
is_polling_stuck = true
|
|
39
37
|
return setTimeout(polling, calcNextBackoff(backoff), backoff + 1)
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
if (code !== 200) {
|
|
43
41
|
// unretryable error, should kill
|
|
44
|
-
|
|
45
|
-
return
|
|
42
|
+
is_polling_stuck = true
|
|
43
|
+
return kill('poll')
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
if (status == 'polling') status = 'active'
|
|
49
46
|
// 200, success
|
|
47
|
+
is_polling_stuck = false
|
|
50
48
|
body = parseJSON(body) || {}
|
|
51
49
|
if (body.host) apiUrl = absUrl(body.host)
|
|
52
50
|
|
|
53
51
|
var seqToken = body.sequential_token
|
|
54
52
|
// the server returns a malform payload. We should end
|
|
55
|
-
if (!seqToken)
|
|
56
|
-
status = 'dead'
|
|
57
|
-
return onDead('poll')
|
|
58
|
-
}
|
|
53
|
+
if (!seqToken) return kill('poll')
|
|
59
54
|
lastToken = seqToken
|
|
60
55
|
onEvents(body.events || [])
|
|
61
56
|
return polling(0)
|
|
62
57
|
})
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
var subQueue = flow.batch(50, 100, function (
|
|
66
|
-
if (
|
|
67
|
-
if (
|
|
60
|
+
var subQueue = flow.batch(50, 100, function (topics) {
|
|
61
|
+
if (is_dead) return Promise.resolve(repeat('dead', topics.length))
|
|
62
|
+
if (topics.length <= 0) return Promise.resolve([])
|
|
63
|
+
|
|
64
|
+
var resolve
|
|
65
|
+
var promise = new Promise((rs) => {
|
|
66
|
+
resolve = rs
|
|
67
|
+
})
|
|
68
68
|
|
|
69
69
|
var done = false
|
|
70
|
-
setTimeout(()
|
|
71
|
-
if (done)
|
|
72
|
-
if (status == 'dead') return
|
|
73
|
-
if (status == 'active') status = 'subbing'
|
|
70
|
+
setTimeout(function () {
|
|
71
|
+
if (!done && !is_dead) is_sub_stuck = true
|
|
74
72
|
}, 4000)
|
|
75
73
|
|
|
76
74
|
setTimeout(() => {
|
|
77
75
|
// too long, kill
|
|
78
76
|
if (done) return
|
|
79
77
|
done = true
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
resolve(false) // break loop
|
|
78
|
+
is_sub_stuck = true
|
|
79
|
+
resolve(repeat('dead', topics.length))
|
|
80
|
+
if (is_dead) return
|
|
81
|
+
kill('subs')
|
|
85
82
|
}, 60000)
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
flow.loop(function () {
|
|
85
|
+
return new Promise(function (rs) {
|
|
86
|
+
if (is_dead) {
|
|
87
|
+
resolve(repeat('dead', topics.length))
|
|
88
|
+
return rs(false) // break loop
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var query = '?token=' + lastToken
|
|
92
|
+
credential.getAccessToken().then(function (access_token) {
|
|
93
|
+
if (is_dead) {
|
|
94
|
+
resolve(repeat('dead', topics.length))
|
|
97
95
|
return rs(false) // break loop
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
credential.
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
if (credential.user_ref) query += '&user_ref=' + encodeURIComponent(credential.user_ref)
|
|
99
|
+
else if (credential.user_mask) query += '&user-mask=' + encodeURIComponent(credential.user_mask)
|
|
100
|
+
else if (access_token) query += '&access-token=' + access_token
|
|
101
|
+
|
|
102
|
+
var fullurl = apiUrl + 'subs' + query + '&account-id=' + encodeURIComponent(accid || credential.account_id)
|
|
103
|
+
callAPI('post', fullurl, JSON.stringify({events: topics}), function (body, code) {
|
|
104
|
+
if (done) return
|
|
105
|
+
if (is_dead) {
|
|
106
|
+
resolve(repeat('dead', topics.length))
|
|
104
107
|
return rs(false) // break loop
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return rs(false) // break loop
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (retryable(code)) {
|
|
122
|
-
status = 'subbing'
|
|
123
|
-
return setTimeout(rs, 3000, true)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// unretryable error
|
|
127
|
-
if (code !== 200) {
|
|
128
|
-
// we are unable to start the communication with the realtime server.
|
|
129
|
-
// Must notify the user by killing the connection
|
|
130
|
-
status = 'dead'
|
|
131
|
-
onDead('subscribe', body, code)
|
|
132
|
-
out = repeat('dead', events.length)
|
|
133
|
-
return rs(false)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
done = true
|
|
137
|
-
if (lastToken) return rs(false)
|
|
138
|
-
|
|
139
|
-
// first time sub, should grab the initial token
|
|
140
|
-
body = parseJSON(body) || {}
|
|
141
|
-
var initialToken = body.initial_token
|
|
142
|
-
// the server returns a malform payload. We should end
|
|
143
|
-
if (!initialToken) {
|
|
144
|
-
status = 'dead'
|
|
145
|
-
onDead('subscribe', body, code)
|
|
146
|
-
out = repeat('dead', events.length)
|
|
147
|
-
return rs(false) // break loop
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (body.host) apiUrl = absUrl(body.host)
|
|
151
|
-
|
|
152
|
-
lastToken = initialToken
|
|
153
|
-
polling(0)
|
|
110
|
+
if (retryable(code)) {
|
|
111
|
+
is_sub_stuck = true
|
|
112
|
+
return setTimeout(rs, 3000, true)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// unretryable error
|
|
116
|
+
if (code !== 200) {
|
|
117
|
+
// we are unable to start the communication with the realtime server.
|
|
118
|
+
// Must notify the user by killing the connection
|
|
119
|
+
kill('subs')
|
|
120
|
+
resolve(repeat('dead', topics.length))
|
|
154
121
|
return rs(false)
|
|
155
|
-
}
|
|
122
|
+
}
|
|
123
|
+
is_sub_stuck = false
|
|
124
|
+
done = true
|
|
125
|
+
if (lastToken) {
|
|
126
|
+
resolve(topics)
|
|
127
|
+
return rs(false)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// first time sub, should grab the initial token
|
|
131
|
+
body = parseJSON(body) || {}
|
|
132
|
+
var initialToken = body.initial_token
|
|
133
|
+
// the server returns a malform payload. We should end
|
|
134
|
+
if (!initialToken) {
|
|
135
|
+
kill('subs')
|
|
136
|
+
resolve(repeat('dead', topics.length))
|
|
137
|
+
return rs(false) // break loop
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// learn new host
|
|
141
|
+
if (body.host) apiUrl = absUrl(body.host)
|
|
142
|
+
|
|
143
|
+
lastToken = initialToken
|
|
144
|
+
polling(0)
|
|
145
|
+
resolve(topics)
|
|
146
|
+
return rs(false) // to break the loop
|
|
156
147
|
})
|
|
157
148
|
})
|
|
158
149
|
})
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
})
|
|
150
|
+
})
|
|
151
|
+
|
|
162
152
|
return promise
|
|
163
153
|
})
|
|
164
154
|
|
|
165
155
|
// subscribe call /subs API to tell server that we are listening for those events
|
|
166
156
|
// this function may start polling loop if it hasn't been started yet
|
|
167
|
-
//
|
|
168
|
-
// function multiple times at once
|
|
157
|
+
// its safe to call this function multiple times
|
|
169
158
|
this.subscribe = subQueue.push.bind(subQueue)
|
|
170
159
|
|
|
171
160
|
this.getStatus = function () {
|
|
172
|
-
if (
|
|
173
|
-
return 'connecting'
|
|
161
|
+
if (is_dead) return 'dead'
|
|
162
|
+
if (is_sub_stuck || is_polling_stuck) return 'connecting'
|
|
163
|
+
return 'active'
|
|
174
164
|
}
|
|
175
165
|
|
|
176
166
|
// kill force the connection to dead state
|
|
177
|
-
|
|
178
|
-
|
|
167
|
+
var kill = function (reason) {
|
|
168
|
+
is_dead = true
|
|
169
|
+
onDead(reason)
|
|
179
170
|
}
|
|
171
|
+
this.kill = kill
|
|
180
172
|
}
|
|
181
173
|
|
|
182
|
-
// Realtime
|
|
183
|
-
// This class helps you subscribe and listen realtime event from the
|
|
174
|
+
// Realtime helps you subscribe and listen realtime event from th
|
|
184
175
|
// realtime server
|
|
185
|
-
//
|
|
186
|
-
// + auto recreate and resub if the last conn is dead
|
|
187
|
-
// + don't subscribe already subscribed events
|
|
176
|
+
// Realtime is just a stronger Conn.
|
|
188
177
|
function Realtime(apiUrls, credential, callAPI, accid, skipautoreconnect) {
|
|
189
178
|
if (typeof apiUrls === 'string' || apiUrls instanceof String) apiUrls = [apiUrls]
|
|
190
179
|
credential = credential || {}
|
|
@@ -197,19 +186,13 @@ function Realtime(apiUrls, credential, callAPI, accid, skipautoreconnect) {
|
|
|
197
186
|
var pubsub = new Pubsub()
|
|
198
187
|
|
|
199
188
|
// holds all topics that realtime must subscribed
|
|
200
|
-
// {
|
|
201
|
-
// a: 'online',
|
|
202
|
-
// b: 'offline',
|
|
203
|
-
// c: 'connecting',
|
|
204
|
-
// }
|
|
205
189
|
var topics = {}
|
|
206
190
|
|
|
207
191
|
// stop the connection
|
|
208
|
-
var stop = false
|
|
209
192
|
this.stop = function () {
|
|
210
|
-
if (!conn
|
|
211
|
-
stop = true
|
|
193
|
+
if (!conn) return
|
|
212
194
|
conn.kill()
|
|
195
|
+
conn = undefined
|
|
213
196
|
pubsub.emit('interrupted')
|
|
214
197
|
}
|
|
215
198
|
|
|
@@ -226,9 +209,8 @@ function Realtime(apiUrls, credential, callAPI, accid, skipautoreconnect) {
|
|
|
226
209
|
return conn.getStatus()
|
|
227
210
|
}
|
|
228
211
|
|
|
229
|
-
var
|
|
230
|
-
|
|
231
|
-
if (stop) return Promise.resolve({})
|
|
212
|
+
var subscribe = function (events) {
|
|
213
|
+
if (!conn) return Promise.resolve({error: 'dead'})
|
|
232
214
|
if (typeof events === 'string') events = [events]
|
|
233
215
|
if (!Array.isArray(events)) return Promise.resolve({error: 'param should be an array or string'})
|
|
234
216
|
|
|
@@ -237,50 +219,48 @@ function Realtime(apiUrls, credential, callAPI, accid, skipautoreconnect) {
|
|
|
237
219
|
for (var i = 0; i < events.length; i++) {
|
|
238
220
|
var topic = events[i]
|
|
239
221
|
if (!topic) continue
|
|
240
|
-
if (
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
222
|
+
if (topics[topic]) continue
|
|
223
|
+
topics[topic] = true // mark so next time we wont subscribe this event (topic) again
|
|
224
|
+
all.push(conn.subscribe(topic))
|
|
244
225
|
}
|
|
245
226
|
if (all.length === 0) return Promise.resolve({})
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
for (var i = 0; i < errs.length; i++) if (errs[i]) return {error: errs[i]}
|
|
249
|
-
for (var j = 0; j < events.length; j++) topics[events[j]] = true
|
|
227
|
+
return Promise.all(all).then(function () {
|
|
228
|
+
if (!conn || conn.getStatus() == 'dead') return {error: 'dead'}
|
|
250
229
|
return {}
|
|
251
230
|
})
|
|
252
231
|
}
|
|
232
|
+
this.subscribe = subscribe
|
|
253
233
|
|
|
254
234
|
// reconnect make sure there is alway a Conn running in the background
|
|
255
235
|
// if the Conn is dead, it recreate a new one
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// reset subscribed topic
|
|
259
|
-
var allTopics = Object.keys(topics)
|
|
260
|
-
for (var i = 0; i < allTopics.length; i++) topics[allTopics[i]] = false
|
|
236
|
+
var conn
|
|
237
|
+
let reconnect = function () {
|
|
261
238
|
var randomUrl = apiUrls[Math.floor(Math.random() * apiUrls.length)]
|
|
262
239
|
if (conn) conn.kill() // kill old connection
|
|
263
|
-
|
|
240
|
+
let myconn = new Conn(
|
|
264
241
|
randomUrl,
|
|
265
242
|
credential,
|
|
266
243
|
function (code, body, status) {
|
|
267
|
-
if (
|
|
244
|
+
if (myconn != conn) return // outdated
|
|
245
|
+
conn = undefined // prevent dupplicate dead fire
|
|
268
246
|
pubsub.emit('interrupted', code, body, status)
|
|
269
|
-
if (!skipautoreconnect) setTimeout(
|
|
247
|
+
if (!skipautoreconnect) setTimeout(reconnect, 2000) // reconnect and resubscribe after 2 sec
|
|
270
248
|
},
|
|
271
249
|
function (events) {
|
|
272
|
-
if (
|
|
250
|
+
if (myconn != conn) return
|
|
273
251
|
for (var i = 0; i < events.length; i++) pubsub.emit('event', events[i])
|
|
274
252
|
},
|
|
275
253
|
callAPI,
|
|
276
254
|
accid,
|
|
277
255
|
)
|
|
278
|
-
|
|
256
|
+
conn = myconn
|
|
257
|
+
var copyTopics = Object.keys(topics)
|
|
258
|
+
topics = {}
|
|
279
259
|
// resubscribe all subscribed events
|
|
280
|
-
|
|
281
|
-
return this.subscribe(topicKeys)
|
|
260
|
+
return subscribe(copyTopics)
|
|
282
261
|
}
|
|
283
|
-
this.reconnect
|
|
262
|
+
this.reconnect = reconnect
|
|
263
|
+
reconnect()
|
|
284
264
|
}
|
|
285
265
|
|
|
286
266
|
// calcNextBackoff returns number of seconds we must wait
|
package/realtime.test.js
CHANGED
|
@@ -11,7 +11,7 @@ async function testScript(t, script) {
|
|
|
11
11
|
calls.push({method, url, body, cb})
|
|
12
12
|
},
|
|
13
13
|
'acctest',
|
|
14
|
-
|
|
14
|
+
!script.auto_reconnect,
|
|
15
15
|
)
|
|
16
16
|
realtime.onEvent((ev) => events.push(ev))
|
|
17
17
|
|
|
@@ -81,7 +81,7 @@ async function testScript(t, script) {
|
|
|
81
81
|
break
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
t.true(!istimeout, JSON.stringify(step))
|
|
84
|
+
t.true(!istimeout, script.name + ' ' + stepi + ' ' + JSON.stringify(step))
|
|
85
85
|
continue
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -100,7 +100,6 @@ async function testScript(t, script) {
|
|
|
100
100
|
}
|
|
101
101
|
t.true(false, script.name + '[' + stepi + ']=' + step.type)
|
|
102
102
|
}
|
|
103
|
-
|
|
104
103
|
// check events
|
|
105
104
|
let scriptevents = script.events || []
|
|
106
105
|
t.equal(events.length, scriptevents.length)
|
|
@@ -119,7 +118,6 @@ test.only('realtime', async (t) => {
|
|
|
119
118
|
{type: 'subscribe', topics: ['message_sent']},
|
|
120
119
|
{type: 'status', status: 'active'},
|
|
121
120
|
{type: 'call', call_id: '1', path: '/subs', method: 'post', body: {events: ['message_sent']}},
|
|
122
|
-
|
|
123
121
|
{type: 'wait', timeout: 5000},
|
|
124
122
|
{type: 'status', status: 'connecting'},
|
|
125
123
|
{type: 'wait', timeout: 60000},
|
|
@@ -279,11 +277,59 @@ test.only('realtime', async (t) => {
|
|
|
279
277
|
{type: 'status', status: 'active'},
|
|
280
278
|
],
|
|
281
279
|
},
|
|
280
|
+
{
|
|
281
|
+
name: 'reconect while poll',
|
|
282
|
+
steps: [
|
|
283
|
+
{type: 'subscribe', topics: ['message_sent']},
|
|
284
|
+
{type: 'call', call_id: 1, path: '/subs'},
|
|
285
|
+
{type: 'response', call_id: '1', body: {initial_token: 'token1'}, code: 200},
|
|
286
|
+
{type: 'call', call_id: '2', path: '/poll', query: {token: 'token1'}, method: 'get'},
|
|
287
|
+
{type: 'status', status: 'active'},
|
|
288
|
+
{type: 'wait', timeout: 1000},
|
|
289
|
+
{type: 'response', call_id: '2', body: {events: [{text: 'xin chao'}], sequential_token: 'token2'}, code: 200},
|
|
290
|
+
{type: 'call', call_id: '3', path: '/poll', query: {token: 'token2'}, method: 'get'},
|
|
291
|
+
{type: 'response', call_id: '3', body: '400 dead', code: 400},
|
|
292
|
+
{type: 'status', status: 'dead'},
|
|
293
|
+
{type: 'reconnect'},
|
|
294
|
+
{type: 'wait', timeout: 1000},
|
|
295
|
+
{type: 'call', call_id: 4, path: '/subs'},
|
|
296
|
+
{type: 'wait', timeout: 1000},
|
|
297
|
+
{type: 'response', call_id: 4, body: {initial_token: 'token1'}, code: 200},
|
|
298
|
+
{type: 'status', status: 'active'},
|
|
299
|
+
],
|
|
300
|
+
events: [{text: 'xin chao'}],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'resub',
|
|
304
|
+
auto_reconnect: true,
|
|
305
|
+
steps: [
|
|
306
|
+
{type: 'subscribe', topics: ['message_sent']},
|
|
307
|
+
{type: 'call', call_id: 1, path: '/subs'},
|
|
308
|
+
{type: 'response', call_id: '1', body: 'wait a sec', code: 500},
|
|
309
|
+
{type: 'status', status: 'connecting'},
|
|
310
|
+
{type: 'wait', timeout: 1000},
|
|
311
|
+
{type: 'call', call_id: 2, path: '/subs'},
|
|
312
|
+
{type: 'wait', timeout: 61000},
|
|
313
|
+
{type: 'status', status: 'connecting'},
|
|
314
|
+
{type: 'call', call_id: 3, path: '/subs'},
|
|
315
|
+
{type: 'wait', timeout: 1000},
|
|
316
|
+
{type: 'response', call_id: '3', body: 'wait a sec', code: 500},
|
|
317
|
+
{type: 'status', status: 'connecting'},
|
|
318
|
+
{type: 'wait', timeout: 1000},
|
|
319
|
+
{type: 'call', call_id: 4, path: '/subs'},
|
|
320
|
+
{type: 'wait', timeout: 1000},
|
|
321
|
+
{type: 'response', call_id: '4', body: {initial_token: 'token1'}, code: 200},
|
|
322
|
+
{type: 'call', call_id: '5', path: '/poll', query: {token: 'token1'}, method: 'get'},
|
|
323
|
+
{type: 'status', status: 'active'},
|
|
324
|
+
{type: 'response', call_id: '5', body: {events: [{text: 'xin chao'}], sequential_token: 'token2'}, code: 200},
|
|
325
|
+
],
|
|
326
|
+
events: [{text: 'xin chao'}],
|
|
327
|
+
},
|
|
282
328
|
]
|
|
283
329
|
|
|
284
330
|
let ps = []
|
|
285
331
|
for (const script of scripts) {
|
|
286
|
-
|
|
332
|
+
if (script.name != 'resub') continue
|
|
287
333
|
ps.push(testScript(t, script))
|
|
288
334
|
}
|
|
289
335
|
await Promise.all(ps)
|