]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/utils/videos/videos.ts
Fix tests
[github/Chocobozzz/PeerTube.git] / server / tests / utils / videos / videos.ts
1 /* tslint:disable:no-unused-expression */
2
3 import { expect } from 'chai'
4 import { readFile } from 'fs'
5 import * as parseTorrent from 'parse-torrent'
6 import { extname, isAbsolute, join } from 'path'
7 import * as request from 'supertest'
8 import { getMyUserInformation, makeGetRequest, readFilePromise, ServerInfo } from '../'
9 import { VideoPrivacy } from '../../../../shared/models/videos'
10 import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
11 import { dateIsValid, webtorrentAdd } from '../index'
12
13 type VideoAttributes = {
14 name?: string
15 category?: number
16 licence?: number
17 language?: number
18 nsfw?: boolean
19 description?: string
20 tags?: string[]
21 channelId?: number
22 privacy?: VideoPrivacy
23 fixture?: string
24 }
25
26 function getVideoCategories (url: string) {
27 const path = '/api/v1/videos/categories'
28
29 return makeGetRequest({
30 url,
31 path,
32 statusCodeExpected: 200
33 })
34 }
35
36 function getVideoLicences (url: string) {
37 const path = '/api/v1/videos/licences'
38
39 return makeGetRequest({
40 url,
41 path,
42 statusCodeExpected: 200
43 })
44 }
45
46 function getVideoLanguages (url: string) {
47 const path = '/api/v1/videos/languages'
48
49 return makeGetRequest({
50 url,
51 path,
52 statusCodeExpected: 200
53 })
54 }
55
56 function getVideoPrivacies (url: string) {
57 const path = '/api/v1/videos/privacies'
58
59 return makeGetRequest({
60 url,
61 path,
62 statusCodeExpected: 200
63 })
64 }
65
66 function getVideo (url: string, id: number | string, expectedStatus = 200) {
67 const path = '/api/v1/videos/' + id
68
69 return request(url)
70 .get(path)
71 .set('Accept', 'application/json')
72 .expect(expectedStatus)
73 }
74
75 function viewVideo (url: string, id: number | string, expectedStatus = 204) {
76 const path = '/api/v1/videos/' + id + '/views'
77
78 return request(url)
79 .post(path)
80 .set('Accept', 'application/json')
81 .expect(expectedStatus)
82 }
83
84 function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
85 const path = '/api/v1/videos/' + id
86
87 return request(url)
88 .get(path)
89 .set('Authorization', 'Bearer ' + token)
90 .set('Accept', 'application/json')
91 .expect(expectedStatus)
92 }
93
94 function getVideoDescription (url: string, descriptionPath: string) {
95 return request(url)
96 .get(descriptionPath)
97 .set('Accept', 'application/json')
98 .expect(200)
99 .expect('Content-Type', /json/)
100 }
101
102 function getVideosList (url: string) {
103 const path = '/api/v1/videos'
104
105 return request(url)
106 .get(path)
107 .query({ sort: 'name' })
108 .set('Accept', 'application/json')
109 .expect(200)
110 .expect('Content-Type', /json/)
111 }
112
113 function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
114 const path = '/api/v1/users/me/videos'
115
116 const req = request(url)
117 .get(path)
118 .query({ start: start })
119 .query({ count: count })
120
121 if (sort) req.query({ sort })
122
123 return req.set('Accept', 'application/json')
124 .set('Authorization', 'Bearer ' + accessToken)
125 .expect(200)
126 .expect('Content-Type', /json/)
127 }
128
129 function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
130 const path = '/api/v1/videos'
131
132 const req = request(url)
133 .get(path)
134 .query({ start: start })
135 .query({ count: count })
136
137 if (sort) req.query({ sort })
138
139 return req.set('Accept', 'application/json')
140 .expect(200)
141 .expect('Content-Type', /json/)
142 }
143
144 function getVideosListSort (url: string, sort: string) {
145 const path = '/api/v1/videos'
146
147 return request(url)
148 .get(path)
149 .query({ sort: sort })
150 .set('Accept', 'application/json')
151 .expect(200)
152 .expect('Content-Type', /json/)
153 }
154
155 function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
156 const path = '/api/v1/videos'
157
158 return request(url)
159 .delete(path + '/' + id)
160 .set('Accept', 'application/json')
161 .set('Authorization', 'Bearer ' + token)
162 .expect(expectedStatus)
163 }
164
165 function searchVideo (url: string, search: string) {
166 const path = '/api/v1/videos'
167 const req = request(url)
168 .get(path + '/search')
169 .query({ search })
170 .set('Accept', 'application/json')
171
172 return req.expect(200)
173 .expect('Content-Type', /json/)
174 }
175
176 function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
177 const path = '/api/v1/videos'
178
179 const req = request(url)
180 .get(path + '/search')
181 .query({ start })
182 .query({ search })
183 .query({ count })
184
185 if (sort) req.query({ sort })
186
187 return req.set('Accept', 'application/json')
188 .expect(200)
189 .expect('Content-Type', /json/)
190 }
191
192 function searchVideoWithSort (url: string, search: string, sort: string) {
193 const path = '/api/v1/videos'
194
195 return request(url)
196 .get(path + '/search')
197 .query({ search })
198 .query({ sort })
199 .set('Accept', 'application/json')
200 .expect(200)
201 .expect('Content-Type', /json/)
202 }
203
204 async function testVideoImage (url: string, imageName: string, imagePath: string) {
205 // Don't test images if the node env is not set
206 // Because we need a special ffmpeg version for this test
207 if (process.env['NODE_TEST_IMAGE']) {
208 const res = await request(url)
209 .get(imagePath)
210 .expect(200)
211
212 const data = await readFilePromise(join(__dirname, '..', '..', 'api', 'fixtures', imageName + '.jpg'))
213
214 return data.equals(res.body)
215 } else {
216 console.log('Do not test images. Enable it by setting NODE_TEST_IMAGE env variable.')
217 return true
218 }
219 }
220
221 async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
222 const path = '/api/v1/videos/upload'
223 let defaultChannelId = '1'
224
225 try {
226 const res = await getMyUserInformation(url, accessToken)
227 defaultChannelId = res.body.videoChannels[0].id
228 } catch (e) { /* empty */ }
229
230 // Default attributes
231 let attributes = {
232 name: 'my super video',
233 category: 5,
234 licence: 4,
235 language: 3,
236 channelId: defaultChannelId,
237 nsfw: true,
238 description: 'my super description',
239 tags: [ 'tag' ],
240 privacy: VideoPrivacy.PUBLIC,
241 fixture: 'video_short.webm'
242 }
243 attributes = Object.assign(attributes, videoAttributesArg)
244
245 const req = request(url)
246 .post(path)
247 .set('Accept', 'application/json')
248 .set('Authorization', 'Bearer ' + accessToken)
249 .field('name', attributes.name)
250 .field('category', attributes.category.toString())
251 .field('licence', attributes.licence.toString())
252 .field('nsfw', JSON.stringify(attributes.nsfw))
253 .field('description', attributes.description)
254 .field('privacy', attributes.privacy.toString())
255 .field('channelId', attributes.channelId)
256
257 if (attributes.language !== undefined) {
258 req.field('language', attributes.language.toString())
259 }
260
261 for (let i = 0; i < attributes.tags.length; i++) {
262 req.field('tags[' + i + ']', attributes.tags[i])
263 }
264
265 let filePath = ''
266 if (isAbsolute(attributes.fixture)) {
267 filePath = attributes.fixture
268 } else {
269 filePath = join(__dirname, '..', '..', 'api', 'fixtures', attributes.fixture)
270 }
271
272 return req.attach('videofile', filePath)
273 .expect(specialStatus)
274 }
275
276 function updateVideo (url: string, accessToken: string, id: number, attributes: VideoAttributes, specialStatus = 204) {
277 const path = '/api/v1/videos/' + id
278 const body = {}
279
280 if (attributes.name) body['name'] = attributes.name
281 if (attributes.category) body['category'] = attributes.category
282 if (attributes.licence) body['licence'] = attributes.licence
283 if (attributes.language) body['language'] = attributes.language
284 if (attributes.nsfw) body['nsfw'] = attributes.nsfw
285 if (attributes.description) body['description'] = attributes.description
286 if (attributes.tags) body['tags'] = attributes.tags
287 if (attributes.privacy) body['privacy'] = attributes.privacy
288
289 return request(url)
290 .put(path)
291 .send(body)
292 .set('Accept', 'application/json')
293 .set('Authorization', 'Bearer ' + accessToken)
294 .expect(specialStatus)
295 }
296
297 function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
298 const path = '/api/v1/videos/' + id + '/rate'
299
300 return request(url)
301 .put(path)
302 .set('Accept', 'application/json')
303 .set('Authorization', 'Bearer ' + accessToken)
304 .send({ rating })
305 .expect(specialStatus)
306 }
307
308 function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
309 return new Promise<any>((res, rej) => {
310 const torrentName = videoUUID + '-' + resolution + '.torrent'
311 const torrentPath = join(__dirname, '..', '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
312 readFile(torrentPath, (err, data) => {
313 if (err) return rej(err)
314
315 return res(parseTorrent(data))
316 })
317 })
318 }
319
320 async function completeVideoCheck (
321 url: string,
322 video: any,
323 attributes: {
324 name: string
325 category: number
326 licence: number
327 language: number
328 nsfw: boolean
329 description: string
330 host: string
331 account: string
332 isLocal: boolean,
333 tags: string[],
334 privacy: number,
335 likes?: number,
336 dislikes?: number,
337 duration: number,
338 channel: {
339 name: string,
340 description
341 isLocal: boolean
342 }
343 fixture: string,
344 files: {
345 resolution: number
346 size: number
347 }[]
348 }
349 ) {
350 if (!attributes.likes) attributes.likes = 0
351 if (!attributes.dislikes) attributes.dislikes = 0
352
353 expect(video.name).to.equal(attributes.name)
354 expect(video.category).to.equal(attributes.category)
355 expect(video.categoryLabel).to.equal(VIDEO_CATEGORIES[attributes.category] || 'Misc')
356 expect(video.licence).to.equal(attributes.licence)
357 expect(video.licenceLabel).to.equal(VIDEO_LICENCES[attributes.licence] || 'Unknown')
358 expect(video.language).to.equal(attributes.language)
359 expect(video.languageLabel).to.equal(VIDEO_LANGUAGES[attributes.language] || 'Unknown')
360 expect(video.nsfw).to.equal(attributes.nsfw)
361 expect(video.description).to.equal(attributes.description)
362 expect(video.serverHost).to.equal(attributes.host)
363 expect(video.accountName).to.equal(attributes.account)
364 expect(video.likes).to.equal(attributes.likes)
365 expect(video.dislikes).to.equal(attributes.dislikes)
366 expect(video.isLocal).to.equal(attributes.isLocal)
367 expect(video.duration).to.equal(attributes.duration)
368 expect(dateIsValid(video.createdAt)).to.be.true
369 expect(dateIsValid(video.updatedAt)).to.be.true
370
371 const res = await getVideo(url, video.id)
372 const videoDetails = res.body
373
374 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
375 expect(videoDetails.tags).to.deep.equal(attributes.tags)
376 expect(videoDetails.privacy).to.deep.equal(attributes.privacy)
377 expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
378 expect(videoDetails.account.name).to.equal(attributes.account)
379
380 expect(videoDetails.channel.name).to.equal(attributes.channel.name)
381 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
382 expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
383 expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
384
385 for (const attributeFile of attributes.files) {
386 const file = videoDetails.files.find(f => f.resolution === attributeFile.resolution)
387 expect(file).not.to.be.undefined
388
389 let extension = extname(attributes.fixture)
390 // Transcoding enabled on server 2, extension will always be .mp4
391 if (attributes.host === 'localhost:9002') extension = '.mp4'
392
393 const magnetUri = file.magnetUri
394 expect(file.magnetUri).to.have.lengthOf.above(2)
395 expect(file.torrentUrl).to.equal(`http://${attributes.host}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
396 expect(file.fileUrl).to.equal(`http://${attributes.host}/static/webseed/${videoDetails.uuid}-${file.resolution}${extension}`)
397 expect(file.resolution).to.equal(attributeFile.resolution)
398 expect(file.resolutionLabel).to.equal(attributeFile.resolution + 'p')
399
400 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
401 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
402 expect(file.size).to.be.above(minSize).and.below(maxSize)
403
404 const test = await testVideoImage(url, attributes.fixture, videoDetails.thumbnailPath)
405 expect(test).to.equal(true)
406
407 const torrent = await webtorrentAdd(magnetUri, true)
408 expect(torrent.files).to.be.an('array')
409 expect(torrent.files.length).to.equal(1)
410 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
411 }
412 }
413
414 // ---------------------------------------------------------------------------
415
416 export {
417 getVideoDescription,
418 getVideoCategories,
419 getVideoLicences,
420 getVideoPrivacies,
421 getVideoLanguages,
422 getMyVideos,
423 getVideo,
424 getVideoWithToken,
425 getVideosList,
426 getVideosListPagination,
427 getVideosListSort,
428 removeVideo,
429 searchVideo,
430 searchVideoWithPagination,
431 searchVideoWithSort,
432 testVideoImage,
433 uploadVideo,
434 updateVideo,
435 rateVideo,
436 viewVideo,
437 parseTorrentVideo,
438 completeVideoCheck
439 }