diff options
Diffstat (limited to 'packages/tests/src/api/videos/multiple-servers.ts')
-rw-r--r-- | packages/tests/src/api/videos/multiple-servers.ts | 1095 |
1 files changed, 1095 insertions, 0 deletions
diff --git a/packages/tests/src/api/videos/multiple-servers.ts b/packages/tests/src/api/videos/multiple-servers.ts new file mode 100644 index 000000000..03afd7cbb --- /dev/null +++ b/packages/tests/src/api/videos/multiple-servers.ts | |||
@@ -0,0 +1,1095 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import { expect } from 'chai' | ||
4 | import request from 'supertest' | ||
5 | import { wait } from '@peertube/peertube-core-utils' | ||
6 | import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@peertube/peertube-models' | ||
7 | import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils' | ||
8 | import { | ||
9 | cleanupTests, | ||
10 | createMultipleServers, | ||
11 | doubleFollow, | ||
12 | makeGetRequest, | ||
13 | PeerTubeServer, | ||
14 | setAccessTokensToServers, | ||
15 | setDefaultAccountAvatar, | ||
16 | setDefaultChannelAvatar, | ||
17 | waitJobs | ||
18 | } from '@peertube/peertube-server-commands' | ||
19 | import { testImageGeneratedByFFmpeg, dateIsValid } from '@tests/shared/checks.js' | ||
20 | import { checkTmpIsEmpty } from '@tests/shared/directories.js' | ||
21 | import { completeVideoCheck, saveVideoInServers, checkVideoFilesWereRemoved } from '@tests/shared/videos.js' | ||
22 | import { checkWebTorrentWorks } from '@tests/shared/webtorrent.js' | ||
23 | |||
24 | describe('Test multiple servers', function () { | ||
25 | let servers: PeerTubeServer[] = [] | ||
26 | const toRemove = [] | ||
27 | let videoUUID = '' | ||
28 | let videoChannelId: number | ||
29 | |||
30 | before(async function () { | ||
31 | this.timeout(120000) | ||
32 | |||
33 | servers = await createMultipleServers(3) | ||
34 | |||
35 | // Get the access tokens | ||
36 | await setAccessTokensToServers(servers) | ||
37 | |||
38 | { | ||
39 | const videoChannel = { | ||
40 | name: 'super_channel_name', | ||
41 | displayName: 'my channel', | ||
42 | description: 'super channel' | ||
43 | } | ||
44 | await servers[0].channels.create({ attributes: videoChannel }) | ||
45 | await setDefaultChannelAvatar(servers[0], videoChannel.name) | ||
46 | await setDefaultAccountAvatar(servers) | ||
47 | |||
48 | const { data } = await servers[0].channels.list({ start: 0, count: 1 }) | ||
49 | videoChannelId = data[0].id | ||
50 | } | ||
51 | |||
52 | // Server 1 and server 2 follow each other | ||
53 | await doubleFollow(servers[0], servers[1]) | ||
54 | // Server 1 and server 3 follow each other | ||
55 | await doubleFollow(servers[0], servers[2]) | ||
56 | // Server 2 and server 3 follow each other | ||
57 | await doubleFollow(servers[1], servers[2]) | ||
58 | }) | ||
59 | |||
60 | it('Should not have videos for all servers', async function () { | ||
61 | for (const server of servers) { | ||
62 | const { data } = await server.videos.list() | ||
63 | expect(data).to.be.an('array') | ||
64 | expect(data.length).to.equal(0) | ||
65 | } | ||
66 | }) | ||
67 | |||
68 | describe('Should upload the video and propagate on each server', function () { | ||
69 | |||
70 | it('Should upload the video on server 1 and propagate on each server', async function () { | ||
71 | this.timeout(60000) | ||
72 | |||
73 | const attributes = { | ||
74 | name: 'my super name for server 1', | ||
75 | category: 5, | ||
76 | licence: 4, | ||
77 | language: 'ja', | ||
78 | nsfw: true, | ||
79 | description: 'my super description for server 1', | ||
80 | support: 'my super support text for server 1', | ||
81 | originallyPublishedAt: '2019-02-10T13:38:14.449Z', | ||
82 | tags: [ 'tag1p1', 'tag2p1' ], | ||
83 | channelId: videoChannelId, | ||
84 | fixture: 'video_short1.webm' | ||
85 | } | ||
86 | await servers[0].videos.upload({ attributes }) | ||
87 | |||
88 | await waitJobs(servers) | ||
89 | |||
90 | // All servers should have this video | ||
91 | let publishedAt: string = null | ||
92 | for (const server of servers) { | ||
93 | const isLocal = server.port === servers[0].port | ||
94 | const checkAttributes = { | ||
95 | name: 'my super name for server 1', | ||
96 | category: 5, | ||
97 | licence: 4, | ||
98 | language: 'ja', | ||
99 | nsfw: true, | ||
100 | description: 'my super description for server 1', | ||
101 | support: 'my super support text for server 1', | ||
102 | originallyPublishedAt: '2019-02-10T13:38:14.449Z', | ||
103 | account: { | ||
104 | name: 'root', | ||
105 | host: servers[0].host | ||
106 | }, | ||
107 | isLocal, | ||
108 | publishedAt, | ||
109 | duration: 10, | ||
110 | tags: [ 'tag1p1', 'tag2p1' ], | ||
111 | privacy: VideoPrivacy.PUBLIC, | ||
112 | commentsEnabled: true, | ||
113 | downloadEnabled: true, | ||
114 | channel: { | ||
115 | displayName: 'my channel', | ||
116 | name: 'super_channel_name', | ||
117 | description: 'super channel', | ||
118 | isLocal | ||
119 | }, | ||
120 | fixture: 'video_short1.webm', | ||
121 | files: [ | ||
122 | { | ||
123 | resolution: 720, | ||
124 | size: 572456 | ||
125 | } | ||
126 | ] | ||
127 | } | ||
128 | |||
129 | const { data } = await server.videos.list() | ||
130 | expect(data).to.be.an('array') | ||
131 | expect(data.length).to.equal(1) | ||
132 | const video = data[0] | ||
133 | |||
134 | await completeVideoCheck({ server, originServer: servers[0], videoUUID: video.uuid, attributes: checkAttributes }) | ||
135 | publishedAt = video.publishedAt as string | ||
136 | |||
137 | expect(video.channel.avatars).to.have.lengthOf(2) | ||
138 | expect(video.account.avatars).to.have.lengthOf(2) | ||
139 | |||
140 | for (const image of [ ...video.channel.avatars, ...video.account.avatars ]) { | ||
141 | expect(image.createdAt).to.exist | ||
142 | expect(image.updatedAt).to.exist | ||
143 | expect(image.width).to.be.above(20).and.below(1000) | ||
144 | expect(image.path).to.exist | ||
145 | |||
146 | await makeGetRequest({ | ||
147 | url: server.url, | ||
148 | path: image.path, | ||
149 | expectedStatus: HttpStatusCode.OK_200 | ||
150 | }) | ||
151 | } | ||
152 | } | ||
153 | }) | ||
154 | |||
155 | it('Should upload the video on server 2 and propagate on each server', async function () { | ||
156 | this.timeout(240000) | ||
157 | |||
158 | const user = { | ||
159 | username: 'user1', | ||
160 | password: 'super_password' | ||
161 | } | ||
162 | await servers[1].users.create({ username: user.username, password: user.password }) | ||
163 | const userAccessToken = await servers[1].login.getAccessToken(user) | ||
164 | |||
165 | const attributes = { | ||
166 | name: 'my super name for server 2', | ||
167 | category: 4, | ||
168 | licence: 3, | ||
169 | language: 'de', | ||
170 | nsfw: true, | ||
171 | description: 'my super description for server 2', | ||
172 | support: 'my super support text for server 2', | ||
173 | tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], | ||
174 | fixture: 'video_short2.webm', | ||
175 | thumbnailfile: 'custom-thumbnail.jpg', | ||
176 | previewfile: 'custom-preview.jpg' | ||
177 | } | ||
178 | await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' }) | ||
179 | |||
180 | // Transcoding | ||
181 | await waitJobs(servers) | ||
182 | |||
183 | // All servers should have this video | ||
184 | for (const server of servers) { | ||
185 | const isLocal = server.url === servers[1].url | ||
186 | const checkAttributes = { | ||
187 | name: 'my super name for server 2', | ||
188 | category: 4, | ||
189 | licence: 3, | ||
190 | language: 'de', | ||
191 | nsfw: true, | ||
192 | description: 'my super description for server 2', | ||
193 | support: 'my super support text for server 2', | ||
194 | account: { | ||
195 | name: 'user1', | ||
196 | host: servers[1].host | ||
197 | }, | ||
198 | isLocal, | ||
199 | commentsEnabled: true, | ||
200 | downloadEnabled: true, | ||
201 | duration: 5, | ||
202 | tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], | ||
203 | privacy: VideoPrivacy.PUBLIC, | ||
204 | channel: { | ||
205 | displayName: 'Main user1 channel', | ||
206 | name: 'user1_channel', | ||
207 | description: 'super channel', | ||
208 | isLocal | ||
209 | }, | ||
210 | fixture: 'video_short2.webm', | ||
211 | files: [ | ||
212 | { | ||
213 | resolution: 240, | ||
214 | size: 270000 | ||
215 | }, | ||
216 | { | ||
217 | resolution: 360, | ||
218 | size: 359000 | ||
219 | }, | ||
220 | { | ||
221 | resolution: 480, | ||
222 | size: 465000 | ||
223 | }, | ||
224 | { | ||
225 | resolution: 720, | ||
226 | size: 750000 | ||
227 | } | ||
228 | ], | ||
229 | thumbnailfile: 'custom-thumbnail', | ||
230 | previewfile: 'custom-preview' | ||
231 | } | ||
232 | |||
233 | const { data } = await server.videos.list() | ||
234 | expect(data).to.be.an('array') | ||
235 | expect(data.length).to.equal(2) | ||
236 | const video = data[1] | ||
237 | |||
238 | await completeVideoCheck({ server, originServer: servers[1], videoUUID: video.uuid, attributes: checkAttributes }) | ||
239 | } | ||
240 | }) | ||
241 | |||
242 | it('Should upload two videos on server 3 and propagate on each server', async function () { | ||
243 | this.timeout(45000) | ||
244 | |||
245 | { | ||
246 | const attributes = { | ||
247 | name: 'my super name for server 3', | ||
248 | category: 6, | ||
249 | licence: 5, | ||
250 | language: 'de', | ||
251 | nsfw: true, | ||
252 | description: 'my super description for server 3', | ||
253 | support: 'my super support text for server 3', | ||
254 | tags: [ 'tag1p3' ], | ||
255 | fixture: 'video_short3.webm' | ||
256 | } | ||
257 | await servers[2].videos.upload({ attributes }) | ||
258 | } | ||
259 | |||
260 | { | ||
261 | const attributes = { | ||
262 | name: 'my super name for server 3-2', | ||
263 | category: 7, | ||
264 | licence: 6, | ||
265 | language: 'ko', | ||
266 | nsfw: false, | ||
267 | description: 'my super description for server 3-2', | ||
268 | support: 'my super support text for server 3-2', | ||
269 | tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], | ||
270 | fixture: 'video_short.webm' | ||
271 | } | ||
272 | await servers[2].videos.upload({ attributes }) | ||
273 | } | ||
274 | |||
275 | await waitJobs(servers) | ||
276 | |||
277 | // All servers should have this video | ||
278 | for (const server of servers) { | ||
279 | const isLocal = server.url === servers[2].url | ||
280 | const { data } = await server.videos.list() | ||
281 | |||
282 | expect(data).to.be.an('array') | ||
283 | expect(data.length).to.equal(4) | ||
284 | |||
285 | // We not sure about the order of the two last uploads | ||
286 | let video1 = null | ||
287 | let video2 = null | ||
288 | if (data[2].name === 'my super name for server 3') { | ||
289 | video1 = data[2] | ||
290 | video2 = data[3] | ||
291 | } else { | ||
292 | video1 = data[3] | ||
293 | video2 = data[2] | ||
294 | } | ||
295 | |||
296 | const checkAttributesVideo1 = { | ||
297 | name: 'my super name for server 3', | ||
298 | category: 6, | ||
299 | licence: 5, | ||
300 | language: 'de', | ||
301 | nsfw: true, | ||
302 | description: 'my super description for server 3', | ||
303 | support: 'my super support text for server 3', | ||
304 | account: { | ||
305 | name: 'root', | ||
306 | host: servers[2].host | ||
307 | }, | ||
308 | isLocal, | ||
309 | duration: 5, | ||
310 | commentsEnabled: true, | ||
311 | downloadEnabled: true, | ||
312 | tags: [ 'tag1p3' ], | ||
313 | privacy: VideoPrivacy.PUBLIC, | ||
314 | channel: { | ||
315 | displayName: 'Main root channel', | ||
316 | name: 'root_channel', | ||
317 | description: '', | ||
318 | isLocal | ||
319 | }, | ||
320 | fixture: 'video_short3.webm', | ||
321 | files: [ | ||
322 | { | ||
323 | resolution: 720, | ||
324 | size: 292677 | ||
325 | } | ||
326 | ] | ||
327 | } | ||
328 | await completeVideoCheck({ server, originServer: servers[2], videoUUID: video1.uuid, attributes: checkAttributesVideo1 }) | ||
329 | |||
330 | const checkAttributesVideo2 = { | ||
331 | name: 'my super name for server 3-2', | ||
332 | category: 7, | ||
333 | licence: 6, | ||
334 | language: 'ko', | ||
335 | nsfw: false, | ||
336 | description: 'my super description for server 3-2', | ||
337 | support: 'my super support text for server 3-2', | ||
338 | account: { | ||
339 | name: 'root', | ||
340 | host: servers[2].host | ||
341 | }, | ||
342 | commentsEnabled: true, | ||
343 | downloadEnabled: true, | ||
344 | isLocal, | ||
345 | duration: 5, | ||
346 | tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], | ||
347 | privacy: VideoPrivacy.PUBLIC, | ||
348 | channel: { | ||
349 | displayName: 'Main root channel', | ||
350 | name: 'root_channel', | ||
351 | description: '', | ||
352 | isLocal | ||
353 | }, | ||
354 | fixture: 'video_short.webm', | ||
355 | files: [ | ||
356 | { | ||
357 | resolution: 720, | ||
358 | size: 218910 | ||
359 | } | ||
360 | ] | ||
361 | } | ||
362 | await completeVideoCheck({ server, originServer: servers[2], videoUUID: video2.uuid, attributes: checkAttributesVideo2 }) | ||
363 | } | ||
364 | }) | ||
365 | }) | ||
366 | |||
367 | describe('It should list local videos', function () { | ||
368 | it('Should list only local videos on server 1', async function () { | ||
369 | const { data, total } = await servers[0].videos.list({ isLocal: true }) | ||
370 | |||
371 | expect(total).to.equal(1) | ||
372 | expect(data).to.be.an('array') | ||
373 | expect(data.length).to.equal(1) | ||
374 | expect(data[0].name).to.equal('my super name for server 1') | ||
375 | }) | ||
376 | |||
377 | it('Should list only local videos on server 2', async function () { | ||
378 | const { data, total } = await servers[1].videos.list({ isLocal: true }) | ||
379 | |||
380 | expect(total).to.equal(1) | ||
381 | expect(data).to.be.an('array') | ||
382 | expect(data.length).to.equal(1) | ||
383 | expect(data[0].name).to.equal('my super name for server 2') | ||
384 | }) | ||
385 | |||
386 | it('Should list only local videos on server 3', async function () { | ||
387 | const { data, total } = await servers[2].videos.list({ isLocal: true }) | ||
388 | |||
389 | expect(total).to.equal(2) | ||
390 | expect(data).to.be.an('array') | ||
391 | expect(data.length).to.equal(2) | ||
392 | expect(data[0].name).to.equal('my super name for server 3') | ||
393 | expect(data[1].name).to.equal('my super name for server 3-2') | ||
394 | }) | ||
395 | }) | ||
396 | |||
397 | describe('Should seed the uploaded video', function () { | ||
398 | |||
399 | it('Should add the file 1 by asking server 3', async function () { | ||
400 | this.retries(2) | ||
401 | this.timeout(30000) | ||
402 | |||
403 | const { data } = await servers[2].videos.list() | ||
404 | |||
405 | const video = data[0] | ||
406 | toRemove.push(data[2]) | ||
407 | toRemove.push(data[3]) | ||
408 | |||
409 | const videoDetails = await servers[2].videos.get({ id: video.id }) | ||
410 | |||
411 | await checkWebTorrentWorks(videoDetails.files[0].magnetUri) | ||
412 | }) | ||
413 | |||
414 | it('Should add the file 2 by asking server 1', async function () { | ||
415 | this.retries(2) | ||
416 | this.timeout(30000) | ||
417 | |||
418 | const { data } = await servers[0].videos.list() | ||
419 | |||
420 | const video = data[1] | ||
421 | const videoDetails = await servers[0].videos.get({ id: video.id }) | ||
422 | |||
423 | await checkWebTorrentWorks(videoDetails.files[0].magnetUri) | ||
424 | }) | ||
425 | |||
426 | it('Should add the file 3 by asking server 2', async function () { | ||
427 | this.retries(2) | ||
428 | this.timeout(30000) | ||
429 | |||
430 | const { data } = await servers[1].videos.list() | ||
431 | |||
432 | const video = data[2] | ||
433 | const videoDetails = await servers[1].videos.get({ id: video.id }) | ||
434 | |||
435 | await checkWebTorrentWorks(videoDetails.files[0].magnetUri) | ||
436 | }) | ||
437 | |||
438 | it('Should add the file 3-2 by asking server 1', async function () { | ||
439 | this.retries(2) | ||
440 | this.timeout(30000) | ||
441 | |||
442 | const { data } = await servers[0].videos.list() | ||
443 | |||
444 | const video = data[3] | ||
445 | const videoDetails = await servers[0].videos.get({ id: video.id }) | ||
446 | |||
447 | await checkWebTorrentWorks(videoDetails.files[0].magnetUri) | ||
448 | }) | ||
449 | |||
450 | it('Should add the file 2 in 360p by asking server 1', async function () { | ||
451 | this.retries(2) | ||
452 | this.timeout(30000) | ||
453 | |||
454 | const { data } = await servers[0].videos.list() | ||
455 | |||
456 | const video = data.find(v => v.name === 'my super name for server 2') | ||
457 | const videoDetails = await servers[0].videos.get({ id: video.id }) | ||
458 | |||
459 | const file = videoDetails.files.find(f => f.resolution.id === 360) | ||
460 | expect(file).not.to.be.undefined | ||
461 | |||
462 | await checkWebTorrentWorks(file.magnetUri) | ||
463 | }) | ||
464 | }) | ||
465 | |||
466 | describe('Should update video views, likes and dislikes', function () { | ||
467 | let localVideosServer3 = [] | ||
468 | let remoteVideosServer1 = [] | ||
469 | let remoteVideosServer2 = [] | ||
470 | let remoteVideosServer3 = [] | ||
471 | |||
472 | before(async function () { | ||
473 | { | ||
474 | const { data } = await servers[0].videos.list() | ||
475 | remoteVideosServer1 = data.filter(video => video.isLocal === false).map(video => video.uuid) | ||
476 | } | ||
477 | |||
478 | { | ||
479 | const { data } = await servers[1].videos.list() | ||
480 | remoteVideosServer2 = data.filter(video => video.isLocal === false).map(video => video.uuid) | ||
481 | } | ||
482 | |||
483 | { | ||
484 | const { data } = await servers[2].videos.list() | ||
485 | localVideosServer3 = data.filter(video => video.isLocal === true).map(video => video.uuid) | ||
486 | remoteVideosServer3 = data.filter(video => video.isLocal === false).map(video => video.uuid) | ||
487 | } | ||
488 | }) | ||
489 | |||
490 | it('Should view multiple videos on owned servers', async function () { | ||
491 | this.timeout(30000) | ||
492 | |||
493 | await servers[2].views.simulateView({ id: localVideosServer3[0] }) | ||
494 | await wait(1000) | ||
495 | |||
496 | await servers[2].views.simulateView({ id: localVideosServer3[0] }) | ||
497 | await servers[2].views.simulateView({ id: localVideosServer3[1] }) | ||
498 | |||
499 | await wait(1000) | ||
500 | |||
501 | await servers[2].views.simulateView({ id: localVideosServer3[0] }) | ||
502 | await servers[2].views.simulateView({ id: localVideosServer3[0] }) | ||
503 | |||
504 | await waitJobs(servers) | ||
505 | |||
506 | for (const server of servers) { | ||
507 | await server.debug.sendCommand({ body: { command: 'process-video-views-buffer' } }) | ||
508 | } | ||
509 | |||
510 | await waitJobs(servers) | ||
511 | |||
512 | for (const server of servers) { | ||
513 | const { data } = await server.videos.list() | ||
514 | |||
515 | const video0 = data.find(v => v.uuid === localVideosServer3[0]) | ||
516 | const video1 = data.find(v => v.uuid === localVideosServer3[1]) | ||
517 | |||
518 | expect(video0.views).to.equal(3) | ||
519 | expect(video1.views).to.equal(1) | ||
520 | } | ||
521 | }) | ||
522 | |||
523 | it('Should view multiple videos on each servers', async function () { | ||
524 | this.timeout(45000) | ||
525 | |||
526 | const tasks: Promise<any>[] = [] | ||
527 | tasks.push(servers[0].views.simulateView({ id: remoteVideosServer1[0] })) | ||
528 | tasks.push(servers[1].views.simulateView({ id: remoteVideosServer2[0] })) | ||
529 | tasks.push(servers[1].views.simulateView({ id: remoteVideosServer2[0] })) | ||
530 | tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[0] })) | ||
531 | tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[1] })) | ||
532 | tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[1] })) | ||
533 | tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[1] })) | ||
534 | tasks.push(servers[2].views.simulateView({ id: localVideosServer3[1] })) | ||
535 | tasks.push(servers[2].views.simulateView({ id: localVideosServer3[1] })) | ||
536 | tasks.push(servers[2].views.simulateView({ id: localVideosServer3[1] })) | ||
537 | |||
538 | await Promise.all(tasks) | ||
539 | |||
540 | await waitJobs(servers) | ||
541 | |||
542 | for (const server of servers) { | ||
543 | await server.debug.sendCommand({ body: { command: 'process-video-views-buffer' } }) | ||
544 | } | ||
545 | |||
546 | await waitJobs(servers) | ||
547 | |||
548 | let baseVideos = null | ||
549 | |||
550 | for (const server of servers) { | ||
551 | const { data } = await server.videos.list() | ||
552 | |||
553 | // Initialize base videos for future comparisons | ||
554 | if (baseVideos === null) { | ||
555 | baseVideos = data | ||
556 | continue | ||
557 | } | ||
558 | |||
559 | for (const baseVideo of baseVideos) { | ||
560 | const sameVideo = data.find(video => video.name === baseVideo.name) | ||
561 | expect(baseVideo.views).to.equal(sameVideo.views) | ||
562 | } | ||
563 | } | ||
564 | }) | ||
565 | |||
566 | it('Should like and dislikes videos on different services', async function () { | ||
567 | this.timeout(50000) | ||
568 | |||
569 | await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' }) | ||
570 | await wait(500) | ||
571 | await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'dislike' }) | ||
572 | await wait(500) | ||
573 | await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' }) | ||
574 | await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'like' }) | ||
575 | await wait(500) | ||
576 | await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'dislike' }) | ||
577 | await servers[2].videos.rate({ id: remoteVideosServer3[1], rating: 'dislike' }) | ||
578 | await wait(500) | ||
579 | await servers[2].videos.rate({ id: remoteVideosServer3[0], rating: 'like' }) | ||
580 | |||
581 | await waitJobs(servers) | ||
582 | await wait(5000) | ||
583 | await waitJobs(servers) | ||
584 | |||
585 | let baseVideos = null | ||
586 | for (const server of servers) { | ||
587 | const { data } = await server.videos.list() | ||
588 | |||
589 | // Initialize base videos for future comparisons | ||
590 | if (baseVideos === null) { | ||
591 | baseVideos = data | ||
592 | continue | ||
593 | } | ||
594 | |||
595 | for (const baseVideo of baseVideos) { | ||
596 | const sameVideo = data.find(video => video.name === baseVideo.name) | ||
597 | expect(baseVideo.likes).to.equal(sameVideo.likes, `Likes of ${sameVideo.uuid} do not correspond`) | ||
598 | expect(baseVideo.dislikes).to.equal(sameVideo.dislikes, `Dislikes of ${sameVideo.uuid} do not correspond`) | ||
599 | } | ||
600 | } | ||
601 | }) | ||
602 | }) | ||
603 | |||
604 | describe('Should manipulate these videos', function () { | ||
605 | let updatedAtMin: Date | ||
606 | |||
607 | it('Should update video 3', async function () { | ||
608 | this.timeout(30000) | ||
609 | |||
610 | const attributes = { | ||
611 | name: 'my super video updated', | ||
612 | category: 10, | ||
613 | licence: 7, | ||
614 | language: 'fr', | ||
615 | nsfw: true, | ||
616 | description: 'my super description updated', | ||
617 | support: 'my super support text updated', | ||
618 | tags: [ 'tag_up_1', 'tag_up_2' ], | ||
619 | thumbnailfile: 'custom-thumbnail.jpg', | ||
620 | originallyPublishedAt: '2019-02-11T13:38:14.449Z', | ||
621 | previewfile: 'custom-preview.jpg' | ||
622 | } | ||
623 | |||
624 | updatedAtMin = new Date() | ||
625 | await servers[2].videos.update({ id: toRemove[0].id, attributes }) | ||
626 | |||
627 | await waitJobs(servers) | ||
628 | }) | ||
629 | |||
630 | it('Should have the video 3 updated on each server', async function () { | ||
631 | this.timeout(30000) | ||
632 | |||
633 | for (const server of servers) { | ||
634 | const { data } = await server.videos.list() | ||
635 | |||
636 | const videoUpdated = data.find(video => video.name === 'my super video updated') | ||
637 | expect(!!videoUpdated).to.be.true | ||
638 | |||
639 | expect(new Date(videoUpdated.updatedAt)).to.be.greaterThan(updatedAtMin) | ||
640 | |||
641 | const isLocal = server.url === servers[2].url | ||
642 | const checkAttributes = { | ||
643 | name: 'my super video updated', | ||
644 | category: 10, | ||
645 | licence: 7, | ||
646 | language: 'fr', | ||
647 | nsfw: true, | ||
648 | description: 'my super description updated', | ||
649 | support: 'my super support text updated', | ||
650 | originallyPublishedAt: '2019-02-11T13:38:14.449Z', | ||
651 | account: { | ||
652 | name: 'root', | ||
653 | host: servers[2].host | ||
654 | }, | ||
655 | isLocal, | ||
656 | duration: 5, | ||
657 | commentsEnabled: true, | ||
658 | downloadEnabled: true, | ||
659 | tags: [ 'tag_up_1', 'tag_up_2' ], | ||
660 | privacy: VideoPrivacy.PUBLIC, | ||
661 | channel: { | ||
662 | displayName: 'Main root channel', | ||
663 | name: 'root_channel', | ||
664 | description: '', | ||
665 | isLocal | ||
666 | }, | ||
667 | fixture: 'video_short3.webm', | ||
668 | files: [ | ||
669 | { | ||
670 | resolution: 720, | ||
671 | size: 292677 | ||
672 | } | ||
673 | ], | ||
674 | thumbnailfile: 'custom-thumbnail', | ||
675 | previewfile: 'custom-preview' | ||
676 | } | ||
677 | await completeVideoCheck({ server, originServer: servers[2], videoUUID: videoUpdated.uuid, attributes: checkAttributes }) | ||
678 | } | ||
679 | }) | ||
680 | |||
681 | it('Should only update thumbnail and update updatedAt attribute', async function () { | ||
682 | this.timeout(30000) | ||
683 | |||
684 | const attributes = { | ||
685 | thumbnailfile: 'custom-thumbnail.jpg' | ||
686 | } | ||
687 | |||
688 | updatedAtMin = new Date() | ||
689 | await servers[2].videos.update({ id: toRemove[0].id, attributes }) | ||
690 | |||
691 | await waitJobs(servers) | ||
692 | |||
693 | for (const server of servers) { | ||
694 | const { data } = await server.videos.list() | ||
695 | |||
696 | const videoUpdated = data.find(video => video.name === 'my super video updated') | ||
697 | expect(new Date(videoUpdated.updatedAt)).to.be.greaterThan(updatedAtMin) | ||
698 | } | ||
699 | }) | ||
700 | |||
701 | it('Should remove the videos 3 and 3-2 by asking server 3 and correctly delete files', async function () { | ||
702 | this.timeout(30000) | ||
703 | |||
704 | for (const id of [ toRemove[0].id, toRemove[1].id ]) { | ||
705 | await saveVideoInServers(servers, id) | ||
706 | |||
707 | await servers[2].videos.remove({ id }) | ||
708 | |||
709 | await waitJobs(servers) | ||
710 | |||
711 | for (const server of servers) { | ||
712 | await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails }) | ||
713 | } | ||
714 | } | ||
715 | }) | ||
716 | |||
717 | it('Should have videos 1 and 3 on each server', async function () { | ||
718 | for (const server of servers) { | ||
719 | const { data } = await server.videos.list() | ||
720 | |||
721 | expect(data).to.be.an('array') | ||
722 | expect(data.length).to.equal(2) | ||
723 | expect(data[0].name).not.to.equal(data[1].name) | ||
724 | expect(data[0].name).not.to.equal(toRemove[0].name) | ||
725 | expect(data[1].name).not.to.equal(toRemove[0].name) | ||
726 | expect(data[0].name).not.to.equal(toRemove[1].name) | ||
727 | expect(data[1].name).not.to.equal(toRemove[1].name) | ||
728 | |||
729 | videoUUID = data.find(video => video.name === 'my super name for server 1').uuid | ||
730 | } | ||
731 | }) | ||
732 | |||
733 | it('Should get the same video by UUID on each server', async function () { | ||
734 | let baseVideo = null | ||
735 | for (const server of servers) { | ||
736 | const video = await server.videos.get({ id: videoUUID }) | ||
737 | |||
738 | if (baseVideo === null) { | ||
739 | baseVideo = video | ||
740 | continue | ||
741 | } | ||
742 | |||
743 | expect(baseVideo.name).to.equal(video.name) | ||
744 | expect(baseVideo.uuid).to.equal(video.uuid) | ||
745 | expect(baseVideo.category.id).to.equal(video.category.id) | ||
746 | expect(baseVideo.language.id).to.equal(video.language.id) | ||
747 | expect(baseVideo.licence.id).to.equal(video.licence.id) | ||
748 | expect(baseVideo.nsfw).to.equal(video.nsfw) | ||
749 | expect(baseVideo.account.name).to.equal(video.account.name) | ||
750 | expect(baseVideo.account.displayName).to.equal(video.account.displayName) | ||
751 | expect(baseVideo.account.url).to.equal(video.account.url) | ||
752 | expect(baseVideo.account.host).to.equal(video.account.host) | ||
753 | expect(baseVideo.tags).to.deep.equal(video.tags) | ||
754 | } | ||
755 | }) | ||
756 | |||
757 | it('Should get the preview from each server', async function () { | ||
758 | for (const server of servers) { | ||
759 | const video = await server.videos.get({ id: videoUUID }) | ||
760 | |||
761 | await testImageGeneratedByFFmpeg(server.url, 'video_short1-preview.webm', video.previewPath) | ||
762 | } | ||
763 | }) | ||
764 | }) | ||
765 | |||
766 | describe('Should comment these videos', function () { | ||
767 | let childOfFirstChild: VideoCommentThreadTree | ||
768 | |||
769 | it('Should add comment (threads and replies)', async function () { | ||
770 | this.timeout(25000) | ||
771 | |||
772 | { | ||
773 | const text = 'my super first comment' | ||
774 | await servers[0].comments.createThread({ videoId: videoUUID, text }) | ||
775 | } | ||
776 | |||
777 | { | ||
778 | const text = 'my super second comment' | ||
779 | await servers[2].comments.createThread({ videoId: videoUUID, text }) | ||
780 | } | ||
781 | |||
782 | await waitJobs(servers) | ||
783 | |||
784 | { | ||
785 | const threadId = await servers[1].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' }) | ||
786 | |||
787 | const text = 'my super answer to thread 1' | ||
788 | await servers[1].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text }) | ||
789 | } | ||
790 | |||
791 | await waitJobs(servers) | ||
792 | |||
793 | { | ||
794 | const threadId = await servers[2].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' }) | ||
795 | |||
796 | const body = await servers[2].comments.getThread({ videoId: videoUUID, threadId }) | ||
797 | const childCommentId = body.children[0].comment.id | ||
798 | |||
799 | const text3 = 'my second answer to thread 1' | ||
800 | await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: text3 }) | ||
801 | |||
802 | const text2 = 'my super answer to answer of thread 1' | ||
803 | await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: childCommentId, text: text2 }) | ||
804 | } | ||
805 | |||
806 | await waitJobs(servers) | ||
807 | }) | ||
808 | |||
809 | it('Should have these threads', async function () { | ||
810 | for (const server of servers) { | ||
811 | const body = await server.comments.listThreads({ videoId: videoUUID }) | ||
812 | |||
813 | expect(body.total).to.equal(2) | ||
814 | expect(body.data).to.be.an('array') | ||
815 | expect(body.data).to.have.lengthOf(2) | ||
816 | |||
817 | { | ||
818 | const comment = body.data.find(c => c.text === 'my super first comment') | ||
819 | expect(comment).to.not.be.undefined | ||
820 | expect(comment.inReplyToCommentId).to.be.null | ||
821 | expect(comment.account.name).to.equal('root') | ||
822 | expect(comment.account.host).to.equal(servers[0].host) | ||
823 | expect(comment.totalReplies).to.equal(3) | ||
824 | expect(dateIsValid(comment.createdAt as string)).to.be.true | ||
825 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | ||
826 | } | ||
827 | |||
828 | { | ||
829 | const comment = body.data.find(c => c.text === 'my super second comment') | ||
830 | expect(comment).to.not.be.undefined | ||
831 | expect(comment.inReplyToCommentId).to.be.null | ||
832 | expect(comment.account.name).to.equal('root') | ||
833 | expect(comment.account.host).to.equal(servers[2].host) | ||
834 | expect(comment.totalReplies).to.equal(0) | ||
835 | expect(dateIsValid(comment.createdAt as string)).to.be.true | ||
836 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | ||
837 | } | ||
838 | } | ||
839 | }) | ||
840 | |||
841 | it('Should have these comments', async function () { | ||
842 | for (const server of servers) { | ||
843 | const body = await server.comments.listThreads({ videoId: videoUUID }) | ||
844 | const threadId = body.data.find(c => c.text === 'my super first comment').id | ||
845 | |||
846 | const tree = await server.comments.getThread({ videoId: videoUUID, threadId }) | ||
847 | |||
848 | expect(tree.comment.text).equal('my super first comment') | ||
849 | expect(tree.comment.account.name).equal('root') | ||
850 | expect(tree.comment.account.host).equal(servers[0].host) | ||
851 | expect(tree.children).to.have.lengthOf(2) | ||
852 | |||
853 | const firstChild = tree.children[0] | ||
854 | expect(firstChild.comment.text).to.equal('my super answer to thread 1') | ||
855 | expect(firstChild.comment.account.name).equal('root') | ||
856 | expect(firstChild.comment.account.host).equal(servers[1].host) | ||
857 | expect(firstChild.children).to.have.lengthOf(1) | ||
858 | |||
859 | childOfFirstChild = firstChild.children[0] | ||
860 | expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') | ||
861 | expect(childOfFirstChild.comment.account.name).equal('root') | ||
862 | expect(childOfFirstChild.comment.account.host).equal(servers[2].host) | ||
863 | expect(childOfFirstChild.children).to.have.lengthOf(0) | ||
864 | |||
865 | const secondChild = tree.children[1] | ||
866 | expect(secondChild.comment.text).to.equal('my second answer to thread 1') | ||
867 | expect(secondChild.comment.account.name).equal('root') | ||
868 | expect(secondChild.comment.account.host).equal(servers[2].host) | ||
869 | expect(secondChild.children).to.have.lengthOf(0) | ||
870 | } | ||
871 | }) | ||
872 | |||
873 | it('Should delete a reply', async function () { | ||
874 | this.timeout(30000) | ||
875 | |||
876 | await servers[2].comments.delete({ videoId: videoUUID, commentId: childOfFirstChild.comment.id }) | ||
877 | |||
878 | await waitJobs(servers) | ||
879 | }) | ||
880 | |||
881 | it('Should have this comment marked as deleted', async function () { | ||
882 | for (const server of servers) { | ||
883 | const { data } = await server.comments.listThreads({ videoId: videoUUID }) | ||
884 | const threadId = data.find(c => c.text === 'my super first comment').id | ||
885 | |||
886 | const tree = await server.comments.getThread({ videoId: videoUUID, threadId }) | ||
887 | expect(tree.comment.text).equal('my super first comment') | ||
888 | |||
889 | const firstChild = tree.children[0] | ||
890 | expect(firstChild.comment.text).to.equal('my super answer to thread 1') | ||
891 | expect(firstChild.children).to.have.lengthOf(1) | ||
892 | |||
893 | const deletedComment = firstChild.children[0].comment | ||
894 | expect(deletedComment.isDeleted).to.be.true | ||
895 | expect(deletedComment.deletedAt).to.not.be.null | ||
896 | expect(deletedComment.account).to.be.null | ||
897 | expect(deletedComment.text).to.equal('') | ||
898 | |||
899 | const secondChild = tree.children[1] | ||
900 | expect(secondChild.comment.text).to.equal('my second answer to thread 1') | ||
901 | } | ||
902 | }) | ||
903 | |||
904 | it('Should delete the thread comments', async function () { | ||
905 | this.timeout(30000) | ||
906 | |||
907 | const { data } = await servers[0].comments.listThreads({ videoId: videoUUID }) | ||
908 | const commentId = data.find(c => c.text === 'my super first comment').id | ||
909 | await servers[0].comments.delete({ videoId: videoUUID, commentId }) | ||
910 | |||
911 | await waitJobs(servers) | ||
912 | }) | ||
913 | |||
914 | it('Should have the threads marked as deleted on other servers too', async function () { | ||
915 | for (const server of servers) { | ||
916 | const body = await server.comments.listThreads({ videoId: videoUUID }) | ||
917 | |||
918 | expect(body.total).to.equal(2) | ||
919 | expect(body.data).to.be.an('array') | ||
920 | expect(body.data).to.have.lengthOf(2) | ||
921 | |||
922 | { | ||
923 | const comment = body.data[0] | ||
924 | expect(comment).to.not.be.undefined | ||
925 | expect(comment.inReplyToCommentId).to.be.null | ||
926 | expect(comment.account.name).to.equal('root') | ||
927 | expect(comment.account.host).to.equal(servers[2].host) | ||
928 | expect(comment.totalReplies).to.equal(0) | ||
929 | expect(dateIsValid(comment.createdAt as string)).to.be.true | ||
930 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | ||
931 | } | ||
932 | |||
933 | { | ||
934 | const deletedComment = body.data[1] | ||
935 | expect(deletedComment).to.not.be.undefined | ||
936 | expect(deletedComment.isDeleted).to.be.true | ||
937 | expect(deletedComment.deletedAt).to.not.be.null | ||
938 | expect(deletedComment.text).to.equal('') | ||
939 | expect(deletedComment.inReplyToCommentId).to.be.null | ||
940 | expect(deletedComment.account).to.be.null | ||
941 | expect(deletedComment.totalReplies).to.equal(2) | ||
942 | expect(dateIsValid(deletedComment.createdAt as string)).to.be.true | ||
943 | expect(dateIsValid(deletedComment.updatedAt as string)).to.be.true | ||
944 | expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true | ||
945 | } | ||
946 | } | ||
947 | }) | ||
948 | |||
949 | it('Should delete a remote thread by the origin server', async function () { | ||
950 | this.timeout(5000) | ||
951 | |||
952 | const { data } = await servers[0].comments.listThreads({ videoId: videoUUID }) | ||
953 | const commentId = data.find(c => c.text === 'my super second comment').id | ||
954 | await servers[0].comments.delete({ videoId: videoUUID, commentId }) | ||
955 | |||
956 | await waitJobs(servers) | ||
957 | }) | ||
958 | |||
959 | it('Should have the threads marked as deleted on other servers too', async function () { | ||
960 | for (const server of servers) { | ||
961 | const body = await server.comments.listThreads({ videoId: videoUUID }) | ||
962 | |||
963 | expect(body.total).to.equal(2) | ||
964 | expect(body.data).to.have.lengthOf(2) | ||
965 | |||
966 | { | ||
967 | const comment = body.data[0] | ||
968 | expect(comment.text).to.equal('') | ||
969 | expect(comment.isDeleted).to.be.true | ||
970 | expect(comment.createdAt).to.not.be.null | ||
971 | expect(comment.deletedAt).to.not.be.null | ||
972 | expect(comment.account).to.be.null | ||
973 | expect(comment.totalReplies).to.equal(0) | ||
974 | } | ||
975 | |||
976 | { | ||
977 | const comment = body.data[1] | ||
978 | expect(comment.text).to.equal('') | ||
979 | expect(comment.isDeleted).to.be.true | ||
980 | expect(comment.createdAt).to.not.be.null | ||
981 | expect(comment.deletedAt).to.not.be.null | ||
982 | expect(comment.account).to.be.null | ||
983 | expect(comment.totalReplies).to.equal(2) | ||
984 | } | ||
985 | } | ||
986 | }) | ||
987 | |||
988 | it('Should disable comments and download', async function () { | ||
989 | this.timeout(20000) | ||
990 | |||
991 | const attributes = { | ||
992 | commentsEnabled: false, | ||
993 | downloadEnabled: false | ||
994 | } | ||
995 | |||
996 | await servers[0].videos.update({ id: videoUUID, attributes }) | ||
997 | |||
998 | await waitJobs(servers) | ||
999 | |||
1000 | for (const server of servers) { | ||
1001 | const video = await server.videos.get({ id: videoUUID }) | ||
1002 | expect(video.commentsEnabled).to.be.false | ||
1003 | expect(video.downloadEnabled).to.be.false | ||
1004 | |||
1005 | const text = 'my super forbidden comment' | ||
1006 | await server.comments.createThread({ videoId: videoUUID, text, expectedStatus: HttpStatusCode.CONFLICT_409 }) | ||
1007 | } | ||
1008 | }) | ||
1009 | }) | ||
1010 | |||
1011 | describe('With minimum parameters', function () { | ||
1012 | it('Should upload and propagate the video', async function () { | ||
1013 | this.timeout(120000) | ||
1014 | |||
1015 | const path = '/api/v1/videos/upload' | ||
1016 | |||
1017 | const req = request(servers[1].url) | ||
1018 | .post(path) | ||
1019 | .set('Accept', 'application/json') | ||
1020 | .set('Authorization', 'Bearer ' + servers[1].accessToken) | ||
1021 | .field('name', 'minimum parameters') | ||
1022 | .field('privacy', '1') | ||
1023 | .field('channelId', '1') | ||
1024 | |||
1025 | await req.attach('videofile', buildAbsoluteFixturePath('video_short.webm')) | ||
1026 | .expect(HttpStatusCode.OK_200) | ||
1027 | |||
1028 | await waitJobs(servers) | ||
1029 | |||
1030 | for (const server of servers) { | ||
1031 | const { data } = await server.videos.list() | ||
1032 | const video = data.find(v => v.name === 'minimum parameters') | ||
1033 | |||
1034 | const isLocal = server.url === servers[1].url | ||
1035 | const checkAttributes = { | ||
1036 | name: 'minimum parameters', | ||
1037 | category: null, | ||
1038 | licence: null, | ||
1039 | language: null, | ||
1040 | nsfw: false, | ||
1041 | description: null, | ||
1042 | support: null, | ||
1043 | account: { | ||
1044 | name: 'root', | ||
1045 | host: servers[1].host | ||
1046 | }, | ||
1047 | isLocal, | ||
1048 | duration: 5, | ||
1049 | commentsEnabled: true, | ||
1050 | downloadEnabled: true, | ||
1051 | tags: [], | ||
1052 | privacy: VideoPrivacy.PUBLIC, | ||
1053 | channel: { | ||
1054 | displayName: 'Main root channel', | ||
1055 | name: 'root_channel', | ||
1056 | description: '', | ||
1057 | isLocal | ||
1058 | }, | ||
1059 | fixture: 'video_short.webm', | ||
1060 | files: [ | ||
1061 | { | ||
1062 | resolution: 720, | ||
1063 | size: 61000 | ||
1064 | }, | ||
1065 | { | ||
1066 | resolution: 480, | ||
1067 | size: 40000 | ||
1068 | }, | ||
1069 | { | ||
1070 | resolution: 360, | ||
1071 | size: 32000 | ||
1072 | }, | ||
1073 | { | ||
1074 | resolution: 240, | ||
1075 | size: 23000 | ||
1076 | } | ||
1077 | ] | ||
1078 | } | ||
1079 | await completeVideoCheck({ server, originServer: servers[1], videoUUID: video.uuid, attributes: checkAttributes }) | ||
1080 | } | ||
1081 | }) | ||
1082 | }) | ||
1083 | |||
1084 | describe('TMP directory', function () { | ||
1085 | it('Should have an empty tmp directory', async function () { | ||
1086 | for (const server of servers) { | ||
1087 | await checkTmpIsEmpty(server) | ||
1088 | } | ||
1089 | }) | ||
1090 | }) | ||
1091 | |||
1092 | after(async function () { | ||
1093 | await cleanupTests(servers) | ||
1094 | }) | ||
1095 | }) | ||