]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/videos-common-filters.ts
Add runner server tests
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / videos-common-filters.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { expect } from 'chai'
4 import { pick } from '@shared/core-utils'
5 import { HttpStatusCode, UserRole, Video, VideoDetails, VideoInclude, VideoPrivacy } from '@shared/models'
6 import {
7 cleanupTests,
8 createMultipleServers,
9 doubleFollow,
10 makeGetRequest,
11 PeerTubeServer,
12 setAccessTokensToServers,
13 setDefaultAccountAvatar,
14 setDefaultVideoChannel,
15 waitJobs
16 } from '@shared/server-commands'
17
18 describe('Test videos filter', function () {
19 let servers: PeerTubeServer[]
20 let paths: string[]
21 let remotePaths: string[]
22
23 const subscriptionVideosPath = '/api/v1/users/me/subscriptions/videos'
24
25 // ---------------------------------------------------------------
26
27 before(async function () {
28 this.timeout(240000)
29
30 servers = await createMultipleServers(2)
31
32 await setAccessTokensToServers(servers)
33 await setDefaultVideoChannel(servers)
34 await setDefaultAccountAvatar(servers)
35
36 await servers[1].config.enableMinimumTranscoding()
37
38 for (const server of servers) {
39 const moderator = { username: 'moderator', password: 'my super password' }
40 await server.users.create({ username: moderator.username, password: moderator.password, role: UserRole.MODERATOR })
41 server['moderatorAccessToken'] = await server.login.getAccessToken(moderator)
42
43 await server.videos.upload({ attributes: { name: 'public ' + server.serverNumber } })
44
45 {
46 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED }
47 await server.videos.upload({ attributes })
48 }
49
50 {
51 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE }
52 await server.videos.upload({ attributes })
53 }
54
55 // Subscribing to itself
56 await server.subscriptions.add({ targetUri: 'root_channel@' + server.host })
57 }
58
59 await doubleFollow(servers[0], servers[1])
60
61 paths = [
62 `/api/v1/video-channels/root_channel/videos`,
63 `/api/v1/accounts/root/videos`,
64 '/api/v1/videos',
65 '/api/v1/search/videos',
66 subscriptionVideosPath
67 ]
68
69 remotePaths = [
70 `/api/v1/video-channels/root_channel@${servers[1].host}/videos`,
71 `/api/v1/accounts/root@${servers[1].host}/videos`,
72 '/api/v1/videos',
73 '/api/v1/search/videos'
74 ]
75 })
76
77 describe('Check deprecated videos filter', function () {
78
79 async function getVideosNames (options: {
80 server: PeerTubeServer
81 token: string
82 filter: string
83 skipSubscription?: boolean
84 expectedStatus?: HttpStatusCode
85 }) {
86 const { server, token, filter, skipSubscription = false, expectedStatus = HttpStatusCode.OK_200 } = options
87
88 const videosResults: Video[][] = []
89
90 for (const path of paths) {
91 if (skipSubscription && path === subscriptionVideosPath) continue
92
93 const res = await makeGetRequest({
94 url: server.url,
95 path,
96 token,
97 query: {
98 sort: 'createdAt',
99 filter
100 },
101 expectedStatus
102 })
103
104 videosResults.push(res.body.data.map(v => v.name))
105 }
106
107 return videosResults
108 }
109
110 it('Should display local videos', async function () {
111 for (const server of servers) {
112 const namesResults = await getVideosNames({ server, token: server.accessToken, filter: 'local' })
113 for (const names of namesResults) {
114 expect(names).to.have.lengthOf(1)
115 expect(names[0]).to.equal('public ' + server.serverNumber)
116 }
117 }
118 })
119
120 it('Should display all local videos by the admin or the moderator', async function () {
121 for (const server of servers) {
122 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
123
124 const namesResults = await getVideosNames({ server, token, filter: 'all-local', skipSubscription: true })
125 for (const names of namesResults) {
126 expect(names).to.have.lengthOf(3)
127
128 expect(names[0]).to.equal('public ' + server.serverNumber)
129 expect(names[1]).to.equal('unlisted ' + server.serverNumber)
130 expect(names[2]).to.equal('private ' + server.serverNumber)
131 }
132 }
133 }
134 })
135
136 it('Should display all videos by the admin or the moderator', async function () {
137 for (const server of servers) {
138 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
139
140 const [ channelVideos, accountVideos, videos, searchVideos ] = await getVideosNames({ server, token, filter: 'all' })
141 expect(channelVideos).to.have.lengthOf(3)
142 expect(accountVideos).to.have.lengthOf(3)
143
144 expect(videos).to.have.lengthOf(5)
145 expect(searchVideos).to.have.lengthOf(5)
146 }
147 }
148 })
149 })
150
151 describe('Check videos filters', function () {
152
153 async function listVideos (options: {
154 server: PeerTubeServer
155 path: string
156 isLocal?: boolean
157 hasWebtorrentFiles?: boolean
158 hasHLSFiles?: boolean
159 include?: VideoInclude
160 privacyOneOf?: VideoPrivacy[]
161 category?: number
162 tagsAllOf?: string[]
163 token?: string
164 expectedStatus?: HttpStatusCode
165 excludeAlreadyWatched?: boolean
166 }) {
167 const res = await makeGetRequest({
168 url: options.server.url,
169 path: options.path,
170 token: options.token ?? options.server.accessToken,
171 query: {
172 ...pick(options, [
173 'isLocal',
174 'include',
175 'category',
176 'tagsAllOf',
177 'hasWebtorrentFiles',
178 'hasHLSFiles',
179 'privacyOneOf',
180 'excludeAlreadyWatched'
181 ]),
182
183 sort: 'createdAt'
184 },
185 expectedStatus: options.expectedStatus ?? HttpStatusCode.OK_200
186 })
187
188 return res.body.data as Video[]
189 }
190
191 async function getVideosNames (
192 options: {
193 server: PeerTubeServer
194 isLocal?: boolean
195 include?: VideoInclude
196 privacyOneOf?: VideoPrivacy[]
197 token?: string
198 expectedStatus?: HttpStatusCode
199 skipSubscription?: boolean
200 excludeAlreadyWatched?: boolean
201 }
202 ) {
203 const { skipSubscription = false } = options
204 const videosResults: string[][] = []
205
206 for (const path of paths) {
207 if (skipSubscription && path === subscriptionVideosPath) continue
208
209 const videos = await listVideos({ ...options, path })
210
211 videosResults.push(videos.map(v => v.name))
212 }
213
214 return videosResults
215 }
216
217 it('Should display local videos', async function () {
218 for (const server of servers) {
219 const namesResults = await getVideosNames({ server, isLocal: true })
220
221 for (const names of namesResults) {
222 expect(names).to.have.lengthOf(1)
223 expect(names[0]).to.equal('public ' + server.serverNumber)
224 }
225 }
226 })
227
228 it('Should display local videos with hidden privacy by the admin or the moderator', async function () {
229 for (const server of servers) {
230 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
231
232 const namesResults = await getVideosNames(
233 {
234 server,
235 token,
236 isLocal: true,
237 privacyOneOf: [ VideoPrivacy.UNLISTED, VideoPrivacy.PUBLIC, VideoPrivacy.PRIVATE ],
238 skipSubscription: true
239 }
240 )
241
242 for (const names of namesResults) {
243 expect(names).to.have.lengthOf(3)
244
245 expect(names[0]).to.equal('public ' + server.serverNumber)
246 expect(names[1]).to.equal('unlisted ' + server.serverNumber)
247 expect(names[2]).to.equal('private ' + server.serverNumber)
248 }
249 }
250 }
251 })
252
253 it('Should display all videos by the admin or the moderator', async function () {
254 for (const server of servers) {
255 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
256
257 const [ channelVideos, accountVideos, videos, searchVideos ] = await getVideosNames({
258 server,
259 token,
260 privacyOneOf: [ VideoPrivacy.UNLISTED, VideoPrivacy.PUBLIC, VideoPrivacy.PRIVATE ]
261 })
262
263 expect(channelVideos).to.have.lengthOf(3)
264 expect(accountVideos).to.have.lengthOf(3)
265
266 expect(videos).to.have.lengthOf(5)
267 expect(searchVideos).to.have.lengthOf(5)
268 }
269 }
270 })
271
272 it('Should display only remote videos', async function () {
273 this.timeout(120000)
274
275 await servers[1].videos.upload({ attributes: { name: 'remote video' } })
276
277 await waitJobs(servers)
278
279 const finder = (videos: Video[]) => videos.find(v => v.name === 'remote video')
280
281 for (const path of remotePaths) {
282 {
283 const videos = await listVideos({ server: servers[0], path })
284 const video = finder(videos)
285 expect(video).to.exist
286 }
287
288 {
289 const videos = await listVideos({ server: servers[0], path, isLocal: false })
290 const video = finder(videos)
291 expect(video).to.exist
292 }
293
294 {
295 const videos = await listVideos({ server: servers[0], path, isLocal: true })
296 const video = finder(videos)
297 expect(video).to.not.exist
298 }
299 }
300 })
301
302 it('Should include not published videos', async function () {
303 await servers[0].config.enableLive({ allowReplay: false, transcoding: false })
304 await servers[0].live.create({ fields: { name: 'live video', channelId: servers[0].store.channel.id, privacy: VideoPrivacy.PUBLIC } })
305
306 const finder = (videos: Video[]) => videos.find(v => v.name === 'live video')
307
308 for (const path of paths) {
309 {
310 const videos = await listVideos({ server: servers[0], path })
311 const video = finder(videos)
312 expect(video).to.not.exist
313 expect(videos[0].state).to.not.exist
314 expect(videos[0].waitTranscoding).to.not.exist
315 }
316
317 {
318 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.NOT_PUBLISHED_STATE })
319 const video = finder(videos)
320 expect(video).to.exist
321 expect(video.state).to.exist
322 }
323 }
324 })
325
326 it('Should include blacklisted videos', async function () {
327 const { id } = await servers[0].videos.upload({ attributes: { name: 'blacklisted' } })
328
329 await servers[0].blacklist.add({ videoId: id })
330
331 const finder = (videos: Video[]) => videos.find(v => v.name === 'blacklisted')
332
333 for (const path of paths) {
334 {
335 const videos = await listVideos({ server: servers[0], path })
336 const video = finder(videos)
337 expect(video).to.not.exist
338 expect(videos[0].blacklisted).to.not.exist
339 }
340
341 {
342 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.BLACKLISTED })
343 const video = finder(videos)
344 expect(video).to.exist
345 expect(video.blacklisted).to.be.true
346 }
347 }
348 })
349
350 it('Should include videos from muted account', async function () {
351 const finder = (videos: Video[]) => videos.find(v => v.name === 'remote video')
352
353 await servers[0].blocklist.addToServerBlocklist({ account: 'root@' + servers[1].host })
354
355 for (const path of remotePaths) {
356 {
357 const videos = await listVideos({ server: servers[0], path })
358 const video = finder(videos)
359 expect(video).to.not.exist
360
361 // Some paths won't have videos
362 if (videos[0]) {
363 expect(videos[0].blockedOwner).to.not.exist
364 expect(videos[0].blockedServer).to.not.exist
365 }
366 }
367
368 {
369 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.BLOCKED_OWNER })
370
371 const video = finder(videos)
372 expect(video).to.exist
373 expect(video.blockedServer).to.be.false
374 expect(video.blockedOwner).to.be.true
375 }
376 }
377
378 await servers[0].blocklist.removeFromServerBlocklist({ account: 'root@' + servers[1].host })
379 })
380
381 it('Should include videos from muted server', async function () {
382 const finder = (videos: Video[]) => videos.find(v => v.name === 'remote video')
383
384 await servers[0].blocklist.addToServerBlocklist({ server: servers[1].host })
385
386 for (const path of remotePaths) {
387 {
388 const videos = await listVideos({ server: servers[0], path })
389 const video = finder(videos)
390 expect(video).to.not.exist
391
392 // Some paths won't have videos
393 if (videos[0]) {
394 expect(videos[0].blockedOwner).to.not.exist
395 expect(videos[0].blockedServer).to.not.exist
396 }
397 }
398
399 {
400 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.BLOCKED_OWNER })
401 const video = finder(videos)
402 expect(video).to.exist
403 expect(video.blockedServer).to.be.true
404 expect(video.blockedOwner).to.be.false
405 }
406 }
407
408 await servers[0].blocklist.removeFromServerBlocklist({ server: servers[1].host })
409 })
410
411 it('Should include video files', async function () {
412 for (const path of paths) {
413 {
414 const videos = await listVideos({ server: servers[0], path })
415
416 for (const video of videos) {
417 const videoWithFiles = video as VideoDetails
418
419 expect(videoWithFiles.files).to.not.exist
420 expect(videoWithFiles.streamingPlaylists).to.not.exist
421 }
422 }
423
424 {
425 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.FILES })
426
427 for (const video of videos) {
428 const videoWithFiles = video as VideoDetails
429
430 expect(videoWithFiles.files).to.exist
431 expect(videoWithFiles.files).to.have.length.at.least(1)
432 }
433 }
434 }
435 })
436
437 it('Should filter by tags and category', async function () {
438 await servers[0].videos.upload({ attributes: { name: 'tag filter', tags: [ 'tag1', 'tag2' ] } })
439 await servers[0].videos.upload({ attributes: { name: 'tag filter with category', tags: [ 'tag3' ], category: 4 } })
440
441 for (const path of paths) {
442 {
443 const videos = await listVideos({ server: servers[0], path, tagsAllOf: [ 'tag1', 'tag2' ] })
444 expect(videos).to.have.lengthOf(1)
445 expect(videos[0].name).to.equal('tag filter')
446 }
447
448 {
449 const videos = await listVideos({ server: servers[0], path, tagsAllOf: [ 'tag1', 'tag3' ] })
450 expect(videos).to.have.lengthOf(0)
451 }
452
453 {
454 const { data, total } = await servers[0].videos.list({ tagsAllOf: [ 'tag3' ], categoryOneOf: [ 4 ] })
455 expect(total).to.equal(1)
456 expect(data[0].name).to.equal('tag filter with category')
457 }
458
459 {
460 const { total } = await servers[0].videos.list({ tagsAllOf: [ 'tag4' ], categoryOneOf: [ 4 ] })
461 expect(total).to.equal(0)
462 }
463 }
464 })
465
466 it('Should filter by HLS or WebTorrent files', async function () {
467 this.timeout(360000)
468
469 const finderFactory = (name: string) => (videos: Video[]) => videos.some(v => v.name === name)
470
471 await servers[0].config.enableTranscoding(true, false)
472 await servers[0].videos.upload({ attributes: { name: 'webtorrent video' } })
473 const hasWebtorrent = finderFactory('webtorrent video')
474
475 await waitJobs(servers)
476
477 await servers[0].config.enableTranscoding(false, true)
478 await servers[0].videos.upload({ attributes: { name: 'hls video' } })
479 const hasHLS = finderFactory('hls video')
480
481 await waitJobs(servers)
482
483 await servers[0].config.enableTranscoding(true, true)
484 await servers[0].videos.upload({ attributes: { name: 'hls and webtorrent video' } })
485 const hasBoth = finderFactory('hls and webtorrent video')
486
487 await waitJobs(servers)
488
489 for (const path of paths) {
490 {
491 const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: true })
492
493 expect(hasWebtorrent(videos)).to.be.true
494 expect(hasHLS(videos)).to.be.false
495 expect(hasBoth(videos)).to.be.true
496 }
497
498 {
499 const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: false })
500
501 expect(hasWebtorrent(videos)).to.be.false
502 expect(hasHLS(videos)).to.be.true
503 expect(hasBoth(videos)).to.be.false
504 }
505
506 {
507 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true })
508
509 expect(hasWebtorrent(videos)).to.be.false
510 expect(hasHLS(videos)).to.be.true
511 expect(hasBoth(videos)).to.be.true
512 }
513
514 {
515 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false })
516
517 expect(hasWebtorrent(videos)).to.be.true
518 expect(hasHLS(videos)).to.be.false
519 expect(hasBoth(videos)).to.be.false
520 }
521
522 {
523 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false, hasWebtorrentFiles: false })
524
525 expect(hasWebtorrent(videos)).to.be.false
526 expect(hasHLS(videos)).to.be.false
527 expect(hasBoth(videos)).to.be.false
528 }
529
530 {
531 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true, hasWebtorrentFiles: true })
532
533 expect(hasWebtorrent(videos)).to.be.false
534 expect(hasHLS(videos)).to.be.false
535 expect(hasBoth(videos)).to.be.true
536 }
537 }
538 })
539
540 it('Should filter already watched videos by the user', async function () {
541 const { id } = await servers[0].videos.upload({ attributes: { name: 'video for history' } })
542
543 for (const path of paths) {
544 const videos = await listVideos({ server: servers[0], path, isLocal: true, excludeAlreadyWatched: true })
545 const foundVideo = videos.find(video => video.id === id)
546
547 expect(foundVideo).to.not.be.undefined
548 }
549 await servers[0].views.view({ id, token: servers[0].accessToken })
550
551 for (const path of paths) {
552 const videos = await listVideos({ server: servers[0], path, excludeAlreadyWatched: true })
553 const foundVideo = videos.find(video => video.id === id)
554
555 expect(foundVideo).to.be.undefined
556 }
557 })
558 })
559
560 after(async function () {
561 await cleanupTests(servers)
562 })
563 })