live.ts 14.3 KB
Newer Older
Chocobozzz's avatar
Chocobozzz committed
1
2
3
4
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */

import 'mocha'
import { omit } from 'lodash'
Chocobozzz's avatar
Chocobozzz committed
5
import { HttpStatusCode } from '@shared/core-utils'
Chocobozzz's avatar
Chocobozzz committed
6
import {
Chocobozzz's avatar
Chocobozzz committed
7
  buildAbsoluteFixturePath,
Chocobozzz's avatar
Chocobozzz committed
8
  cleanupTests,
9
  createSingleServer,
Chocobozzz's avatar
Chocobozzz committed
10
  LiveCommand,
Chocobozzz's avatar
Chocobozzz committed
11
12
13
  makePostBodyRequest,
  makeUploadRequest,
  sendRTMPStream,
14
  PeerTubeServer,
Chocobozzz's avatar
Chocobozzz committed
15
  setAccessTokensToServers,
Chocobozzz's avatar
Chocobozzz committed
16
  stopFfmpeg
Chocobozzz's avatar
Chocobozzz committed
17
18
} from '@shared/extra-utils'
import { VideoCreateResult, VideoPrivacy } from '@shared/models'
Chocobozzz's avatar
Chocobozzz committed
19
20
21

describe('Test video lives API validator', function () {
  const path = '/api/v1/videos/live'
22
  let server: PeerTubeServer
Chocobozzz's avatar
Chocobozzz committed
23
24
  let userAccessToken = ''
  let channelId: number
25
  let video: VideoCreateResult
Chocobozzz's avatar
Chocobozzz committed
26
  let videoIdNotLive: number
Chocobozzz's avatar
Chocobozzz committed
27
  let command: LiveCommand
Chocobozzz's avatar
Chocobozzz committed
28
29
30
31
32
33

  // ---------------------------------------------------------------

  before(async function () {
    this.timeout(30000)

34
    server = await createSingleServer(1)
Chocobozzz's avatar
Chocobozzz committed
35
36
37

    await setAccessTokensToServers([ server ])

Chocobozzz's avatar
Chocobozzz committed
38
    await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
39
40
41
42
43
44
45
      newConfig: {
        live: {
          enabled: true,
          maxInstanceLives: 20,
          maxUserLives: 20,
          allowReplay: true
        }
Chocobozzz's avatar
Chocobozzz committed
46
47
48
49
50
      }
    })

    const username = 'user1'
    const password = 'my super password'
Chocobozzz's avatar
Chocobozzz committed
51
52
    await server.users.create({ username: username, password: password })
    userAccessToken = await server.login.getAccessToken({ username, password })
Chocobozzz's avatar
Chocobozzz committed
53
54

    {
Chocobozzz's avatar
Chocobozzz committed
55
      const { videoChannels } = await server.users.getMyInfo()
Chocobozzz's avatar
Chocobozzz committed
56
      channelId = videoChannels[0].id
Chocobozzz's avatar
Chocobozzz committed
57
58
59
    }

    {
Chocobozzz's avatar
Chocobozzz committed
60
      videoIdNotLive = (await server.videos.quickUpload({ name: 'not live' })).id
Chocobozzz's avatar
Chocobozzz committed
61
    }
Chocobozzz's avatar
Chocobozzz committed
62

Chocobozzz's avatar
Chocobozzz committed
63
    command = server.live
Chocobozzz's avatar
Chocobozzz committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  })

  describe('When creating a live', function () {
    let baseCorrectParams

    before(function () {
      baseCorrectParams = {
        name: 'my super name',
        category: 5,
        licence: 1,
        language: 'pt',
        nsfw: false,
        commentsEnabled: true,
        downloadEnabled: true,
        waitTranscoding: true,
        description: 'my super description',
        support: 'my super support text',
        tags: [ 'tag1', 'tag2' ],
        privacy: VideoPrivacy.PUBLIC,
        channelId,
Chocobozzz's avatar
Chocobozzz committed
84
85
        saveReplay: false,
        permanentLive: false
Chocobozzz's avatar
Chocobozzz committed
86
87
88
89
90
91
92
93
94
      }
    })

    it('Should fail with nothing', async function () {
      const fields = {}
      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a long name', async function () {
Chocobozzz's avatar
Chocobozzz committed
95
      const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
Chocobozzz's avatar
Chocobozzz committed
96
97
98
99
100

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a bad category', async function () {
Chocobozzz's avatar
Chocobozzz committed
101
      const fields = { ...baseCorrectParams, category: 125 }
Chocobozzz's avatar
Chocobozzz committed
102
103
104
105
106

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a bad licence', async function () {
Chocobozzz's avatar
Chocobozzz committed
107
      const fields = { ...baseCorrectParams, licence: 125 }
Chocobozzz's avatar
Chocobozzz committed
108
109
110
111
112

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a bad language', async function () {
Chocobozzz's avatar
Chocobozzz committed
113
      const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
Chocobozzz's avatar
Chocobozzz committed
114
115
116
117
118

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a long description', async function () {
Chocobozzz's avatar
Chocobozzz committed
119
      const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
Chocobozzz's avatar
Chocobozzz committed
120
121
122
123
124

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a long support text', async function () {
Chocobozzz's avatar
Chocobozzz committed
125
      const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
Chocobozzz's avatar
Chocobozzz committed
126
127
128
129
130
131
132
133
134
135
136

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail without a channel', async function () {
      const fields = omit(baseCorrectParams, 'channelId')

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a bad channel', async function () {
Chocobozzz's avatar
Chocobozzz committed
137
      const fields = { ...baseCorrectParams, channelId: 545454 }
Chocobozzz's avatar
Chocobozzz committed
138
139
140
141
142
143
144
145
146

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with another user channel', async function () {
      const user = {
        username: 'fake',
        password: 'fake_password'
      }
Chocobozzz's avatar
Chocobozzz committed
147
      await server.users.create({ username: user.username, password: user.password })
Chocobozzz's avatar
Chocobozzz committed
148

Chocobozzz's avatar
Chocobozzz committed
149
150
      const accessTokenUser = await server.login.getAccessToken(user)
      const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
Chocobozzz's avatar
Chocobozzz committed
151
      const customChannelId = videoChannels[0].id
Chocobozzz's avatar
Chocobozzz committed
152

Chocobozzz's avatar
Chocobozzz committed
153
      const fields = { ...baseCorrectParams, channelId: customChannelId }
Chocobozzz's avatar
Chocobozzz committed
154
155
156
157
158

      await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
    })

    it('Should fail with too many tags', async function () {
Chocobozzz's avatar
Chocobozzz committed
159
      const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
Chocobozzz's avatar
Chocobozzz committed
160
161
162
163
164

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a tag length too low', async function () {
Chocobozzz's avatar
Chocobozzz committed
165
      const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
Chocobozzz's avatar
Chocobozzz committed
166
167
168
169
170

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with a tag length too big', async function () {
Chocobozzz's avatar
Chocobozzz committed
171
      const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
Chocobozzz's avatar
Chocobozzz committed
172
173
174
175
176
177
178

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

    it('Should fail with an incorrect thumbnail file', async function () {
      const fields = baseCorrectParams
      const attaches = {
Chocobozzz's avatar
Chocobozzz committed
179
        thumbnailfile: buildAbsoluteFixturePath('video_short.mp4')
Chocobozzz's avatar
Chocobozzz committed
180
181
182
183
184
185
186
187
      }

      await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
    })

    it('Should fail with a big thumbnail file', async function () {
      const fields = baseCorrectParams
      const attaches = {
Chocobozzz's avatar
Chocobozzz committed
188
        thumbnailfile: buildAbsoluteFixturePath('preview-big.png')
Chocobozzz's avatar
Chocobozzz committed
189
190
191
192
193
194
195
196
      }

      await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
    })

    it('Should fail with an incorrect preview file', async function () {
      const fields = baseCorrectParams
      const attaches = {
Chocobozzz's avatar
Chocobozzz committed
197
        previewfile: buildAbsoluteFixturePath('video_short.mp4')
Chocobozzz's avatar
Chocobozzz committed
198
199
200
201
202
203
204
205
      }

      await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
    })

    it('Should fail with a big preview file', async function () {
      const fields = baseCorrectParams
      const attaches = {
Chocobozzz's avatar
Chocobozzz committed
206
        previewfile: buildAbsoluteFixturePath('preview-big.png')
Chocobozzz's avatar
Chocobozzz committed
207
208
209
210
211
      }

      await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
    })

Chocobozzz's avatar
Chocobozzz committed
212
    it('Should fail with save replay and permanent live set to true', async function () {
Chocobozzz's avatar
Chocobozzz committed
213
      const fields = { ...baseCorrectParams, saveReplay: true, permanentLive: true }
Chocobozzz's avatar
Chocobozzz committed
214
215
216
217

      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
    })

Chocobozzz's avatar
Chocobozzz committed
218
219
220
221
222
223
224
225
    it('Should succeed with the correct parameters', async function () {
      this.timeout(30000)

      const res = await makePostBodyRequest({
        url: server.url,
        path,
        token: server.accessToken,
        fields: baseCorrectParams,
226
        statusCodeExpected: HttpStatusCode.OK_200
Chocobozzz's avatar
Chocobozzz committed
227
228
      })

229
      video = res.body.video
Chocobozzz's avatar
Chocobozzz committed
230
231
232
    })

    it('Should forbid if live is disabled', async function () {
Chocobozzz's avatar
Chocobozzz committed
233
      await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
234
235
236
237
        newConfig: {
          live: {
            enabled: false
          }
Chocobozzz's avatar
Chocobozzz committed
238
239
240
241
242
243
244
245
        }
      })

      await makePostBodyRequest({
        url: server.url,
        path,
        token: server.accessToken,
        fields: baseCorrectParams,
246
        statusCodeExpected: HttpStatusCode.FORBIDDEN_403
Chocobozzz's avatar
Chocobozzz committed
247
248
249
250
      })
    })

    it('Should forbid to save replay if not enabled by the admin', async function () {
Chocobozzz's avatar
Chocobozzz committed
251
      const fields = { ...baseCorrectParams, saveReplay: true }
Chocobozzz's avatar
Chocobozzz committed
252

Chocobozzz's avatar
Chocobozzz committed
253
      await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
254
255
256
257
258
        newConfig: {
          live: {
            enabled: true,
            allowReplay: false
          }
Chocobozzz's avatar
Chocobozzz committed
259
260
261
262
263
264
265
266
        }
      })

      await makePostBodyRequest({
        url: server.url,
        path,
        token: server.accessToken,
        fields,
267
        statusCodeExpected: HttpStatusCode.FORBIDDEN_403
Chocobozzz's avatar
Chocobozzz committed
268
269
270
271
      })
    })

    it('Should allow to save replay if enabled by the admin', async function () {
Chocobozzz's avatar
Chocobozzz committed
272
      const fields = { ...baseCorrectParams, saveReplay: true }
Chocobozzz's avatar
Chocobozzz committed
273

Chocobozzz's avatar
Chocobozzz committed
274
      await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
275
276
277
278
279
        newConfig: {
          live: {
            enabled: true,
            allowReplay: true
          }
Chocobozzz's avatar
Chocobozzz committed
280
281
282
283
284
285
286
287
        }
      })

      await makePostBodyRequest({
        url: server.url,
        path,
        token: server.accessToken,
        fields,
288
        statusCodeExpected: HttpStatusCode.OK_200
Chocobozzz's avatar
Chocobozzz committed
289
290
291
292
      })
    })

    it('Should not allow live if max instance lives is reached', async function () {
Chocobozzz's avatar
Chocobozzz committed
293
      await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
294
295
296
297
298
        newConfig: {
          live: {
            enabled: true,
            maxInstanceLives: 1
          }
Chocobozzz's avatar
Chocobozzz committed
299
300
301
302
303
304
305
306
        }
      })

      await makePostBodyRequest({
        url: server.url,
        path,
        token: server.accessToken,
        fields: baseCorrectParams,
307
        statusCodeExpected: HttpStatusCode.FORBIDDEN_403
Chocobozzz's avatar
Chocobozzz committed
308
309
310
311
      })
    })

    it('Should not allow live if max user lives is reached', async function () {
Chocobozzz's avatar
Chocobozzz committed
312
      await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
313
314
315
316
317
318
        newConfig: {
          live: {
            enabled: true,
            maxInstanceLives: 20,
            maxUserLives: 1
          }
Chocobozzz's avatar
Chocobozzz committed
319
320
321
322
323
324
325
326
        }
      })

      await makePostBodyRequest({
        url: server.url,
        path,
        token: server.accessToken,
        fields: baseCorrectParams,
327
        statusCodeExpected: HttpStatusCode.FORBIDDEN_403
Chocobozzz's avatar
Chocobozzz committed
328
329
330
331
332
333
334
      })
    })
  })

  describe('When getting live information', function () {

    it('Should fail without access token', async function () {
Chocobozzz's avatar
Chocobozzz committed
335
      await command.get({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
Chocobozzz's avatar
Chocobozzz committed
336
337
338
    })

    it('Should fail with a bad access token', async function () {
Chocobozzz's avatar
Chocobozzz committed
339
      await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
Chocobozzz's avatar
Chocobozzz committed
340
341
342
    })

    it('Should fail with access token of another user', async function () {
Chocobozzz's avatar
Chocobozzz committed
343
      await command.get({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
Chocobozzz's avatar
Chocobozzz committed
344
345
346
    })

    it('Should fail with a bad video id', async function () {
Chocobozzz's avatar
Chocobozzz committed
347
      await command.get({ videoId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
Chocobozzz's avatar
Chocobozzz committed
348
349
350
    })

    it('Should fail with an unknown video id', async function () {
Chocobozzz's avatar
Chocobozzz committed
351
      await command.get({ videoId: 454555, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
Chocobozzz's avatar
Chocobozzz committed
352
353
354
    })

    it('Should fail with a non live video', async function () {
Chocobozzz's avatar
Chocobozzz committed
355
      await command.get({ videoId: videoIdNotLive, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
Chocobozzz's avatar
Chocobozzz committed
356
357
358
    })

    it('Should succeed with the correct params', async function () {
Chocobozzz's avatar
Chocobozzz committed
359
360
361
      await command.get({ videoId: video.id })
      await command.get({ videoId: video.uuid })
      await command.get({ videoId: video.shortUUID })
Chocobozzz's avatar
Chocobozzz committed
362
363
364
365
366
367
    })
  })

  describe('When updating live information', async function () {

    it('Should fail without access token', async function () {
Chocobozzz's avatar
Chocobozzz committed
368
      await command.update({ token: '', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
Chocobozzz's avatar
Chocobozzz committed
369
370
371
    })

    it('Should fail with a bad access token', async function () {
Chocobozzz's avatar
Chocobozzz committed
372
      await command.update({ token: 'toto', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
Chocobozzz's avatar
Chocobozzz committed
373
374
375
    })

    it('Should fail with access token of another user', async function () {
Chocobozzz's avatar
Chocobozzz committed
376
      await command.update({ token: userAccessToken, videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
Chocobozzz's avatar
Chocobozzz committed
377
378
379
    })

    it('Should fail with a bad video id', async function () {
Chocobozzz's avatar
Chocobozzz committed
380
      await command.update({ videoId: 'toto', fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
Chocobozzz's avatar
Chocobozzz committed
381
382
383
    })

    it('Should fail with an unknown video id', async function () {
Chocobozzz's avatar
Chocobozzz committed
384
      await command.update({ videoId: 454555, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
Chocobozzz's avatar
Chocobozzz committed
385
386
387
    })

    it('Should fail with a non live video', async function () {
Chocobozzz's avatar
Chocobozzz committed
388
      await command.update({ videoId: videoIdNotLive, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
Chocobozzz's avatar
Chocobozzz committed
389
390
    })

Chocobozzz's avatar
Chocobozzz committed
391
392
393
    it('Should fail with save replay and permanent live set to true', async function () {
      const fields = { saveReplay: true, permanentLive: true }

Chocobozzz's avatar
Chocobozzz committed
394
      await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
Chocobozzz's avatar
Chocobozzz committed
395
396
    })

Chocobozzz's avatar
Chocobozzz committed
397
    it('Should succeed with the correct params', async function () {
Chocobozzz's avatar
Chocobozzz committed
398
399
400
      await command.update({ videoId: video.id, fields: { saveReplay: false } })
      await command.update({ videoId: video.uuid, fields: { saveReplay: false } })
      await command.update({ videoId: video.shortUUID, fields: { saveReplay: false } })
Chocobozzz's avatar
Chocobozzz committed
401
402
403
    })

    it('Should fail to update replay status if replay is not allowed on the instance', async function () {
Chocobozzz's avatar
Chocobozzz committed
404
      await server.config.updateCustomSubConfig({
Chocobozzz's avatar
Chocobozzz committed
405
406
407
408
409
        newConfig: {
          live: {
            enabled: true,
            allowReplay: false
          }
Chocobozzz's avatar
Chocobozzz committed
410
411
412
        }
      })

Chocobozzz's avatar
Chocobozzz committed
413
      await command.update({ videoId: video.id, fields: { saveReplay: true }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
Chocobozzz's avatar
Chocobozzz committed
414
415
416
    })

    it('Should fail to update a live if it has already started', async function () {
Chocobozzz's avatar
Chocobozzz committed
417
      this.timeout(40000)
Chocobozzz's avatar
Chocobozzz committed
418

Chocobozzz's avatar
Chocobozzz committed
419
      const live = await command.get({ videoId: video.id })
Chocobozzz's avatar
Chocobozzz committed
420

Chocobozzz's avatar
Chocobozzz committed
421
      const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
Chocobozzz's avatar
Chocobozzz committed
422

Chocobozzz's avatar
Chocobozzz committed
423
424
      await command.waitUntilPublished({ videoId: video.id })
      await command.update({ videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
Chocobozzz's avatar
Chocobozzz committed
425

Chocobozzz's avatar
Chocobozzz committed
426
      await stopFfmpeg(ffmpegCommand)
Chocobozzz's avatar
Chocobozzz committed
427
    })
Chocobozzz's avatar
Chocobozzz committed
428
429

    it('Should fail to stream twice in the save live', async function () {
Chocobozzz's avatar
Chocobozzz committed
430
      this.timeout(40000)
Chocobozzz's avatar
Chocobozzz committed
431

Chocobozzz's avatar
Chocobozzz committed
432
      const live = await command.get({ videoId: video.id })
Chocobozzz's avatar
Chocobozzz committed
433

Chocobozzz's avatar
Chocobozzz committed
434
      const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
Chocobozzz's avatar
Chocobozzz committed
435

Chocobozzz's avatar
Chocobozzz committed
436
      await command.waitUntilPublished({ videoId: video.id })
Chocobozzz's avatar
Chocobozzz committed
437

Chocobozzz's avatar
Chocobozzz committed
438
      await command.runAndTestStreamError({ videoId: video.id, shouldHaveError: true })
Chocobozzz's avatar
Chocobozzz committed
439

Chocobozzz's avatar
Chocobozzz committed
440
      await stopFfmpeg(ffmpegCommand)
Chocobozzz's avatar
Chocobozzz committed
441
    })
Chocobozzz's avatar
Chocobozzz committed
442
443
444
445
446
447
  })

  after(async function () {
    await cleanupTests([ server ])
  })
})