]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/api/live/live.ts
Introduce subscriptions command
[github/Chocobozzz/PeerTube.git] / server / tests / api / live / live.ts
CommitLineData
af4ae64f
C
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import * as chai from 'chai'
ca5c612b
C
5import { join } from 'path'
6import { ffprobePromise, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils'
053aed43 7import { LiveVideo, LiveVideoCreate, Video, VideoDetails, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
0d8de275 8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
af4ae64f 9import {
68e70a74 10 addVideoToBlacklist,
ca5c612b 11 buildServerDirectory,
bd54ad19 12 checkLiveCleanup,
5c0904fc 13 checkLiveSegmentHash,
bd54ad19 14 checkResolutionsInMasterPlaylist,
af4ae64f
C
15 cleanupTests,
16 createLive,
17 doubleFollow,
18 flushAndRunMultipleServers,
19 getLive,
1fd61899 20 getMyVideosWithFilter,
5c0904fc 21 getPlaylist,
af4ae64f
C
22 getVideo,
23 getVideosList,
1fd61899 24 getVideosWithFilters,
5c0904fc 25 killallServers,
af4ae64f
C
26 makeRawRequest,
27 removeVideo,
5c0904fc 28 reRunServer,
68e70a74 29 sendRTMPStream,
bd54ad19 30 sendRTMPStreamInVideo,
af4ae64f
C
31 ServerInfo,
32 setAccessTokensToServers,
33 setDefaultVideoChannel,
bd54ad19 34 stopFfmpeg,
97969c4e 35 testFfmpegStreamError,
af4ae64f 36 testImage,
af4ae64f 37 updateLive,
1fd61899 38 uploadVideoAndGetId,
e4bf7856 39 wait,
bd54ad19 40 waitJobs,
0e856b78 41 waitUntilLiveEnded,
6b67897e 42 waitUntilLivePublished,
8ebf2a5d 43 waitUntilLivePublishedOnAllServers,
5cac83a7 44 waitUntilLiveSegmentGeneration
af4ae64f
C
45} from '../../../../shared/extra-utils'
46
47const expect = chai.expect
48
49describe('Test live', function () {
50 let servers: ServerInfo[] = []
af4ae64f
C
51
52 before(async function () {
53 this.timeout(120000)
54
55 servers = await flushAndRunMultipleServers(2)
56
57 // Get the access tokens
58 await setAccessTokensToServers(servers)
59 await setDefaultVideoChannel(servers)
60
65e6e260
C
61 await servers[0].configCommand.updateCustomSubConfig({
62 newConfig: {
63 live: {
64 enabled: true,
65 allowReplay: true,
66 transcoding: {
67 enabled: false
68 }
68e70a74 69 }
af4ae64f
C
70 }
71 })
72
73 // Server 1 and server 2 follow each other
74 await doubleFollow(servers[0], servers[1])
75 })
76
77 describe('Live creation, update and delete', function () {
68e70a74 78 let liveVideoUUID: string
af4ae64f
C
79
80 it('Should create a live with the appropriate parameters', async function () {
81 this.timeout(20000)
82
83 const attributes: LiveVideoCreate = {
84 category: 1,
85 licence: 2,
86 language: 'fr',
87 description: 'super live description',
88 support: 'support field',
89 channelId: servers[0].videoChannel.id,
90 nsfw: false,
91 waitTranscoding: false,
92 name: 'my super live',
93 tags: [ 'tag1', 'tag2' ],
94 commentsEnabled: false,
95 downloadEnabled: false,
96 saveReplay: true,
97 privacy: VideoPrivacy.PUBLIC,
98 previewfile: 'video_short1-preview.webm.jpg',
99 thumbnailfile: 'video_short1.webm.jpg'
100 }
101
102 const res = await createLive(servers[0].url, servers[0].accessToken, attributes)
103 liveVideoUUID = res.body.video.uuid
104
105 await waitJobs(servers)
106
107 for (const server of servers) {
108 const resVideo = await getVideo(server.url, liveVideoUUID)
109 const video: VideoDetails = resVideo.body
110
111 expect(video.category.id).to.equal(1)
112 expect(video.licence.id).to.equal(2)
113 expect(video.language.id).to.equal('fr')
114 expect(video.description).to.equal('super live description')
115 expect(video.support).to.equal('support field')
116
117 expect(video.channel.name).to.equal(servers[0].videoChannel.name)
118 expect(video.channel.host).to.equal(servers[0].videoChannel.host)
119
1ab60243
C
120 expect(video.isLive).to.be.true
121
af4ae64f
C
122 expect(video.nsfw).to.be.false
123 expect(video.waitTranscoding).to.be.false
124 expect(video.name).to.equal('my super live')
125 expect(video.tags).to.deep.equal([ 'tag1', 'tag2' ])
126 expect(video.commentsEnabled).to.be.false
127 expect(video.downloadEnabled).to.be.false
128 expect(video.privacy.id).to.equal(VideoPrivacy.PUBLIC)
129
130 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
131 await testImage(server.url, 'video_short1.webm', video.thumbnailPath)
132
133 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID)
134 const live: LiveVideo = resLive.body
135
136 if (server.url === servers[0].url) {
c655c9ef 137 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
af4ae64f
C
138 expect(live.streamKey).to.not.be.empty
139 } else {
140 expect(live.rtmpUrl).to.be.null
141 expect(live.streamKey).to.be.null
142 }
143
144 expect(live.saveReplay).to.be.true
145 }
146 })
147
148 it('Should have a default preview and thumbnail', async function () {
149 this.timeout(20000)
150
151 const attributes: LiveVideoCreate = {
152 name: 'default live thumbnail',
153 channelId: servers[0].videoChannel.id,
154 privacy: VideoPrivacy.UNLISTED,
155 nsfw: true
156 }
157
158 const res = await createLive(servers[0].url, servers[0].accessToken, attributes)
159 const videoId = res.body.video.uuid
160
161 await waitJobs(servers)
162
163 for (const server of servers) {
164 const resVideo = await getVideo(server.url, videoId)
165 const video: VideoDetails = resVideo.body
166
167 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
168 expect(video.nsfw).to.be.true
169
f2eb23cd
RK
170 await makeRawRequest(server.url + video.thumbnailPath, HttpStatusCode.OK_200)
171 await makeRawRequest(server.url + video.previewPath, HttpStatusCode.OK_200)
af4ae64f
C
172 }
173 })
174
175 it('Should not have the live listed since nobody streams into', async function () {
176 for (const server of servers) {
177 const res = await getVideosList(server.url)
178
179 expect(res.body.total).to.equal(0)
180 expect(res.body.data).to.have.lengthOf(0)
181 }
182 })
183
184 it('Should not be able to update a live of another server', async function () {
f2eb23cd 185 await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, HttpStatusCode.FORBIDDEN_403)
af4ae64f
C
186 })
187
188 it('Should update the live', async function () {
189 this.timeout(10000)
190
191 await updateLive(servers[0].url, servers[0].accessToken, liveVideoUUID, { saveReplay: false })
192 await waitJobs(servers)
193 })
194
195 it('Have the live updated', async function () {
196 for (const server of servers) {
197 const res = await getLive(server.url, server.accessToken, liveVideoUUID)
198 const live: LiveVideo = res.body
199
200 if (server.url === servers[0].url) {
c655c9ef 201 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
af4ae64f
C
202 expect(live.streamKey).to.not.be.empty
203 } else {
204 expect(live.rtmpUrl).to.be.null
205 expect(live.streamKey).to.be.null
206 }
207
208 expect(live.saveReplay).to.be.false
209 }
210 })
211
212 it('Delete the live', async function () {
213 this.timeout(10000)
214
215 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID)
216 await waitJobs(servers)
217 })
218
219 it('Should have the live deleted', async function () {
220 for (const server of servers) {
f2eb23cd
RK
221 await getVideo(server.url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
222 await getLive(server.url, server.accessToken, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
af4ae64f
C
223 }
224 })
225 })
226
1fd61899
C
227 describe('Live filters', function () {
228 let command: any
229 let liveVideoId: string
230 let vodVideoId: string
231
232 before(async function () {
233 this.timeout(120000)
234
235 vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid
236
237 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id }
238 const resLive = await createLive(servers[0].url, servers[0].accessToken, liveOptions)
239 liveVideoId = resLive.body.video.uuid
240
241 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
8ebf2a5d 242 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
1fd61899
C
243 await waitJobs(servers)
244 })
245
246 it('Should only display lives', async function () {
247 const res = await getVideosWithFilters(servers[0].url, { isLive: true })
248
249 expect(res.body.total).to.equal(1)
250 expect(res.body.data).to.have.lengthOf(1)
251 expect(res.body.data[0].name).to.equal('live')
252 })
253
254 it('Should not display lives', async function () {
255 const res = await getVideosWithFilters(servers[0].url, { isLive: false })
256
257 expect(res.body.total).to.equal(1)
258 expect(res.body.data).to.have.lengthOf(1)
259 expect(res.body.data[0].name).to.equal('vod video')
260 })
261
262 it('Should display my lives', async function () {
263 this.timeout(60000)
264
265 await stopFfmpeg(command)
266 await waitJobs(servers)
267
268 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true })
269 const videos = res.body.data as Video[]
270
271 const result = videos.every(v => v.isLive)
272 expect(result).to.be.true
273 })
274
275 it('Should not display my lives', async function () {
276 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: false })
277 const videos = res.body.data as Video[]
278
279 const result = videos.every(v => !v.isLive)
280 expect(result).to.be.true
281 })
282
283 after(async function () {
284 await removeVideo(servers[0].url, servers[0].accessToken, vodVideoId)
285 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoId)
286 })
287 })
288
68e70a74
C
289 describe('Stream checks', function () {
290 let liveVideo: LiveVideo & VideoDetails
291 let rtmpUrl: string
292
293 before(function () {
c655c9ef 294 rtmpUrl = 'rtmp://' + servers[0].hostname + ':' + servers[0].rtmpPort + ''
68e70a74 295 })
af4ae64f 296
68e70a74 297 async function createLiveWrapper () {
97969c4e
C
298 const liveAttributes = {
299 name: 'user live',
bd54ad19 300 channelId: servers[0].videoChannel.id,
97969c4e 301 privacy: VideoPrivacy.PUBLIC,
68e70a74 302 saveReplay: false
97969c4e
C
303 }
304
bd54ad19 305 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
68e70a74 306 const uuid = res.body.video.uuid
97969c4e 307
68e70a74
C
308 const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid)
309 const resVideo = await getVideo(servers[0].url, uuid)
97969c4e 310
68e70a74
C
311 return Object.assign(resVideo.body, resLive.body) as LiveVideo & VideoDetails
312 }
97969c4e 313
68e70a74 314 it('Should not allow a stream without the appropriate path', async function () {
2df6f943 315 this.timeout(60000)
97969c4e 316
68e70a74 317 liveVideo = await createLiveWrapper()
97969c4e 318
68e70a74
C
319 const command = sendRTMPStream(rtmpUrl + '/bad-live', liveVideo.streamKey)
320 await testFfmpegStreamError(command, true)
af4ae64f
C
321 })
322
68e70a74 323 it('Should not allow a stream without the appropriate stream key', async function () {
2df6f943 324 this.timeout(60000)
97969c4e 325
68e70a74
C
326 const command = sendRTMPStream(rtmpUrl + '/live', 'bad-stream-key')
327 await testFfmpegStreamError(command, true)
97969c4e
C
328 })
329
68e70a74 330 it('Should succeed with the correct params', async function () {
2df6f943 331 this.timeout(60000)
97969c4e 332
68e70a74
C
333 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
334 await testFfmpegStreamError(command, false)
af4ae64f
C
335 })
336
1ab60243
C
337 it('Should list this live now someone stream into it', async function () {
338 for (const server of servers) {
339 const res = await getVideosList(server.url)
340
341 expect(res.body.total).to.equal(1)
342 expect(res.body.data).to.have.lengthOf(1)
343
344 const video: Video = res.body.data[0]
345
346 expect(video.name).to.equal('user live')
347 expect(video.isLive).to.be.true
348 }
349 })
350
68e70a74 351 it('Should not allow a stream on a live that was blacklisted', async function () {
2df6f943 352 this.timeout(60000)
97969c4e 353
68e70a74 354 liveVideo = await createLiveWrapper()
af4ae64f 355
68e70a74 356 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideo.uuid)
af4ae64f 357
68e70a74
C
358 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
359 await testFfmpegStreamError(command, true)
af4ae64f
C
360 })
361
68e70a74 362 it('Should not allow a stream on a live that was deleted', async function () {
2df6f943 363 this.timeout(60000)
af4ae64f 364
68e70a74 365 liveVideo = await createLiveWrapper()
af4ae64f 366
68e70a74 367 await removeVideo(servers[0].url, servers[0].accessToken, liveVideo.uuid)
af4ae64f 368
68e70a74
C
369 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
370 await testFfmpegStreamError(command, true)
af4ae64f
C
371 })
372 })
373
374 describe('Live transcoding', function () {
bd54ad19
C
375 let liveVideoId: string
376
377 async function createLiveWrapper (saveReplay: boolean) {
378 const liveAttributes = {
379 name: 'live video',
380 channelId: servers[0].videoChannel.id,
381 privacy: VideoPrivacy.PUBLIC,
382 saveReplay
383 }
384
385 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
386 return res.body.video.uuid
387 }
388
389 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) {
390 for (const server of servers) {
391 const resList = await getVideosList(server.url)
392 const videos: Video[] = resList.body.data
393
394 expect(videos.find(v => v.uuid === liveVideoId)).to.exist
395
396 const resVideo = await getVideo(server.url, liveVideoId)
397 const video: VideoDetails = resVideo.body
398
399 expect(video.streamingPlaylists).to.have.lengthOf(1)
400
401 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
402 expect(hlsPlaylist).to.exist
403
404 // Only finite files are displayed
405 expect(hlsPlaylist.files).to.have.lengthOf(0)
406
407 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions)
5c0904fc
C
408
409 for (let i = 0; i < resolutions.length; i++) {
a800dbf3 410 const segmentNum = 3
0d8de275
C
411 const segmentName = `${i}-00000${segmentNum}.ts`
412 await waitUntilLiveSegmentGeneration(servers[0], video.uuid, i, segmentNum)
5c0904fc
C
413
414 const res = await getPlaylist(`${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`)
415 const subPlaylist = res.text
416
417 expect(subPlaylist).to.contain(segmentName)
418
419 const baseUrlAndPath = servers[0].url + '/static/streaming-playlists/hls'
420 await checkLiveSegmentHash(baseUrlAndPath, video.uuid, segmentName, hlsPlaylist)
421 }
bd54ad19
C
422 }
423 }
424
425 function updateConf (resolutions: number[]) {
65e6e260
C
426 return servers[0].configCommand.updateCustomSubConfig({
427 newConfig: {
428 live: {
bd54ad19 429 enabled: true,
65e6e260
C
430 allowReplay: true,
431 maxDuration: -1,
432 transcoding: {
433 enabled: true,
434 resolutions: {
435 '240p': resolutions.includes(240),
436 '360p': resolutions.includes(360),
437 '480p': resolutions.includes(480),
438 '720p': resolutions.includes(720),
439 '1080p': resolutions.includes(1080),
440 '2160p': resolutions.includes(2160)
441 }
bd54ad19
C
442 }
443 }
444 }
445 })
446 }
447
448 before(async function () {
449 await updateConf([])
450 })
af4ae64f
C
451
452 it('Should enable transcoding without additional resolutions', async function () {
dbb15e37 453 this.timeout(60000)
bd54ad19
C
454
455 liveVideoId = await createLiveWrapper(false)
456
457 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
8ebf2a5d 458 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
bd54ad19 459 await waitJobs(servers)
af4ae64f 460
bd54ad19
C
461 await testVideoResolutions(liveVideoId, [ 720 ])
462
463 await stopFfmpeg(command)
af4ae64f
C
464 })
465
466 it('Should enable transcoding with some resolutions', async function () {
dbb15e37 467 this.timeout(60000)
bd54ad19
C
468
469 const resolutions = [ 240, 480 ]
470 await updateConf(resolutions)
471 liveVideoId = await createLiveWrapper(false)
472
473 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId)
8ebf2a5d 474 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
bd54ad19
C
475 await waitJobs(servers)
476
477 await testVideoResolutions(liveVideoId, resolutions)
478
479 await stopFfmpeg(command)
af4ae64f
C
480 })
481
482 it('Should enable transcoding with some resolutions and correctly save them', async function () {
f42c2152 483 this.timeout(200000)
bd54ad19
C
484
485 const resolutions = [ 240, 360, 720 ]
ca5c612b 486
bd54ad19
C
487 await updateConf(resolutions)
488 liveVideoId = await createLiveWrapper(true)
489
ca5c612b 490 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId, 'video_short2.webm')
8ebf2a5d 491 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
bd54ad19
C
492 await waitJobs(servers)
493
494 await testVideoResolutions(liveVideoId, resolutions)
495
496 await stopFfmpeg(command)
34caef7f
C
497 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId)
498
bd54ad19
C
499 await waitJobs(servers)
500
8ebf2a5d 501 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
e0783718 502
ca5c612b 503 const bitrateLimits = {
4ef9ea48 504 720: 5000 * 1000, // 60FPS
6b67897e
C
505 360: 1100 * 1000,
506 240: 600 * 1000
ca5c612b
C
507 }
508
bd54ad19
C
509 for (const server of servers) {
510 const resVideo = await getVideo(server.url, liveVideoId)
511 const video: VideoDetails = resVideo.body
512
e0783718 513 expect(video.state.id).to.equal(VideoState.PUBLISHED)
bd54ad19
C
514 expect(video.duration).to.be.greaterThan(1)
515 expect(video.files).to.have.lengthOf(0)
516
517 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
f2eb23cd
RK
518 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200)
519 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200)
bd54ad19
C
520
521 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length)
522
523 for (const resolution of resolutions) {
524 const file = hlsPlaylist.files.find(f => f.resolution.id === resolution)
525
526 expect(file).to.exist
bd54ad19
C
527 expect(file.size).to.be.greaterThan(1)
528
884d2c39
C
529 if (resolution >= 720) {
530 expect(file.fps).to.be.approximately(60, 2)
531 } else {
532 expect(file.fps).to.be.approximately(30, 2)
533 }
534
ca5c612b
C
535 const filename = `${video.uuid}-${resolution}-fragmented.mp4`
536 const segmentPath = buildServerDirectory(servers[0], join('streaming-playlists', 'hls', video.uuid, filename))
537
538 const probe = await ffprobePromise(segmentPath)
539 const videoStream = await getVideoStreamFromFile(segmentPath, probe)
6b67897e 540
ca5c612b
C
541 expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height])
542
f2eb23cd
RK
543 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200)
544 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
bd54ad19
C
545 }
546 }
af4ae64f
C
547 })
548
549 it('Should correctly have cleaned up the live files', async function () {
bd54ad19
C
550 this.timeout(30000)
551
552 await checkLiveCleanup(servers[0], liveVideoId, [ 240, 360, 720 ])
af4ae64f
C
553 })
554 })
555
5c0904fc
C
556 describe('After a server restart', function () {
557 let liveVideoId: string
558 let liveVideoReplayId: string
559
560 async function createLiveWrapper (saveReplay: boolean) {
561 const liveAttributes = {
562 name: 'live video',
563 channelId: servers[0].videoChannel.id,
564 privacy: VideoPrivacy.PUBLIC,
565 saveReplay
566 }
567
568 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes)
569 return res.body.video.uuid
570 }
571
572 before(async function () {
0d8de275 573 this.timeout(120000)
5c0904fc
C
574
575 liveVideoId = await createLiveWrapper(false)
576 liveVideoReplayId = await createLiveWrapper(true)
577
578 await Promise.all([
579 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId),
580 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoReplayId)
581 ])
582
583 await Promise.all([
0d8de275
C
584 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId),
585 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId)
5c0904fc
C
586 ])
587
0d8de275
C
588 await waitUntilLiveSegmentGeneration(servers[0], liveVideoId, 0, 2)
589 await waitUntilLiveSegmentGeneration(servers[0], liveVideoReplayId, 0, 2)
590
5c0904fc
C
591 await killallServers([ servers[0] ])
592 await reRunServer(servers[0])
593
594 await wait(5000)
595 })
596
597 it('Should cleanup lives', async function () {
598 this.timeout(60000)
599
0d8de275 600 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId)
5c0904fc
C
601 })
602
603 it('Should save a live replay', async function () {
0d8de275 604 this.timeout(120000)
5c0904fc 605
b49f22d8 606 await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId)
5c0904fc
C
607 })
608 })
609
af4ae64f
C
610 after(async function () {
611 await cleanupTests(servers)
612 })
613})