]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/utils/videos/videos.ts
Only use account name in routes
[github/Chocobozzz/PeerTube.git] / server / tests / utils / videos / videos.ts
1 /* tslint:disable:no-unused-expression */
2
3 import { expect } from 'chai'
4 import { existsSync, readFile } from 'fs'
5 import * as parseTorrent from 'parse-torrent'
6 import { extname, join } from 'path'
7 import * as request from 'supertest'
8 import {
9 buildAbsoluteFixturePath,
10 getMyUserInformation,
11 makeGetRequest,
12 makePutBodyRequest,
13 makeUploadRequest,
14 root,
15 ServerInfo,
16 testImage
17 } from '../'
18 import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
19 import { readdirPromise } from '../../../helpers/core-utils'
20 import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
21 import { dateIsValid, webtorrentAdd } from '../index'
22
23 type VideoAttributes = {
24 name?: string
25 category?: number
26 licence?: number
27 language?: string
28 nsfw?: boolean
29 commentsEnabled?: boolean
30 description?: string
31 tags?: string[]
32 channelId?: number
33 privacy?: VideoPrivacy
34 fixture?: string
35 thumbnailfile?: string
36 previewfile?: string
37 }
38
39 function getVideoCategories (url: string) {
40 const path = '/api/v1/videos/categories'
41
42 return makeGetRequest({
43 url,
44 path,
45 statusCodeExpected: 200
46 })
47 }
48
49 function getVideoLicences (url: string) {
50 const path = '/api/v1/videos/licences'
51
52 return makeGetRequest({
53 url,
54 path,
55 statusCodeExpected: 200
56 })
57 }
58
59 function getVideoLanguages (url: string) {
60 const path = '/api/v1/videos/languages'
61
62 return makeGetRequest({
63 url,
64 path,
65 statusCodeExpected: 200
66 })
67 }
68
69 function getVideoPrivacies (url: string) {
70 const path = '/api/v1/videos/privacies'
71
72 return makeGetRequest({
73 url,
74 path,
75 statusCodeExpected: 200
76 })
77 }
78
79 function getVideo (url: string, id: number | string, expectedStatus = 200) {
80 const path = '/api/v1/videos/' + id
81
82 return request(url)
83 .get(path)
84 .set('Accept', 'application/json')
85 .expect(expectedStatus)
86 }
87
88 function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
89 const path = '/api/v1/videos/' + id + '/views'
90
91 const req = request(url)
92 .post(path)
93 .set('Accept', 'application/json')
94
95 if (xForwardedFor) {
96 req.set('X-Forwarded-For', xForwardedFor)
97 }
98
99 return req.expect(expectedStatus)
100 }
101
102 function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
103 const path = '/api/v1/videos/' + id
104
105 return request(url)
106 .get(path)
107 .set('Authorization', 'Bearer ' + token)
108 .set('Accept', 'application/json')
109 .expect(expectedStatus)
110 }
111
112 function getVideoDescription (url: string, descriptionPath: string) {
113 return request(url)
114 .get(descriptionPath)
115 .set('Accept', 'application/json')
116 .expect(200)
117 .expect('Content-Type', /json/)
118 }
119
120 function getVideosList (url: string) {
121 const path = '/api/v1/videos'
122
123 return request(url)
124 .get(path)
125 .query({ sort: 'name' })
126 .set('Accept', 'application/json')
127 .expect(200)
128 .expect('Content-Type', /json/)
129 }
130
131 function getVideosListWithToken (url: string, token: string) {
132 const path = '/api/v1/videos'
133
134 return request(url)
135 .get(path)
136 .set('Authorization', 'Bearer ' + token)
137 .query({ sort: 'name' })
138 .set('Accept', 'application/json')
139 .expect(200)
140 .expect('Content-Type', /json/)
141 }
142
143 function getLocalVideos (url: string) {
144 const path = '/api/v1/videos'
145
146 return request(url)
147 .get(path)
148 .query({ sort: 'name', filter: 'local' })
149 .set('Accept', 'application/json')
150 .expect(200)
151 .expect('Content-Type', /json/)
152 }
153
154 function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
155 const path = '/api/v1/users/me/videos'
156
157 const req = request(url)
158 .get(path)
159 .query({ start: start })
160 .query({ count: count })
161
162 if (sort) req.query({ sort })
163
164 return req.set('Accept', 'application/json')
165 .set('Authorization', 'Bearer ' + accessToken)
166 .expect(200)
167 .expect('Content-Type', /json/)
168 }
169
170 function getAccountVideos (url: string, accessToken: string, accountName: string, start: number, count: number, sort?: string) {
171 const path = '/api/v1/accounts/' + accountName + '/videos'
172
173 return makeGetRequest({
174 url,
175 path,
176 query: {
177 start,
178 count,
179 sort
180 },
181 token: accessToken,
182 statusCodeExpected: 200
183 })
184 }
185
186 function getVideoChannelVideos (
187 url: string,
188 accessToken: string,
189 videoChannelId: number | string,
190 start: number,
191 count: number,
192 sort?: string
193 ) {
194 const path = '/api/v1/video-channels/' + videoChannelId + '/videos'
195
196 return makeGetRequest({
197 url,
198 path,
199 query: {
200 start,
201 count,
202 sort
203 },
204 token: accessToken,
205 statusCodeExpected: 200
206 })
207 }
208
209 function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
210 const path = '/api/v1/videos'
211
212 const req = request(url)
213 .get(path)
214 .query({ start: start })
215 .query({ count: count })
216
217 if (sort) req.query({ sort })
218
219 return req.set('Accept', 'application/json')
220 .expect(200)
221 .expect('Content-Type', /json/)
222 }
223
224 function getVideosListSort (url: string, sort: string) {
225 const path = '/api/v1/videos'
226
227 return request(url)
228 .get(path)
229 .query({ sort: sort })
230 .set('Accept', 'application/json')
231 .expect(200)
232 .expect('Content-Type', /json/)
233 }
234
235 function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
236 const path = '/api/v1/videos'
237
238 return request(url)
239 .delete(path + '/' + id)
240 .set('Accept', 'application/json')
241 .set('Authorization', 'Bearer ' + token)
242 .expect(expectedStatus)
243 }
244
245 function searchVideo (url: string, search: string) {
246 const path = '/api/v1/videos'
247 const req = request(url)
248 .get(path + '/search')
249 .query({ search })
250 .set('Accept', 'application/json')
251
252 return req.expect(200)
253 .expect('Content-Type', /json/)
254 }
255
256 function searchVideoWithToken (url: string, search: string, token: string) {
257 const path = '/api/v1/videos'
258 const req = request(url)
259 .get(path + '/search')
260 .set('Authorization', 'Bearer ' + token)
261 .query({ search })
262 .set('Accept', 'application/json')
263
264 return req.expect(200)
265 .expect('Content-Type', /json/)
266 }
267
268 function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
269 const path = '/api/v1/videos'
270
271 const req = request(url)
272 .get(path + '/search')
273 .query({ start })
274 .query({ search })
275 .query({ count })
276
277 if (sort) req.query({ sort })
278
279 return req.set('Accept', 'application/json')
280 .expect(200)
281 .expect('Content-Type', /json/)
282 }
283
284 function searchVideoWithSort (url: string, search: string, sort: string) {
285 const path = '/api/v1/videos'
286
287 return request(url)
288 .get(path + '/search')
289 .query({ search })
290 .query({ sort })
291 .set('Accept', 'application/json')
292 .expect(200)
293 .expect('Content-Type', /json/)
294 }
295
296 async function checkVideoFilesWereRemoved (videoUUID: string, serverNumber: number) {
297 const testDirectory = 'test' + serverNumber
298
299 for (const directory of [ 'videos', 'thumbnails', 'torrents', 'previews' ]) {
300 const directoryPath = join(root(), testDirectory, directory)
301
302 const directoryExists = existsSync(directoryPath)
303 expect(directoryExists).to.be.true
304
305 const files = await readdirPromise(directoryPath)
306 for (const file of files) {
307 expect(file).to.not.contain(videoUUID)
308 }
309 }
310 }
311
312 async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
313 const path = '/api/v1/videos/upload'
314 let defaultChannelId = '1'
315
316 try {
317 const res = await getMyUserInformation(url, accessToken)
318 defaultChannelId = res.body.videoChannels[0].id
319 } catch (e) { /* empty */ }
320
321 // Override default attributes
322 const attributes = Object.assign({
323 name: 'my super video',
324 category: 5,
325 licence: 4,
326 language: 'zh',
327 channelId: defaultChannelId,
328 nsfw: true,
329 description: 'my super description',
330 support: 'my super support text',
331 tags: [ 'tag' ],
332 privacy: VideoPrivacy.PUBLIC,
333 commentsEnabled: true,
334 fixture: 'video_short.webm'
335 }, videoAttributesArg)
336
337 const req = request(url)
338 .post(path)
339 .set('Accept', 'application/json')
340 .set('Authorization', 'Bearer ' + accessToken)
341 .field('name', attributes.name)
342 .field('nsfw', JSON.stringify(attributes.nsfw))
343 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
344 .field('privacy', attributes.privacy.toString())
345 .field('channelId', attributes.channelId)
346
347 if (attributes.description !== undefined) {
348 req.field('description', attributes.description)
349 }
350 if (attributes.language !== undefined) {
351 req.field('language', attributes.language.toString())
352 }
353 if (attributes.category !== undefined) {
354 req.field('category', attributes.category.toString())
355 }
356 if (attributes.licence !== undefined) {
357 req.field('licence', attributes.licence.toString())
358 }
359
360 for (let i = 0; i < attributes.tags.length; i++) {
361 req.field('tags[' + i + ']', attributes.tags[i])
362 }
363
364 if (attributes.thumbnailfile !== undefined) {
365 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
366 }
367 if (attributes.previewfile !== undefined) {
368 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
369 }
370
371 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
372 .expect(specialStatus)
373 }
374
375 function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
376 const path = '/api/v1/videos/' + id
377 const body = {}
378
379 if (attributes.name) body['name'] = attributes.name
380 if (attributes.category) body['category'] = attributes.category
381 if (attributes.licence) body['licence'] = attributes.licence
382 if (attributes.language) body['language'] = attributes.language
383 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
384 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
385 if (attributes.description) body['description'] = attributes.description
386 if (attributes.tags) body['tags'] = attributes.tags
387 if (attributes.privacy) body['privacy'] = attributes.privacy
388 if (attributes.channelId) body['channelId'] = attributes.channelId
389
390 // Upload request
391 if (attributes.thumbnailfile || attributes.previewfile) {
392 const attaches: any = {}
393 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
394 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
395
396 return makeUploadRequest({
397 url,
398 method: 'PUT',
399 path,
400 token: accessToken,
401 fields: body,
402 attaches,
403 statusCodeExpected
404 })
405 }
406
407 return makePutBodyRequest({
408 url,
409 path,
410 fields: body,
411 token: accessToken,
412 statusCodeExpected
413 })
414 }
415
416 function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
417 const path = '/api/v1/videos/' + id + '/rate'
418
419 return request(url)
420 .put(path)
421 .set('Accept', 'application/json')
422 .set('Authorization', 'Bearer ' + accessToken)
423 .send({ rating })
424 .expect(specialStatus)
425 }
426
427 function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
428 return new Promise<any>((res, rej) => {
429 const torrentName = videoUUID + '-' + resolution + '.torrent'
430 const torrentPath = join(__dirname, '..', '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
431 readFile(torrentPath, (err, data) => {
432 if (err) return rej(err)
433
434 return res(parseTorrent(data))
435 })
436 })
437 }
438
439 async function completeVideoCheck (
440 url: string,
441 video: any,
442 attributes: {
443 name: string
444 category: number
445 licence: number
446 language: string
447 nsfw: boolean
448 commentsEnabled: boolean
449 description: string
450 support: string
451 account: {
452 name: string
453 host: string
454 }
455 isLocal: boolean,
456 tags: string[],
457 privacy: number,
458 likes?: number,
459 dislikes?: number,
460 duration: number,
461 channel: {
462 name: string,
463 description
464 isLocal: boolean
465 }
466 fixture: string,
467 files: {
468 resolution: number
469 size: number
470 }[],
471 thumbnailfile?: string
472 previewfile?: string
473 }
474 ) {
475 if (!attributes.likes) attributes.likes = 0
476 if (!attributes.dislikes) attributes.dislikes = 0
477
478 expect(video.name).to.equal(attributes.name)
479 expect(video.category.id).to.equal(attributes.category)
480 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
481 expect(video.licence.id).to.equal(attributes.licence)
482 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
483 expect(video.language.id).to.equal(attributes.language)
484 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
485 expect(video.privacy.id).to.deep.equal(attributes.privacy)
486 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
487 expect(video.nsfw).to.equal(attributes.nsfw)
488 expect(video.description).to.equal(attributes.description)
489 expect(video.account.id).to.be.a('number')
490 expect(video.account.uuid).to.be.a('string')
491 expect(video.account.host).to.equal(attributes.account.host)
492 expect(video.account.name).to.equal(attributes.account.name)
493 expect(video.channel.displayName).to.equal(attributes.channel.name)
494 expect(video.channel.name).to.have.lengthOf(36)
495 expect(video.likes).to.equal(attributes.likes)
496 expect(video.dislikes).to.equal(attributes.dislikes)
497 expect(video.isLocal).to.equal(attributes.isLocal)
498 expect(video.duration).to.equal(attributes.duration)
499 expect(dateIsValid(video.createdAt)).to.be.true
500 expect(dateIsValid(video.publishedAt)).to.be.true
501 expect(dateIsValid(video.updatedAt)).to.be.true
502
503 const res = await getVideo(url, video.uuid)
504 const videoDetails: VideoDetails = res.body
505
506 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
507 expect(videoDetails.tags).to.deep.equal(attributes.tags)
508 expect(videoDetails.account.name).to.equal(attributes.account.name)
509 expect(videoDetails.account.host).to.equal(attributes.account.host)
510 expect(videoDetails.channel.displayName).to.equal(attributes.channel.name)
511 expect(videoDetails.channel.name).to.have.lengthOf(36)
512 expect(videoDetails.channel.host).to.equal(attributes.account.host)
513 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
514 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
515 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
516 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
517
518 for (const attributeFile of attributes.files) {
519 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
520 expect(file).not.to.be.undefined
521
522 let extension = extname(attributes.fixture)
523 // Transcoding enabled on server 2, extension will always be .mp4
524 if (attributes.account.host === 'localhost:9002') extension = '.mp4'
525
526 const magnetUri = file.magnetUri
527 expect(file.magnetUri).to.have.lengthOf.above(2)
528 expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
529 expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
530 expect(file.resolution.id).to.equal(attributeFile.resolution)
531 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
532
533 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
534 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
535 expect(file.size).to.be.above(minSize).and.below(maxSize)
536
537 {
538 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
539 }
540
541 if (attributes.previewfile) {
542 await testImage(url, attributes.previewfile, videoDetails.previewPath)
543 }
544
545 const torrent = await webtorrentAdd(magnetUri, true)
546 expect(torrent.files).to.be.an('array')
547 expect(torrent.files.length).to.equal(1)
548 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
549 }
550 }
551
552 // ---------------------------------------------------------------------------
553
554 export {
555 getVideoDescription,
556 getVideoCategories,
557 getVideoLicences,
558 getVideoPrivacies,
559 getVideoLanguages,
560 getMyVideos,
561 getAccountVideos,
562 getVideoChannelVideos,
563 searchVideoWithToken,
564 getVideo,
565 getVideoWithToken,
566 getVideosList,
567 getVideosListPagination,
568 getVideosListSort,
569 removeVideo,
570 searchVideo,
571 searchVideoWithPagination,
572 searchVideoWithSort,
573 getVideosListWithToken,
574 uploadVideo,
575 updateVideo,
576 rateVideo,
577 viewVideo,
578 parseTorrentVideo,
579 getLocalVideos,
580 completeVideoCheck,
581 checkVideoFilesWereRemoved
582 }