aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests
diff options
context:
space:
mode:
Diffstat (limited to 'server/tests')
-rw-r--r--server/tests/api/check-params/blocklist.ts494
-rw-r--r--server/tests/api/check-params/index.ts3
-rw-r--r--server/tests/api/check-params/users.ts2
-rw-r--r--server/tests/api/check-params/video-channels.ts8
-rw-r--r--server/tests/api/check-params/video-imports.ts2
-rw-r--r--server/tests/api/check-params/videos-filter.ts127
-rw-r--r--server/tests/api/check-params/videos-history.ts79
-rw-r--r--server/tests/api/check-params/videos.ts4
-rw-r--r--server/tests/api/index-4.ts1
-rw-r--r--server/tests/api/index.ts1
-rw-r--r--server/tests/api/redundancy/index.ts1
-rw-r--r--server/tests/api/redundancy/redundancy.ts483
-rw-r--r--server/tests/api/server/follows.ts40
-rw-r--r--server/tests/api/server/index.ts1
-rw-r--r--server/tests/api/server/redundancy.ts20
-rw-r--r--server/tests/api/users/blocklist.ts511
-rw-r--r--server/tests/api/users/index.ts1
-rw-r--r--server/tests/api/users/users-multiple-servers.ts6
-rw-r--r--server/tests/api/users/users.ts36
-rw-r--r--server/tests/api/videos/index.ts2
-rw-r--r--server/tests/api/videos/multiple-servers.ts8
-rw-r--r--server/tests/api/videos/single-server.ts2
-rw-r--r--server/tests/api/videos/video-imports.ts2
-rw-r--r--server/tests/api/videos/video-transcoder.ts58
-rw-r--r--server/tests/api/videos/videos-filter.ts130
-rw-r--r--server/tests/api/videos/videos-history.ts128
-rw-r--r--server/tests/cli/optimize-old-videos.ts120
-rw-r--r--server/tests/helpers/core-utils.ts48
-rw-r--r--server/tests/helpers/index.ts1
-rw-r--r--server/tests/utils/miscs/miscs.ts35
-rw-r--r--server/tests/utils/requests/requests.ts4
-rw-r--r--server/tests/utils/server/follows.ts6
-rw-r--r--server/tests/utils/users/blocklist.ts198
-rw-r--r--server/tests/utils/users/users.ts3
-rw-r--r--server/tests/utils/videos/video-comments.ts14
-rw-r--r--server/tests/utils/videos/video-history.ts14
36 files changed, 2542 insertions, 51 deletions
diff --git a/server/tests/api/check-params/blocklist.ts b/server/tests/api/check-params/blocklist.ts
new file mode 100644
index 000000000..c745ac975
--- /dev/null
+++ b/server/tests/api/check-params/blocklist.ts
@@ -0,0 +1,494 @@
1/* tslint:disable:no-unused-expression */
2
3import 'mocha'
4
5import {
6 createUser,
7 doubleFollow,
8 flushAndRunMultipleServers,
9 flushTests,
10 killallServers,
11 makeDeleteRequest,
12 makeGetRequest,
13 makePostBodyRequest,
14 ServerInfo,
15 setAccessTokensToServers, userLogin
16} from '../../utils'
17import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
18
19describe('Test blocklist API validators', function () {
20 let servers: ServerInfo[]
21 let server: ServerInfo
22 let userAccessToken: string
23
24 before(async function () {
25 this.timeout(60000)
26
27 await flushTests()
28
29 servers = await flushAndRunMultipleServers(2)
30 await setAccessTokensToServers(servers)
31
32 server = servers[0]
33
34 const user = { username: 'user1', password: 'password' }
35 await createUser(server.url, server.accessToken, user.username, user.password)
36
37 userAccessToken = await userLogin(server, user)
38
39 await doubleFollow(servers[0], servers[1])
40 })
41
42 // ---------------------------------------------------------------
43
44 describe('When managing user blocklist', function () {
45
46 describe('When managing user accounts blocklist', function () {
47 const path = '/api/v1/users/me/blocklist/accounts'
48
49 describe('When listing blocked accounts', function () {
50 it('Should fail with an unauthenticated user', async function () {
51 await makeGetRequest({
52 url: server.url,
53 path,
54 statusCodeExpected: 401
55 })
56 })
57
58 it('Should fail with a bad start pagination', async function () {
59 await checkBadStartPagination(server.url, path, server.accessToken)
60 })
61
62 it('Should fail with a bad count pagination', async function () {
63 await checkBadCountPagination(server.url, path, server.accessToken)
64 })
65
66 it('Should fail with an incorrect sort', async function () {
67 await checkBadSortPagination(server.url, path, server.accessToken)
68 })
69 })
70
71 describe('When blocking an account', function () {
72 it('Should fail with an unauthenticated user', async function () {
73 await makePostBodyRequest({
74 url: server.url,
75 path,
76 fields: { accountName: 'user1' },
77 statusCodeExpected: 401
78 })
79 })
80
81 it('Should fail with an unknown account', async function () {
82 await makePostBodyRequest({
83 url: server.url,
84 token: server.accessToken,
85 path,
86 fields: { accountName: 'user2' },
87 statusCodeExpected: 404
88 })
89 })
90
91 it('Should fail to block ourselves', async function () {
92 await makePostBodyRequest({
93 url: server.url,
94 token: server.accessToken,
95 path,
96 fields: { accountName: 'root' },
97 statusCodeExpected: 409
98 })
99 })
100
101 it('Should succeed with the correct params', async function () {
102 await makePostBodyRequest({
103 url: server.url,
104 token: server.accessToken,
105 path,
106 fields: { accountName: 'user1' },
107 statusCodeExpected: 204
108 })
109 })
110 })
111
112 describe('When unblocking an account', function () {
113 it('Should fail with an unauthenticated user', async function () {
114 await makeDeleteRequest({
115 url: server.url,
116 path: path + '/user1',
117 statusCodeExpected: 401
118 })
119 })
120
121 it('Should fail with an unknown account block', async function () {
122 await makeDeleteRequest({
123 url: server.url,
124 path: path + '/user2',
125 token: server.accessToken,
126 statusCodeExpected: 404
127 })
128 })
129
130 it('Should succeed with the correct params', async function () {
131 await makeDeleteRequest({
132 url: server.url,
133 path: path + '/user1',
134 token: server.accessToken,
135 statusCodeExpected: 204
136 })
137 })
138 })
139 })
140
141 describe('When managing user servers blocklist', function () {
142 const path = '/api/v1/users/me/blocklist/servers'
143
144 describe('When listing blocked servers', function () {
145 it('Should fail with an unauthenticated user', async function () {
146 await makeGetRequest({
147 url: server.url,
148 path,
149 statusCodeExpected: 401
150 })
151 })
152
153 it('Should fail with a bad start pagination', async function () {
154 await checkBadStartPagination(server.url, path, server.accessToken)
155 })
156
157 it('Should fail with a bad count pagination', async function () {
158 await checkBadCountPagination(server.url, path, server.accessToken)
159 })
160
161 it('Should fail with an incorrect sort', async function () {
162 await checkBadSortPagination(server.url, path, server.accessToken)
163 })
164 })
165
166 describe('When blocking a server', function () {
167 it('Should fail with an unauthenticated user', async function () {
168 await makePostBodyRequest({
169 url: server.url,
170 path,
171 fields: { host: 'localhost:9002' },
172 statusCodeExpected: 401
173 })
174 })
175
176 it('Should fail with an unknown server', async function () {
177 await makePostBodyRequest({
178 url: server.url,
179 token: server.accessToken,
180 path,
181 fields: { host: 'localhost:9003' },
182 statusCodeExpected: 404
183 })
184 })
185
186 it('Should fail with our own server', async function () {
187 await makePostBodyRequest({
188 url: server.url,
189 token: server.accessToken,
190 path,
191 fields: { host: 'localhost:9001' },
192 statusCodeExpected: 409
193 })
194 })
195
196 it('Should succeed with the correct params', async function () {
197 await makePostBodyRequest({
198 url: server.url,
199 token: server.accessToken,
200 path,
201 fields: { host: 'localhost:9002' },
202 statusCodeExpected: 204
203 })
204 })
205 })
206
207 describe('When unblocking a server', function () {
208 it('Should fail with an unauthenticated user', async function () {
209 await makeDeleteRequest({
210 url: server.url,
211 path: path + '/localhost:9002',
212 statusCodeExpected: 401
213 })
214 })
215
216 it('Should fail with an unknown server block', async function () {
217 await makeDeleteRequest({
218 url: server.url,
219 path: path + '/localhost:9003',
220 token: server.accessToken,
221 statusCodeExpected: 404
222 })
223 })
224
225 it('Should succeed with the correct params', async function () {
226 await makeDeleteRequest({
227 url: server.url,
228 path: path + '/localhost:9002',
229 token: server.accessToken,
230 statusCodeExpected: 204
231 })
232 })
233 })
234 })
235 })
236
237 describe('When managing server blocklist', function () {
238
239 describe('When managing server accounts blocklist', function () {
240 const path = '/api/v1/server/blocklist/accounts'
241
242 describe('When listing blocked accounts', function () {
243 it('Should fail with an unauthenticated user', async function () {
244 await makeGetRequest({
245 url: server.url,
246 path,
247 statusCodeExpected: 401
248 })
249 })
250
251 it('Should fail with a user without the appropriate rights', async function () {
252 await makeGetRequest({
253 url: server.url,
254 token: userAccessToken,
255 path,
256 statusCodeExpected: 403
257 })
258 })
259
260 it('Should fail with a bad start pagination', async function () {
261 await checkBadStartPagination(server.url, path, server.accessToken)
262 })
263
264 it('Should fail with a bad count pagination', async function () {
265 await checkBadCountPagination(server.url, path, server.accessToken)
266 })
267
268 it('Should fail with an incorrect sort', async function () {
269 await checkBadSortPagination(server.url, path, server.accessToken)
270 })
271 })
272
273 describe('When blocking an account', function () {
274 it('Should fail with an unauthenticated user', async function () {
275 await makePostBodyRequest({
276 url: server.url,
277 path,
278 fields: { accountName: 'user1' },
279 statusCodeExpected: 401
280 })
281 })
282
283 it('Should fail with a user without the appropriate rights', async function () {
284 await makePostBodyRequest({
285 url: server.url,
286 token: userAccessToken,
287 path,
288 fields: { accountName: 'user1' },
289 statusCodeExpected: 403
290 })
291 })
292
293 it('Should fail with an unknown account', async function () {
294 await makePostBodyRequest({
295 url: server.url,
296 token: server.accessToken,
297 path,
298 fields: { accountName: 'user2' },
299 statusCodeExpected: 404
300 })
301 })
302
303 it('Should fail to block ourselves', async function () {
304 await makePostBodyRequest({
305 url: server.url,
306 token: server.accessToken,
307 path,
308 fields: { accountName: 'root' },
309 statusCodeExpected: 409
310 })
311 })
312
313 it('Should succeed with the correct params', async function () {
314 await makePostBodyRequest({
315 url: server.url,
316 token: server.accessToken,
317 path,
318 fields: { accountName: 'user1' },
319 statusCodeExpected: 204
320 })
321 })
322 })
323
324 describe('When unblocking an account', function () {
325 it('Should fail with an unauthenticated user', async function () {
326 await makeDeleteRequest({
327 url: server.url,
328 path: path + '/user1',
329 statusCodeExpected: 401
330 })
331 })
332
333 it('Should fail with a user without the appropriate rights', async function () {
334 await makeDeleteRequest({
335 url: server.url,
336 path: path + '/user1',
337 token: userAccessToken,
338 statusCodeExpected: 403
339 })
340 })
341
342 it('Should fail with an unknown account block', async function () {
343 await makeDeleteRequest({
344 url: server.url,
345 path: path + '/user2',
346 token: server.accessToken,
347 statusCodeExpected: 404
348 })
349 })
350
351 it('Should succeed with the correct params', async function () {
352 await makeDeleteRequest({
353 url: server.url,
354 path: path + '/user1',
355 token: server.accessToken,
356 statusCodeExpected: 204
357 })
358 })
359 })
360 })
361
362 describe('When managing server servers blocklist', function () {
363 const path = '/api/v1/server/blocklist/servers'
364
365 describe('When listing blocked servers', function () {
366 it('Should fail with an unauthenticated user', async function () {
367 await makeGetRequest({
368 url: server.url,
369 path,
370 statusCodeExpected: 401
371 })
372 })
373
374 it('Should fail with a user without the appropriate rights', async function () {
375 await makeGetRequest({
376 url: server.url,
377 token: userAccessToken,
378 path,
379 statusCodeExpected: 403
380 })
381 })
382
383 it('Should fail with a bad start pagination', async function () {
384 await checkBadStartPagination(server.url, path, server.accessToken)
385 })
386
387 it('Should fail with a bad count pagination', async function () {
388 await checkBadCountPagination(server.url, path, server.accessToken)
389 })
390
391 it('Should fail with an incorrect sort', async function () {
392 await checkBadSortPagination(server.url, path, server.accessToken)
393 })
394 })
395
396 describe('When blocking a server', function () {
397 it('Should fail with an unauthenticated user', async function () {
398 await makePostBodyRequest({
399 url: server.url,
400 path,
401 fields: { host: 'localhost:9002' },
402 statusCodeExpected: 401
403 })
404 })
405
406 it('Should fail with a user without the appropriate rights', async function () {
407 await makePostBodyRequest({
408 url: server.url,
409 token: userAccessToken,
410 path,
411 fields: { host: 'localhost:9002' },
412 statusCodeExpected: 403
413 })
414 })
415
416 it('Should fail with an unknown server', async function () {
417 await makePostBodyRequest({
418 url: server.url,
419 token: server.accessToken,
420 path,
421 fields: { host: 'localhost:9003' },
422 statusCodeExpected: 404
423 })
424 })
425
426 it('Should fail with our own server', async function () {
427 await makePostBodyRequest({
428 url: server.url,
429 token: server.accessToken,
430 path,
431 fields: { host: 'localhost:9001' },
432 statusCodeExpected: 409
433 })
434 })
435
436 it('Should succeed with the correct params', async function () {
437 await makePostBodyRequest({
438 url: server.url,
439 token: server.accessToken,
440 path,
441 fields: { host: 'localhost:9002' },
442 statusCodeExpected: 204
443 })
444 })
445 })
446
447 describe('When unblocking a server', function () {
448 it('Should fail with an unauthenticated user', async function () {
449 await makeDeleteRequest({
450 url: server.url,
451 path: path + '/localhost:9002',
452 statusCodeExpected: 401
453 })
454 })
455
456 it('Should fail with a user without the appropriate rights', async function () {
457 await makeDeleteRequest({
458 url: server.url,
459 path: path + '/localhost:9002',
460 token: userAccessToken,
461 statusCodeExpected: 403
462 })
463 })
464
465 it('Should fail with an unknown server block', async function () {
466 await makeDeleteRequest({
467 url: server.url,
468 path: path + '/localhost:9003',
469 token: server.accessToken,
470 statusCodeExpected: 404
471 })
472 })
473
474 it('Should succeed with the correct params', async function () {
475 await makeDeleteRequest({
476 url: server.url,
477 path: path + '/localhost:9002',
478 token: server.accessToken,
479 statusCodeExpected: 204
480 })
481 })
482 })
483 })
484 })
485
486 after(async function () {
487 killallServers(servers)
488
489 // Keep the logs if the test failed
490 if (this['ok']) {
491 await flushTests()
492 }
493 })
494})
diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts
index 44460a167..877ceb0a7 100644
--- a/server/tests/api/check-params/index.ts
+++ b/server/tests/api/check-params/index.ts
@@ -1,5 +1,6 @@
1// Order of the tests we want to execute 1// Order of the tests we want to execute
2import './accounts' 2import './accounts'
3import './blocklist'
3import './config' 4import './config'
4import './follows' 5import './follows'
5import './jobs' 6import './jobs'
@@ -15,3 +16,5 @@ import './video-channels'
15import './video-comments' 16import './video-comments'
16import './video-imports' 17import './video-imports'
17import './videos' 18import './videos'
19import './videos-filter'
20import './videos-history'
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index cbfa0c137..ec46609a4 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -315,7 +315,7 @@ describe('Test users API validators', function () {
315 315
316 it('Should fail with a too long description', async function () { 316 it('Should fail with a too long description', async function () {
317 const fields = { 317 const fields = {
318 description: 'super'.repeat(60) 318 description: 'super'.repeat(201)
319 } 319 }
320 320
321 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 321 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index 3a7942945..e5696224d 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -118,12 +118,12 @@ describe('Test video channels API validator', function () {
118 }) 118 })
119 119
120 it('Should fail with a long description', async function () { 120 it('Should fail with a long description', async function () {
121 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(150) }) 121 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(201) })
122 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 122 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
123 }) 123 })
124 124
125 it('Should fail with a long support text', async function () { 125 it('Should fail with a long support text', async function () {
126 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(150) }) 126 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) })
127 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 127 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
128 }) 128 })
129 129
@@ -185,12 +185,12 @@ describe('Test video channels API validator', function () {
185 }) 185 })
186 186
187 it('Should fail with a long description', async function () { 187 it('Should fail with a long description', async function () {
188 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(150) }) 188 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(201) })
189 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 189 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
190 }) 190 })
191 191
192 it('Should fail with a long support text', async function () { 192 it('Should fail with a long support text', async function () {
193 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(150) }) 193 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) })
194 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 194 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
195 }) 195 })
196 196
diff --git a/server/tests/api/check-params/video-imports.ts b/server/tests/api/check-params/video-imports.ts
index 44645b0e2..b51f3d2cd 100644
--- a/server/tests/api/check-params/video-imports.ts
+++ b/server/tests/api/check-params/video-imports.ts
@@ -140,7 +140,7 @@ describe('Test video imports API validator', function () {
140 }) 140 })
141 141
142 it('Should fail with a long support text', async function () { 142 it('Should fail with a long support text', async function () {
143 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(150) }) 143 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) })
144 144
145 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 145 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
146 }) 146 })
diff --git a/server/tests/api/check-params/videos-filter.ts b/server/tests/api/check-params/videos-filter.ts
new file mode 100644
index 000000000..784cd8ba1
--- /dev/null
+++ b/server/tests/api/check-params/videos-filter.ts
@@ -0,0 +1,127 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 createUser,
7 flushTests,
8 killallServers,
9 makeGetRequest,
10 runServer,
11 ServerInfo,
12 setAccessTokensToServers,
13 userLogin
14} from '../../utils'
15import { UserRole } from '../../../../shared/models/users'
16
17const expect = chai.expect
18
19async function testEndpoints (server: ServerInfo, token: string, filter: string, statusCodeExpected: number) {
20 const paths = [
21 '/api/v1/video-channels/root_channel/videos',
22 '/api/v1/accounts/root/videos',
23 '/api/v1/videos',
24 '/api/v1/search/videos'
25 ]
26
27 for (const path of paths) {
28 await makeGetRequest({
29 url: server.url,
30 path,
31 token,
32 query: {
33 filter
34 },
35 statusCodeExpected
36 })
37 }
38}
39
40describe('Test videos filters', function () {
41 let server: ServerInfo
42 let userAccessToken: string
43 let moderatorAccessToken: string
44
45 // ---------------------------------------------------------------
46
47 before(async function () {
48 this.timeout(30000)
49
50 await flushTests()
51
52 server = await runServer(1)
53
54 await setAccessTokensToServers([ server ])
55
56 const user = { username: 'user1', password: 'my super password' }
57 await createUser(server.url, server.accessToken, user.username, user.password)
58 userAccessToken = await userLogin(server, user)
59
60 const moderator = { username: 'moderator', password: 'my super password' }
61 await createUser(
62 server.url,
63 server.accessToken,
64 moderator.username,
65 moderator.password,
66 undefined,
67 undefined,
68 UserRole.MODERATOR
69 )
70 moderatorAccessToken = await userLogin(server, moderator)
71 })
72
73 describe('When setting a video filter', function () {
74
75 it('Should fail with a bad filter', async function () {
76 await testEndpoints(server, server.accessToken, 'bad-filter', 400)
77 })
78
79 it('Should succeed with a good filter', async function () {
80 await testEndpoints(server, server.accessToken,'local', 200)
81 })
82
83 it('Should fail to list all-local with a simple user', async function () {
84 await testEndpoints(server, userAccessToken, 'all-local', 401)
85 })
86
87 it('Should succeed to list all-local with a moderator', async function () {
88 await testEndpoints(server, moderatorAccessToken, 'all-local', 200)
89 })
90
91 it('Should succeed to list all-local with an admin', async function () {
92 await testEndpoints(server, server.accessToken, 'all-local', 200)
93 })
94
95 // Because we cannot authenticate the user on the RSS endpoint
96 it('Should fail on the feeds endpoint with the all-local filter', async function () {
97 await makeGetRequest({
98 url: server.url,
99 path: '/feeds/videos.json',
100 statusCodeExpected: 401,
101 query: {
102 filter: 'all-local'
103 }
104 })
105 })
106
107 it('Should succed on the feeds endpoint with the local filter', async function () {
108 await makeGetRequest({
109 url: server.url,
110 path: '/feeds/videos.json',
111 statusCodeExpected: 200,
112 query: {
113 filter: 'local'
114 }
115 })
116 })
117 })
118
119 after(async function () {
120 killallServers([ server ])
121
122 // Keep the logs if the test failed
123 if (this['ok']) {
124 await flushTests()
125 }
126 })
127})
diff --git a/server/tests/api/check-params/videos-history.ts b/server/tests/api/check-params/videos-history.ts
new file mode 100644
index 000000000..808c3b616
--- /dev/null
+++ b/server/tests/api/check-params/videos-history.ts
@@ -0,0 +1,79 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 flushTests,
7 killallServers,
8 makePostBodyRequest,
9 makePutBodyRequest,
10 runServer,
11 ServerInfo,
12 setAccessTokensToServers,
13 uploadVideo
14} from '../../utils'
15
16const expect = chai.expect
17
18describe('Test videos history API validator', function () {
19 let path: string
20 let server: ServerInfo
21
22 // ---------------------------------------------------------------
23
24 before(async function () {
25 this.timeout(30000)
26
27 await flushTests()
28
29 server = await runServer(1)
30
31 await setAccessTokensToServers([ server ])
32
33 const res = await uploadVideo(server.url, server.accessToken, {})
34 const videoUUID = res.body.video.uuid
35
36 path = '/api/v1/videos/' + videoUUID + '/watching'
37 })
38
39 describe('When notifying a user is watching a video', function () {
40
41 it('Should fail with an unauthenticated user', async function () {
42 const fields = { currentTime: 5 }
43 await makePutBodyRequest({ url: server.url, path, fields, statusCodeExpected: 401 })
44 })
45
46 it('Should fail with an incorrect video id', async function () {
47 const fields = { currentTime: 5 }
48 const path = '/api/v1/videos/blabla/watching'
49 await makePutBodyRequest({ url: server.url, path, fields, token: server.accessToken, statusCodeExpected: 400 })
50 })
51
52 it('Should fail with an unknown video', async function () {
53 const fields = { currentTime: 5 }
54 const path = '/api/v1/videos/d91fff41-c24d-4508-8e13-3bd5902c3b02/watching'
55
56 await makePutBodyRequest({ url: server.url, path, fields, token: server.accessToken, statusCodeExpected: 404 })
57 })
58
59 it('Should fail with a bad current time', async function () {
60 const fields = { currentTime: 'hello' }
61 await makePutBodyRequest({ url: server.url, path, fields, token: server.accessToken, statusCodeExpected: 400 })
62 })
63
64 it('Should succeed with the correct parameters', async function () {
65 const fields = { currentTime: 5 }
66
67 await makePutBodyRequest({ url: server.url, path, fields, token: server.accessToken, statusCodeExpected: 204 })
68 })
69 })
70
71 after(async function () {
72 killallServers([ server ])
73
74 // Keep the logs if the test failed
75 if (this['ok']) {
76 await flushTests()
77 }
78 })
79})
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index 904d22870..699f135c7 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -233,7 +233,7 @@ describe('Test videos API validator', function () {
233 }) 233 })
234 234
235 it('Should fail with a long support text', async function () { 235 it('Should fail with a long support text', async function () {
236 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(150) }) 236 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) })
237 const attaches = baseCorrectAttaches 237 const attaches = baseCorrectAttaches
238 238
239 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) 239 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
@@ -482,7 +482,7 @@ describe('Test videos API validator', function () {
482 }) 482 })
483 483
484 it('Should fail with a long support text', async function () { 484 it('Should fail with a long support text', async function () {
485 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(150) }) 485 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) })
486 486
487 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) 487 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
488 }) 488 })
diff --git a/server/tests/api/index-4.ts b/server/tests/api/index-4.ts
new file mode 100644
index 000000000..8e69b95a6
--- /dev/null
+++ b/server/tests/api/index-4.ts
@@ -0,0 +1 @@
import './redundancy'
diff --git a/server/tests/api/index.ts b/server/tests/api/index.ts
index 2d996dbf9..bc140f860 100644
--- a/server/tests/api/index.ts
+++ b/server/tests/api/index.ts
@@ -2,3 +2,4 @@
2import './index-1' 2import './index-1'
3import './index-2' 3import './index-2'
4import './index-3' 4import './index-3'
5import './index-4'
diff --git a/server/tests/api/redundancy/index.ts b/server/tests/api/redundancy/index.ts
new file mode 100644
index 000000000..8e69b95a6
--- /dev/null
+++ b/server/tests/api/redundancy/index.ts
@@ -0,0 +1 @@
import './redundancy'
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts
new file mode 100644
index 000000000..1960854b6
--- /dev/null
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -0,0 +1,483 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import { VideoDetails } from '../../../../shared/models/videos'
6import {
7 doubleFollow,
8 flushAndRunMultipleServers,
9 getFollowingListPaginationAndSort,
10 getVideo,
11 immutableAssign,
12 killallServers, makeGetRequest,
13 root,
14 ServerInfo,
15 setAccessTokensToServers, unfollow,
16 uploadVideo,
17 viewVideo,
18 wait,
19 waitUntilLog,
20 checkVideoFilesWereRemoved, removeVideo
21} from '../../utils'
22import { waitJobs } from '../../utils/server/jobs'
23import * as magnetUtil from 'magnet-uri'
24import { updateRedundancy } from '../../utils/server/redundancy'
25import { ActorFollow } from '../../../../shared/models/actors'
26import { readdir } from 'fs-extra'
27import { join } from 'path'
28import { VideoRedundancyStrategy } from '../../../../shared/models/redundancy'
29import { getStats } from '../../utils/server/stats'
30import { ServerStats } from '../../../../shared/models/server/server-stats.model'
31
32const expect = chai.expect
33
34let servers: ServerInfo[] = []
35let video1Server2UUID: string
36
37function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: ServerInfo) {
38 const parsed = magnetUtil.decode(file.magnetUri)
39
40 for (const ws of baseWebseeds) {
41 const found = parsed.urlList.find(url => url === `${ws}-${file.resolution.id}.mp4`)
42 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined
43 }
44
45 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length)
46}
47
48async function runServers (strategy: VideoRedundancyStrategy, additionalParams: any = {}) {
49 const config = {
50 redundancy: {
51 videos: {
52 check_interval: '5 seconds',
53 strategies: [
54 immutableAssign({
55 min_lifetime: '1 hour',
56 strategy: strategy,
57 size: '100KB'
58 }, additionalParams)
59 ]
60 }
61 }
62 }
63 servers = await flushAndRunMultipleServers(3, config)
64
65 // Get the access tokens
66 await setAccessTokensToServers(servers)
67
68 {
69 const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, { name: 'video 1 server 2' })
70 video1Server2UUID = res.body.video.uuid
71
72 await viewVideo(servers[ 1 ].url, video1Server2UUID)
73 }
74
75 await waitJobs(servers)
76
77 // Server 1 and server 2 follow each other
78 await doubleFollow(servers[ 0 ], servers[ 1 ])
79 // Server 1 and server 3 follow each other
80 await doubleFollow(servers[ 0 ], servers[ 2 ])
81 // Server 2 and server 3 follow each other
82 await doubleFollow(servers[ 1 ], servers[ 2 ])
83
84 await waitJobs(servers)
85}
86
87async function check1WebSeed (strategy: VideoRedundancyStrategy, videoUUID?: string) {
88 if (!videoUUID) videoUUID = video1Server2UUID
89
90 const webseeds = [
91 'http://localhost:9002/static/webseed/' + videoUUID
92 ]
93
94 for (const server of servers) {
95 {
96 const res = await getVideo(server.url, videoUUID)
97
98 const video: VideoDetails = res.body
99 for (const f of video.files) {
100 checkMagnetWebseeds(f, webseeds, server)
101 }
102 }
103 }
104}
105
106async function checkStatsWith2Webseed (strategy: VideoRedundancyStrategy) {
107 const res = await getStats(servers[0].url)
108 const data: ServerStats = res.body
109
110 expect(data.videosRedundancy).to.have.lengthOf(1)
111 const stat = data.videosRedundancy[0]
112
113 expect(stat.strategy).to.equal(strategy)
114 expect(stat.totalSize).to.equal(102400)
115 expect(stat.totalUsed).to.be.at.least(1).and.below(102401)
116 expect(stat.totalVideoFiles).to.equal(4)
117 expect(stat.totalVideos).to.equal(1)
118}
119
120async function checkStatsWith1Webseed (strategy: VideoRedundancyStrategy) {
121 const res = await getStats(servers[0].url)
122 const data: ServerStats = res.body
123
124 expect(data.videosRedundancy).to.have.lengthOf(1)
125
126 const stat = data.videosRedundancy[0]
127 expect(stat.strategy).to.equal(strategy)
128 expect(stat.totalSize).to.equal(102400)
129 expect(stat.totalUsed).to.equal(0)
130 expect(stat.totalVideoFiles).to.equal(0)
131 expect(stat.totalVideos).to.equal(0)
132}
133
134async function check2Webseeds (strategy: VideoRedundancyStrategy, videoUUID?: string) {
135 if (!videoUUID) videoUUID = video1Server2UUID
136
137 const webseeds = [
138 'http://localhost:9001/static/webseed/' + videoUUID,
139 'http://localhost:9002/static/webseed/' + videoUUID
140 ]
141
142 for (const server of servers) {
143 const res = await getVideo(server.url, videoUUID)
144
145 const video: VideoDetails = res.body
146
147 for (const file of video.files) {
148 checkMagnetWebseeds(file, webseeds, server)
149
150 // Only servers 1 and 2 have the video
151 if (server.serverNumber !== 3) {
152 await makeGetRequest({
153 url: server.url,
154 statusCodeExpected: 200,
155 path: '/static/webseed/' + `${videoUUID}-${file.resolution.id}.mp4`,
156 contentType: null
157 })
158 }
159 }
160 }
161
162 for (const directory of [ 'test1', 'test2' ]) {
163 const files = await readdir(join(root(), directory, 'videos'))
164 expect(files).to.have.length.at.least(4)
165
166 for (const resolution of [ 240, 360, 480, 720 ]) {
167 expect(files.find(f => f === `${videoUUID}-${resolution}.mp4`)).to.not.be.undefined
168 }
169 }
170}
171
172async function enableRedundancyOnServer1 () {
173 await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, true)
174
175 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt')
176 const follows: ActorFollow[] = res.body.data
177 const server2 = follows.find(f => f.following.host === 'localhost:9002')
178 const server3 = follows.find(f => f.following.host === 'localhost:9003')
179
180 expect(server3).to.not.be.undefined
181 expect(server3.following.hostRedundancyAllowed).to.be.false
182
183 expect(server2).to.not.be.undefined
184 expect(server2.following.hostRedundancyAllowed).to.be.true
185}
186
187async function disableRedundancyOnServer1 () {
188 await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, false)
189
190 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt')
191 const follows: ActorFollow[] = res.body.data
192 const server2 = follows.find(f => f.following.host === 'localhost:9002')
193 const server3 = follows.find(f => f.following.host === 'localhost:9003')
194
195 expect(server3).to.not.be.undefined
196 expect(server3.following.hostRedundancyAllowed).to.be.false
197
198 expect(server2).to.not.be.undefined
199 expect(server2.following.hostRedundancyAllowed).to.be.false
200}
201
202async function cleanServers () {
203 killallServers(servers)
204}
205
206describe('Test videos redundancy', function () {
207
208 describe('With most-views strategy', function () {
209 const strategy = 'most-views'
210
211 before(function () {
212 this.timeout(120000)
213
214 return runServers(strategy)
215 })
216
217 it('Should have 1 webseed on the first video', async function () {
218 await check1WebSeed(strategy)
219 await checkStatsWith1Webseed(strategy)
220 })
221
222 it('Should enable redundancy on server 1', function () {
223 return enableRedundancyOnServer1()
224 })
225
226 it('Should have 2 webseed on the first video', async function () {
227 this.timeout(40000)
228
229 await waitJobs(servers)
230 await waitUntilLog(servers[0], 'Duplicated ', 4)
231 await waitJobs(servers)
232
233 await check2Webseeds(strategy)
234 await checkStatsWith2Webseed(strategy)
235 })
236
237 it('Should undo redundancy on server 1 and remove duplicated videos', async function () {
238 this.timeout(40000)
239
240 await disableRedundancyOnServer1()
241
242 await waitJobs(servers)
243 await wait(5000)
244
245 await check1WebSeed(strategy)
246
247 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].serverNumber, [ 'videos' ])
248 })
249
250 after(function () {
251 return cleanServers()
252 })
253 })
254
255 describe('With trending strategy', function () {
256 const strategy = 'trending'
257
258 before(function () {
259 this.timeout(120000)
260
261 return runServers(strategy)
262 })
263
264 it('Should have 1 webseed on the first video', async function () {
265 await check1WebSeed(strategy)
266 await checkStatsWith1Webseed(strategy)
267 })
268
269 it('Should enable redundancy on server 1', function () {
270 return enableRedundancyOnServer1()
271 })
272
273 it('Should have 2 webseed on the first video', async function () {
274 this.timeout(40000)
275
276 await waitJobs(servers)
277 await waitUntilLog(servers[0], 'Duplicated ', 4)
278 await waitJobs(servers)
279
280 await check2Webseeds(strategy)
281 await checkStatsWith2Webseed(strategy)
282 })
283
284 it('Should unfollow on server 1 and remove duplicated videos', async function () {
285 this.timeout(40000)
286
287 await unfollow(servers[0].url, servers[0].accessToken, servers[1])
288
289 await waitJobs(servers)
290 await wait(5000)
291
292 await check1WebSeed(strategy)
293
294 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].serverNumber, [ 'videos' ])
295 })
296
297 after(function () {
298 return cleanServers()
299 })
300 })
301
302 describe('With recently added strategy', function () {
303 const strategy = 'recently-added'
304
305 before(function () {
306 this.timeout(120000)
307
308 return runServers(strategy, { min_views: 3 })
309 })
310
311 it('Should have 1 webseed on the first video', async function () {
312 await check1WebSeed(strategy)
313 await checkStatsWith1Webseed(strategy)
314 })
315
316 it('Should enable redundancy on server 1', function () {
317 return enableRedundancyOnServer1()
318 })
319
320 it('Should still have 1 webseed on the first video', async function () {
321 this.timeout(40000)
322
323 await waitJobs(servers)
324 await wait(15000)
325 await waitJobs(servers)
326
327 await check1WebSeed(strategy)
328 await checkStatsWith1Webseed(strategy)
329 })
330
331 it('Should view 2 times the first video to have > min_views config', async function () {
332 this.timeout(40000)
333
334 await viewVideo(servers[ 0 ].url, video1Server2UUID)
335 await viewVideo(servers[ 2 ].url, video1Server2UUID)
336
337 await wait(10000)
338 await waitJobs(servers)
339 })
340
341 it('Should have 2 webseed on the first video', async function () {
342 this.timeout(40000)
343
344 await waitJobs(servers)
345 await waitUntilLog(servers[0], 'Duplicated ', 4)
346 await waitJobs(servers)
347
348 await check2Webseeds(strategy)
349 await checkStatsWith2Webseed(strategy)
350 })
351
352 it('Should remove the video and the redundancy files', async function () {
353 this.timeout(20000)
354
355 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID)
356
357 await waitJobs(servers)
358
359 for (const server of servers) {
360 await checkVideoFilesWereRemoved(video1Server2UUID, server.serverNumber)
361 }
362 })
363
364 after(function () {
365 return cleanServers()
366 })
367 })
368
369 describe('Test expiration', function () {
370 const strategy = 'recently-added'
371
372 async function checkContains (servers: ServerInfo[], str: string) {
373 for (const server of servers) {
374 const res = await getVideo(server.url, video1Server2UUID)
375 const video: VideoDetails = res.body
376
377 for (const f of video.files) {
378 expect(f.magnetUri).to.contain(str)
379 }
380 }
381 }
382
383 async function checkNotContains (servers: ServerInfo[], str: string) {
384 for (const server of servers) {
385 const res = await getVideo(server.url, video1Server2UUID)
386 const video: VideoDetails = res.body
387
388 for (const f of video.files) {
389 expect(f.magnetUri).to.not.contain(str)
390 }
391 }
392 }
393
394 before(async function () {
395 this.timeout(120000)
396
397 await runServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
398
399 await enableRedundancyOnServer1()
400 })
401
402 it('Should still have 2 webseeds after 10 seconds', async function () {
403 this.timeout(40000)
404
405 await wait(10000)
406
407 try {
408 await checkContains(servers, 'http%3A%2F%2Flocalhost%3A9001')
409 } catch {
410 // Maybe a server deleted a redundancy in the scheduler
411 await wait(2000)
412
413 await checkContains(servers, 'http%3A%2F%2Flocalhost%3A9001')
414 }
415 })
416
417 it('Should stop server 1 and expire video redundancy', async function () {
418 this.timeout(40000)
419
420 killallServers([ servers[0] ])
421
422 await wait(10000)
423
424 await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A9001')
425 })
426
427 after(function () {
428 return killallServers([ servers[1], servers[2] ])
429 })
430 })
431
432 describe('Test file replacement', function () {
433 let video2Server2UUID: string
434 const strategy = 'recently-added'
435
436 before(async function () {
437 this.timeout(120000)
438
439 await runServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
440
441 await enableRedundancyOnServer1()
442
443 await waitJobs(servers)
444 await waitUntilLog(servers[0], 'Duplicated ', 4)
445 await waitJobs(servers)
446
447 await check2Webseeds(strategy)
448 await checkStatsWith2Webseed(strategy)
449
450 const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, { name: 'video 2 server 2' })
451 video2Server2UUID = res.body.video.uuid
452 })
453
454 it('Should cache video 2 webseed on the first video', async function () {
455 this.timeout(50000)
456
457 await waitJobs(servers)
458
459 await wait(7000)
460
461 try {
462 await check1WebSeed(strategy, video1Server2UUID)
463 await check2Webseeds(strategy, video2Server2UUID)
464 } catch {
465 await wait(3000)
466
467 try {
468 await check1WebSeed(strategy, video1Server2UUID)
469 await check2Webseeds(strategy, video2Server2UUID)
470 } catch {
471 await wait(5000)
472
473 await check1WebSeed(strategy, video1Server2UUID)
474 await check2Webseeds(strategy, video2Server2UUID)
475 }
476 }
477 })
478
479 after(function () {
480 return cleanServers()
481 })
482 })
483})
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 310c291bf..e80e93e7f 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -93,7 +93,26 @@ describe('Test follows', function () {
93 expect(server3Follow.state).to.equal('accepted') 93 expect(server3Follow.state).to.equal('accepted')
94 }) 94 })
95 95
96 it('Should have 0 followings on server 1 and 2', async function () { 96 it('Should search followings on server 1', async function () {
97 {
98 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':9002')
99 const follows = res.body.data
100
101 expect(res.body.total).to.equal(1)
102 expect(follows.length).to.equal(1)
103 expect(follows[ 0 ].following.host).to.equal('localhost:9002')
104 }
105
106 {
107 const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', 'bla')
108 const follows = res.body.data
109
110 expect(res.body.total).to.equal(0)
111 expect(follows.length).to.equal(0)
112 }
113 })
114
115 it('Should have 0 followings on server 2 and 3', async function () {
97 for (const server of [ servers[1], servers[2] ]) { 116 for (const server of [ servers[1], servers[2] ]) {
98 const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt') 117 const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
99 const follows = res.body.data 118 const follows = res.body.data
@@ -116,6 +135,25 @@ describe('Test follows', function () {
116 } 135 }
117 }) 136 })
118 137
138 it('Should search followers on server 2', async function () {
139 {
140 const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', '9001')
141 const follows = res.body.data
142
143 expect(res.body.total).to.equal(1)
144 expect(follows.length).to.equal(1)
145 expect(follows[ 0 ].following.host).to.equal('localhost:9003')
146 }
147
148 {
149 const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', 'bla')
150 const follows = res.body.data
151
152 expect(res.body.total).to.equal(0)
153 expect(follows.length).to.equal(0)
154 }
155 })
156
119 it('Should have 0 followers on server 1', async function () { 157 it('Should have 0 followers on server 1', async function () {
120 const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt') 158 const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
121 const follows = res.body.data 159 const follows = res.body.data
diff --git a/server/tests/api/server/index.ts b/server/tests/api/server/index.ts
index c74c68a33..eeb8b7a28 100644
--- a/server/tests/api/server/index.ts
+++ b/server/tests/api/server/index.ts
@@ -3,7 +3,6 @@ import './email'
3import './follows' 3import './follows'
4import './handle-down' 4import './handle-down'
5import './jobs' 5import './jobs'
6import './redundancy'
7import './reverse-proxy' 6import './reverse-proxy'
8import './stats' 7import './stats'
9import './tracker' 8import './tracker'
diff --git a/server/tests/api/server/redundancy.ts b/server/tests/api/server/redundancy.ts
index 1960854b6..f50d6e3cf 100644
--- a/server/tests/api/server/redundancy.ts
+++ b/server/tests/api/server/redundancy.ts
@@ -419,7 +419,7 @@ describe('Test videos redundancy', function () {
419 419
420 killallServers([ servers[0] ]) 420 killallServers([ servers[0] ])
421 421
422 await wait(10000) 422 await wait(15000)
423 423
424 await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A9001') 424 await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A9001')
425 }) 425 })
@@ -452,26 +452,22 @@ describe('Test videos redundancy', function () {
452 }) 452 })
453 453
454 it('Should cache video 2 webseed on the first video', async function () { 454 it('Should cache video 2 webseed on the first video', async function () {
455 this.timeout(50000) 455 this.timeout(120000)
456 456
457 await waitJobs(servers) 457 await waitJobs(servers)
458 458
459 await wait(7000) 459 let checked = false
460 460
461 try { 461 while (checked === false) {
462 await check1WebSeed(strategy, video1Server2UUID) 462 await wait(1000)
463 await check2Webseeds(strategy, video2Server2UUID)
464 } catch {
465 await wait(3000)
466 463
467 try { 464 try {
468 await check1WebSeed(strategy, video1Server2UUID) 465 await check1WebSeed(strategy, video1Server2UUID)
469 await check2Webseeds(strategy, video2Server2UUID) 466 await check2Webseeds(strategy, video2Server2UUID)
470 } catch {
471 await wait(5000)
472 467
473 await check1WebSeed(strategy, video1Server2UUID) 468 checked = true
474 await check2Webseeds(strategy, video2Server2UUID) 469 } catch {
470 checked = false
475 } 471 }
476 } 472 }
477 }) 473 })
diff --git a/server/tests/api/users/blocklist.ts b/server/tests/api/users/blocklist.ts
new file mode 100644
index 000000000..eed4b9f3e
--- /dev/null
+++ b/server/tests/api/users/blocklist.ts
@@ -0,0 +1,511 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import { AccountBlock, ServerBlock, Video } from '../../../../shared/index'
6import {
7 createUser,
8 doubleFollow,
9 flushAndRunMultipleServers,
10 flushTests,
11 killallServers,
12 ServerInfo,
13 uploadVideo,
14 userLogin
15} from '../../utils/index'
16import { setAccessTokensToServers } from '../../utils/users/login'
17import { getVideosListWithToken, getVideosList } from '../../utils/videos/videos'
18import {
19 addVideoCommentReply,
20 addVideoCommentThread,
21 getVideoCommentThreads,
22 getVideoThreadComments
23} from '../../utils/videos/video-comments'
24import { waitJobs } from '../../utils/server/jobs'
25import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
26import {
27 addAccountToAccountBlocklist,
28 addAccountToServerBlocklist,
29 addServerToAccountBlocklist,
30 addServerToServerBlocklist,
31 getAccountBlocklistByAccount,
32 getAccountBlocklistByServer,
33 getServerBlocklistByAccount,
34 getServerBlocklistByServer,
35 removeAccountFromAccountBlocklist,
36 removeAccountFromServerBlocklist,
37 removeServerFromAccountBlocklist,
38 removeServerFromServerBlocklist
39} from '../../utils/users/blocklist'
40
41const expect = chai.expect
42
43async function checkAllVideos (url: string, token: string) {
44 {
45 const res = await getVideosListWithToken(url, token)
46
47 expect(res.body.data).to.have.lengthOf(4)
48 }
49
50 {
51 const res = await getVideosList(url)
52
53 expect(res.body.data).to.have.lengthOf(4)
54 }
55}
56
57async function checkAllComments (url: string, token: string, videoUUID: string) {
58 const resThreads = await getVideoCommentThreads(url, videoUUID, 0, 5, '-createdAt', token)
59
60 const threads: VideoComment[] = resThreads.body.data
61 expect(threads).to.have.lengthOf(2)
62
63 for (const thread of threads) {
64 const res = await getVideoThreadComments(url, videoUUID, thread.id, token)
65
66 const tree: VideoCommentThreadTree = res.body
67 expect(tree.children).to.have.lengthOf(1)
68 }
69}
70
71describe('Test blocklist', function () {
72 let servers: ServerInfo[]
73 let videoUUID1: string
74 let videoUUID2: string
75 let userToken1: string
76 let userModeratorToken: string
77 let userToken2: string
78
79 before(async function () {
80 this.timeout(60000)
81
82 await flushTests()
83
84 servers = await flushAndRunMultipleServers(2)
85 await setAccessTokensToServers(servers)
86
87 {
88 const user = { username: 'user1', password: 'password' }
89 await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
90
91 userToken1 = await userLogin(servers[0], user)
92 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' })
93 }
94
95 {
96 const user = { username: 'moderator', password: 'password' }
97 await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
98
99 userModeratorToken = await userLogin(servers[0], user)
100 }
101
102 {
103 const user = { username: 'user2', password: 'password' }
104 await createUser(servers[1].url, servers[1].accessToken, user.username, user.password)
105
106 userToken2 = await userLogin(servers[1], user)
107 await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' })
108 }
109
110 {
111 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' })
112 videoUUID1 = res.body.video.uuid
113 }
114
115 {
116 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' })
117 videoUUID2 = res.body.video.uuid
118 }
119
120 await doubleFollow(servers[0], servers[1])
121
122 {
123 const resComment = await addVideoCommentThread(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1, 'comment root 1')
124 const resReply = await addVideoCommentReply(servers[ 0 ].url, userToken1, videoUUID1, resComment.body.comment.id, 'comment user 1')
125 await addVideoCommentReply(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1, resReply.body.comment.id, 'comment root 1')
126 }
127
128 {
129 const resComment = await addVideoCommentThread(servers[ 0 ].url, userToken1, videoUUID1, 'comment user 1')
130 await addVideoCommentReply(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1, resComment.body.comment.id, 'comment root 1')
131 }
132
133 await waitJobs(servers)
134 })
135
136 describe('User blocklist', function () {
137
138 describe('When managing account blocklist', function () {
139 it('Should list all videos', function () {
140 return checkAllVideos(servers[ 0 ].url, servers[ 0 ].accessToken)
141 })
142
143 it('Should list the comments', function () {
144 return checkAllComments(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1)
145 })
146
147 it('Should block a remote account', async function () {
148 await addAccountToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002')
149 })
150
151 it('Should hide its videos', async function () {
152 const res = await getVideosListWithToken(servers[ 0 ].url, servers[ 0 ].accessToken)
153
154 const videos: Video[] = res.body.data
155 expect(videos).to.have.lengthOf(3)
156
157 const v = videos.find(v => v.name === 'video user 2')
158 expect(v).to.be.undefined
159 })
160
161 it('Should block a local account', async function () {
162 await addAccountToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user1')
163 })
164
165 it('Should hide its videos', async function () {
166 const res = await getVideosListWithToken(servers[ 0 ].url, servers[ 0 ].accessToken)
167
168 const videos: Video[] = res.body.data
169 expect(videos).to.have.lengthOf(2)
170
171 const v = videos.find(v => v.name === 'video user 1')
172 expect(v).to.be.undefined
173 })
174
175 it('Should hide its comments', async function () {
176 const resThreads = await getVideoCommentThreads(servers[ 0 ].url, videoUUID1, 0, 5, '-createdAt', servers[ 0 ].accessToken)
177
178 const threads: VideoComment[] = resThreads.body.data
179 expect(threads).to.have.lengthOf(1)
180 expect(threads[ 0 ].totalReplies).to.equal(0)
181
182 const t = threads.find(t => t.text === 'comment user 1')
183 expect(t).to.be.undefined
184
185 for (const thread of threads) {
186 const res = await getVideoThreadComments(servers[ 0 ].url, videoUUID1, thread.id, servers[ 0 ].accessToken)
187
188 const tree: VideoCommentThreadTree = res.body
189 expect(tree.children).to.have.lengthOf(0)
190 }
191 })
192
193 it('Should list all the videos with another user', async function () {
194 return checkAllVideos(servers[ 0 ].url, userToken1)
195 })
196
197 it('Should list all the comments with another user', async function () {
198 return checkAllComments(servers[ 0 ].url, userToken1, videoUUID1)
199 })
200
201 it('Should list blocked accounts', async function () {
202 {
203 const res = await getAccountBlocklistByAccount(servers[ 0 ].url, servers[ 0 ].accessToken, 0, 1, 'createdAt')
204 const blocks: AccountBlock[] = res.body.data
205
206 expect(res.body.total).to.equal(2)
207
208 const block = blocks[ 0 ]
209 expect(block.byAccount.displayName).to.equal('root')
210 expect(block.byAccount.name).to.equal('root')
211 expect(block.blockedAccount.displayName).to.equal('user2')
212 expect(block.blockedAccount.name).to.equal('user2')
213 expect(block.blockedAccount.host).to.equal('localhost:9002')
214 }
215
216 {
217 const res = await getAccountBlocklistByAccount(servers[ 0 ].url, servers[ 0 ].accessToken, 1, 2, 'createdAt')
218 const blocks: AccountBlock[] = res.body.data
219
220 expect(res.body.total).to.equal(2)
221
222 const block = blocks[ 0 ]
223 expect(block.byAccount.displayName).to.equal('root')
224 expect(block.byAccount.name).to.equal('root')
225 expect(block.blockedAccount.displayName).to.equal('user1')
226 expect(block.blockedAccount.name).to.equal('user1')
227 expect(block.blockedAccount.host).to.equal('localhost:9001')
228 }
229 })
230
231 it('Should unblock the remote account', async function () {
232 await removeAccountFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002')
233 })
234
235 it('Should display its videos', async function () {
236 const res = await getVideosListWithToken(servers[ 0 ].url, servers[ 0 ].accessToken)
237
238 const videos: Video[] = res.body.data
239 expect(videos).to.have.lengthOf(3)
240
241 const v = videos.find(v => v.name === 'video user 2')
242 expect(v).not.to.be.undefined
243 })
244
245 it('Should unblock the local account', async function () {
246 await removeAccountFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user1')
247 })
248
249 it('Should display its comments', function () {
250 return checkAllComments(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1)
251 })
252 })
253
254 describe('When managing server blocklist', function () {
255 it('Should list all videos', function () {
256 return checkAllVideos(servers[ 0 ].url, servers[ 0 ].accessToken)
257 })
258
259 it('Should list the comments', function () {
260 return checkAllComments(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1)
261 })
262
263 it('Should block a remote server', async function () {
264 await addServerToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002')
265 })
266
267 it('Should hide its videos', async function () {
268 const res = await getVideosListWithToken(servers[ 0 ].url, servers[ 0 ].accessToken)
269
270 const videos: Video[] = res.body.data
271 expect(videos).to.have.lengthOf(2)
272
273 const v1 = videos.find(v => v.name === 'video user 2')
274 const v2 = videos.find(v => v.name === 'video server 2')
275
276 expect(v1).to.be.undefined
277 expect(v2).to.be.undefined
278 })
279
280 it('Should list all the videos with another user', async function () {
281 return checkAllVideos(servers[ 0 ].url, userToken1)
282 })
283
284 it('Should hide its comments')
285
286 it('Should list blocked servers', async function () {
287 const res = await getServerBlocklistByAccount(servers[ 0 ].url, servers[ 0 ].accessToken, 0, 1, 'createdAt')
288 const blocks: ServerBlock[] = res.body.data
289
290 expect(res.body.total).to.equal(1)
291
292 const block = blocks[ 0 ]
293 expect(block.byAccount.displayName).to.equal('root')
294 expect(block.byAccount.name).to.equal('root')
295 expect(block.blockedServer.host).to.equal('localhost:9002')
296 })
297
298 it('Should unblock the remote server', async function () {
299 await removeServerFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002')
300 })
301
302 it('Should display its videos', function () {
303 return checkAllVideos(servers[ 0 ].url, servers[ 0 ].accessToken)
304 })
305
306 it('Should display its comments', function () {
307 return checkAllComments(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID1)
308 })
309 })
310 })
311
312 describe('Server blocklist', function () {
313
314 describe('When managing account blocklist', function () {
315 it('Should list all videos', async function () {
316 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
317 await checkAllVideos(servers[ 0 ].url, token)
318 }
319 })
320
321 it('Should list the comments', async function () {
322 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
323 await checkAllComments(servers[ 0 ].url, token, videoUUID1)
324 }
325 })
326
327 it('Should block a remote account', async function () {
328 await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002')
329 })
330
331 it('Should hide its videos', async function () {
332 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
333 const res = await getVideosListWithToken(servers[ 0 ].url, token)
334
335 const videos: Video[] = res.body.data
336 expect(videos).to.have.lengthOf(3)
337
338 const v = videos.find(v => v.name === 'video user 2')
339 expect(v).to.be.undefined
340 }
341 })
342
343 it('Should block a local account', async function () {
344 await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user1')
345 })
346
347 it('Should hide its videos', async function () {
348 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
349 const res = await getVideosListWithToken(servers[ 0 ].url, token)
350
351 const videos: Video[] = res.body.data
352 expect(videos).to.have.lengthOf(2)
353
354 const v = videos.find(v => v.name === 'video user 1')
355 expect(v).to.be.undefined
356 }
357 })
358
359 it('Should hide its comments', async function () {
360 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
361 const resThreads = await getVideoCommentThreads(servers[ 0 ].url, videoUUID1, 0, 5, '-createdAt', token)
362
363 const threads: VideoComment[] = resThreads.body.data
364 expect(threads).to.have.lengthOf(1)
365 expect(threads[ 0 ].totalReplies).to.equal(0)
366
367 const t = threads.find(t => t.text === 'comment user 1')
368 expect(t).to.be.undefined
369
370 for (const thread of threads) {
371 const res = await getVideoThreadComments(servers[ 0 ].url, videoUUID1, thread.id, token)
372
373 const tree: VideoCommentThreadTree = res.body
374 expect(tree.children).to.have.lengthOf(0)
375 }
376 }
377 })
378
379 it('Should list blocked accounts', async function () {
380 {
381 const res = await getAccountBlocklistByServer(servers[ 0 ].url, servers[ 0 ].accessToken, 0, 1, 'createdAt')
382 const blocks: AccountBlock[] = res.body.data
383
384 expect(res.body.total).to.equal(2)
385
386 const block = blocks[ 0 ]
387 expect(block.byAccount.displayName).to.equal('peertube')
388 expect(block.byAccount.name).to.equal('peertube')
389 expect(block.blockedAccount.displayName).to.equal('user2')
390 expect(block.blockedAccount.name).to.equal('user2')
391 expect(block.blockedAccount.host).to.equal('localhost:9002')
392 }
393
394 {
395 const res = await getAccountBlocklistByServer(servers[ 0 ].url, servers[ 0 ].accessToken, 1, 2, 'createdAt')
396 const blocks: AccountBlock[] = res.body.data
397
398 expect(res.body.total).to.equal(2)
399
400 const block = blocks[ 0 ]
401 expect(block.byAccount.displayName).to.equal('peertube')
402 expect(block.byAccount.name).to.equal('peertube')
403 expect(block.blockedAccount.displayName).to.equal('user1')
404 expect(block.blockedAccount.name).to.equal('user1')
405 expect(block.blockedAccount.host).to.equal('localhost:9001')
406 }
407 })
408
409 it('Should unblock the remote account', async function () {
410 await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002')
411 })
412
413 it('Should display its videos', async function () {
414 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
415 const res = await getVideosListWithToken(servers[ 0 ].url, token)
416
417 const videos: Video[] = res.body.data
418 expect(videos).to.have.lengthOf(3)
419
420 const v = videos.find(v => v.name === 'video user 2')
421 expect(v).not.to.be.undefined
422 }
423 })
424
425 it('Should unblock the local account', async function () {
426 await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user1')
427 })
428
429 it('Should display its comments', async function () {
430 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
431 await checkAllComments(servers[ 0 ].url, token, videoUUID1)
432 }
433 })
434 })
435
436 describe('When managing server blocklist', function () {
437 it('Should list all videos', async function () {
438 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
439 await checkAllVideos(servers[ 0 ].url, token)
440 }
441 })
442
443 it('Should list the comments', async function () {
444 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
445 await checkAllComments(servers[ 0 ].url, token, videoUUID1)
446 }
447 })
448
449 it('Should block a remote server', async function () {
450 await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002')
451 })
452
453 it('Should hide its videos', async function () {
454 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
455 const res1 = await getVideosList(servers[ 0 ].url)
456 const res2 = await getVideosListWithToken(servers[ 0 ].url, token)
457
458 for (const res of [ res1, res2 ]) {
459 const videos: Video[] = res.body.data
460 expect(videos).to.have.lengthOf(2)
461
462 const v1 = videos.find(v => v.name === 'video user 2')
463 const v2 = videos.find(v => v.name === 'video server 2')
464
465 expect(v1).to.be.undefined
466 expect(v2).to.be.undefined
467 }
468 }
469 })
470
471 it('Should hide its comments')
472
473 it('Should list blocked servers', async function () {
474 const res = await getServerBlocklistByServer(servers[ 0 ].url, servers[ 0 ].accessToken, 0, 1, 'createdAt')
475 const blocks: ServerBlock[] = res.body.data
476
477 expect(res.body.total).to.equal(1)
478
479 const block = blocks[ 0 ]
480 expect(block.byAccount.displayName).to.equal('peertube')
481 expect(block.byAccount.name).to.equal('peertube')
482 expect(block.blockedServer.host).to.equal('localhost:9002')
483 })
484
485 it('Should unblock the remote server', async function () {
486 await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002')
487 })
488
489 it('Should list all videos', async function () {
490 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
491 await checkAllVideos(servers[ 0 ].url, token)
492 }
493 })
494
495 it('Should list the comments', async function () {
496 for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
497 await checkAllComments(servers[ 0 ].url, token, videoUUID1)
498 }
499 })
500 })
501 })
502
503 after(async function () {
504 killallServers(servers)
505
506 // Keep the logs if the test failed
507 if (this[ 'ok' ]) {
508 await flushTests()
509 }
510 })
511})
diff --git a/server/tests/api/users/index.ts b/server/tests/api/users/index.ts
index 21d75da3e..0a1b8b0b2 100644
--- a/server/tests/api/users/index.ts
+++ b/server/tests/api/users/index.ts
@@ -1,3 +1,4 @@
1import './blocklist'
1import './user-subscriptions' 2import './user-subscriptions'
2import './users' 3import './users'
3import './users-verification' 4import './users-verification'
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index b67072851..d8699db17 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -148,6 +148,12 @@ describe('Test users with multiple servers', function () {
148 expect(rootServer1Get.displayName).to.equal('my super display name') 148 expect(rootServer1Get.displayName).to.equal('my super display name')
149 expect(rootServer1Get.description).to.equal('my super description updated') 149 expect(rootServer1Get.description).to.equal('my super description updated')
150 150
151 if (server.serverNumber === 1) {
152 expect(rootServer1Get.userId).to.be.a('number')
153 } else {
154 expect(rootServer1Get.userId).to.be.undefined
155 }
156
151 await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png') 157 await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png')
152 } 158 }
153 }) 159 })
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 8b9c6b455..513bca8a0 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -180,7 +180,7 @@ describe('Test users', function () {
180 it('Should be able to upload a video again') 180 it('Should be able to upload a video again')
181 181
182 it('Should be able to create a new user', async function () { 182 it('Should be able to create a new user', async function () {
183 await createUser(server.url, accessToken, user.username,user.password, 2 * 1024 * 1024) 183 await createUser(server.url, accessToken, user.username, user.password, 2 * 1024 * 1024)
184 }) 184 })
185 185
186 it('Should be able to login with this user', async function () { 186 it('Should be able to login with this user', async function () {
@@ -322,6 +322,40 @@ describe('Test users', function () {
322 expect(users[ 1 ].nsfwPolicy).to.equal('display') 322 expect(users[ 1 ].nsfwPolicy).to.equal('display')
323 }) 323 })
324 324
325 it('Should search user by username', async function () {
326 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'oot')
327 const users = res.body.data as User[]
328
329 expect(res.body.total).to.equal(1)
330 expect(users.length).to.equal(1)
331
332 expect(users[ 0 ].username).to.equal('root')
333 })
334
335 it('Should search user by email', async function () {
336 {
337 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'r_1@exam')
338 const users = res.body.data as User[]
339
340 expect(res.body.total).to.equal(1)
341 expect(users.length).to.equal(1)
342
343 expect(users[ 0 ].username).to.equal('user_1')
344 expect(users[ 0 ].email).to.equal('user_1@example.com')
345 }
346
347 {
348 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'example')
349 const users = res.body.data as User[]
350
351 expect(res.body.total).to.equal(2)
352 expect(users.length).to.equal(2)
353
354 expect(users[ 0 ].username).to.equal('root')
355 expect(users[ 1 ].username).to.equal('user_1')
356 }
357 })
358
325 it('Should update my password', async function () { 359 it('Should update my password', async function () {
326 await updateMyUser({ 360 await updateMyUser({
327 url: server.url, 361 url: server.url,
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts
index bf58f9c79..9bdb78491 100644
--- a/server/tests/api/videos/index.ts
+++ b/server/tests/api/videos/index.ts
@@ -14,4 +14,6 @@ import './video-nsfw'
14import './video-privacy' 14import './video-privacy'
15import './video-schedule-update' 15import './video-schedule-update'
16import './video-transcoder' 16import './video-transcoder'
17import './videos-filter'
18import './videos-history'
17import './videos-overview' 19import './videos-overview'
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index 4553ee855..b9ace2885 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -987,19 +987,19 @@ describe('Test multiple servers', function () {
987 files: [ 987 files: [
988 { 988 {
989 resolution: 720, 989 resolution: 720,
990 size: 36000 990 size: 72000
991 }, 991 },
992 { 992 {
993 resolution: 480, 993 resolution: 480,
994 size: 21000 994 size: 45000
995 }, 995 },
996 { 996 {
997 resolution: 360, 997 resolution: 360,
998 size: 17000 998 size: 34600
999 }, 999 },
1000 { 1000 {
1001 resolution: 240, 1001 resolution: 240,
1002 size: 13000 1002 size: 24770
1003 } 1003 }
1004 ] 1004 ]
1005 } 1005 }
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index e3d62b7a0..089c3df25 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -118,7 +118,7 @@ describe('Test a single server', function () {
118 const categories = res.body 118 const categories = res.body
119 expect(Object.keys(categories)).to.have.length.above(10) 119 expect(Object.keys(categories)).to.have.length.above(10)
120 120
121 expect(categories[11]).to.equal('News') 121 expect(categories[11]).to.equal('News & Politics')
122 }) 122 })
123 123
124 it('Should list video licences', async function () { 124 it('Should list video licences', async function () {
diff --git a/server/tests/api/videos/video-imports.ts b/server/tests/api/videos/video-imports.ts
index b7866d529..aaee79a4a 100644
--- a/server/tests/api/videos/video-imports.ts
+++ b/server/tests/api/videos/video-imports.ts
@@ -30,7 +30,7 @@ describe('Test video imports', function () {
30 const videoHttp: VideoDetails = resHttp.body 30 const videoHttp: VideoDetails = resHttp.body
31 31
32 expect(videoHttp.name).to.equal('small video - youtube') 32 expect(videoHttp.name).to.equal('small video - youtube')
33 expect(videoHttp.category.label).to.equal('News') 33 expect(videoHttp.category.label).to.equal('News & Politics')
34 expect(videoHttp.licence.label).to.equal('Attribution') 34 expect(videoHttp.licence.label).to.equal('Attribution')
35 expect(videoHttp.language.label).to.equal('Unknown') 35 expect(videoHttp.language.label).to.equal('Unknown')
36 expect(videoHttp.nsfw).to.be.false 36 expect(videoHttp.nsfw).to.be.false
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts
index 0f83d4d57..85795d2ed 100644
--- a/server/tests/api/videos/video-transcoder.ts
+++ b/server/tests/api/videos/video-transcoder.ts
@@ -4,8 +4,8 @@ import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import * as ffmpeg from 'fluent-ffmpeg' 6import * as ffmpeg from 'fluent-ffmpeg'
7import { VideoDetails, VideoState } from '../../../../shared/models/videos' 7import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos'
8import { getVideoFileFPS, audio } from '../../../helpers/ffmpeg-utils' 8import { audio, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
9import { 9import {
10 buildAbsoluteFixturePath, 10 buildAbsoluteFixturePath,
11 doubleFollow, 11 doubleFollow,
@@ -18,10 +18,13 @@ import {
18 ServerInfo, 18 ServerInfo,
19 setAccessTokensToServers, 19 setAccessTokensToServers,
20 uploadVideo, 20 uploadVideo,
21 webtorrentAdd 21 webtorrentAdd,
22 generateHighBitrateVideo
22} from '../../utils' 23} from '../../utils'
23import { join } from 'path' 24import { join } from 'path'
24import { waitJobs } from '../../utils/server/jobs' 25import { waitJobs } from '../../utils/server/jobs'
26import { pathExists } from 'fs-extra'
27import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
25 28
26const expect = chai.expect 29const expect = chai.expect
27 30
@@ -121,7 +124,7 @@ describe('Test video transcoding', function () {
121 expect(videoDetails.files).to.have.lengthOf(4) 124 expect(videoDetails.files).to.have.lengthOf(4)
122 125
123 const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') 126 const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4')
124 const probe = await audio.get(ffmpeg, path) 127 const probe = await audio.get(path)
125 128
126 if (probe.audioStream) { 129 if (probe.audioStream) {
127 expect(probe.audioStream[ 'codec_name' ]).to.be.equal('aac') 130 expect(probe.audioStream[ 'codec_name' ]).to.be.equal('aac')
@@ -152,7 +155,7 @@ describe('Test video transcoding', function () {
152 155
153 expect(videoDetails.files).to.have.lengthOf(4) 156 expect(videoDetails.files).to.have.lengthOf(4)
154 const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') 157 const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4')
155 const probe = await audio.get(ffmpeg, path) 158 const probe = await audio.get(path)
156 expect(probe).to.not.have.property('audioStream') 159 expect(probe).to.not.have.property('audioStream')
157 } 160 }
158 }) 161 })
@@ -177,9 +180,9 @@ describe('Test video transcoding', function () {
177 180
178 expect(videoDetails.files).to.have.lengthOf(4) 181 expect(videoDetails.files).to.have.lengthOf(4)
179 const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) 182 const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture)
180 const fixtureVideoProbe = await audio.get(ffmpeg, fixturePath) 183 const fixtureVideoProbe = await audio.get(fixturePath)
181 const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') 184 const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4')
182 const videoProbe = await audio.get(ffmpeg, path) 185 const videoProbe = await audio.get(path)
183 if (videoProbe.audioStream && fixtureVideoProbe.audioStream) { 186 if (videoProbe.audioStream && fixtureVideoProbe.audioStream) {
184 const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ] 187 const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ]
185 expect(omit(videoProbe.audioStream, toOmit)).to.be.deep.equal(omit(fixtureVideoProbe.audioStream, toOmit)) 188 expect(omit(videoProbe.audioStream, toOmit)).to.be.deep.equal(omit(fixtureVideoProbe.audioStream, toOmit))
@@ -228,7 +231,7 @@ describe('Test video transcoding', function () {
228 } 231 }
229 }) 232 })
230 233
231 it('Should wait transcoding before publishing the video', async function () { 234 it('Should wait for transcoding before publishing the video', async function () {
232 this.timeout(80000) 235 this.timeout(80000)
233 236
234 { 237 {
@@ -281,6 +284,45 @@ describe('Test video transcoding', function () {
281 } 284 }
282 }) 285 })
283 286
287 it('Should respect maximum bitrate values', async function () {
288 this.timeout(160000)
289
290 let tempFixturePath: string
291
292 {
293 tempFixturePath = await generateHighBitrateVideo()
294
295 const bitrate = await getVideoFileBitrate(tempFixturePath)
296 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 60, VIDEO_TRANSCODING_FPS))
297 }
298
299 const videoAttributes = {
300 name: 'high bitrate video',
301 description: 'high bitrate video',
302 fixture: tempFixturePath
303 }
304
305 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes)
306
307 await waitJobs(servers)
308
309 for (const server of servers) {
310 const res = await getVideosList(server.url)
311
312 const video = res.body.data.find(v => v.name === videoAttributes.name)
313
314 for (const resolution of ['240', '360', '480', '720', '1080']) {
315 const path = join(root(), 'test2', 'videos', video.uuid + '-' + resolution + '.mp4')
316 const bitrate = await getVideoFileBitrate(path)
317 const fps = await getVideoFileFPS(path)
318 const resolution2 = await getVideoFileResolution(path)
319
320 expect(resolution2.videoFileResolution.toString()).to.equal(resolution)
321 expect(bitrate).to.be.below(getMaxBitrate(resolution2.videoFileResolution, fps, VIDEO_TRANSCODING_FPS))
322 }
323 }
324 })
325
284 after(async function () { 326 after(async function () {
285 killallServers(servers) 327 killallServers(servers)
286 }) 328 })
diff --git a/server/tests/api/videos/videos-filter.ts b/server/tests/api/videos/videos-filter.ts
new file mode 100644
index 000000000..a7588129f
--- /dev/null
+++ b/server/tests/api/videos/videos-filter.ts
@@ -0,0 +1,130 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 createUser,
7 doubleFollow,
8 flushAndRunMultipleServers,
9 flushTests,
10 killallServers,
11 makeGetRequest,
12 ServerInfo,
13 setAccessTokensToServers,
14 uploadVideo,
15 userLogin
16} from '../../utils'
17import { Video, VideoPrivacy } from '../../../../shared/models/videos'
18import { UserRole } from '../../../../shared/models/users'
19
20const expect = chai.expect
21
22async function getVideosNames (server: ServerInfo, token: string, filter: string, statusCodeExpected = 200) {
23 const paths = [
24 '/api/v1/video-channels/root_channel/videos',
25 '/api/v1/accounts/root/videos',
26 '/api/v1/videos',
27 '/api/v1/search/videos'
28 ]
29
30 const videosResults: Video[][] = []
31
32 for (const path of paths) {
33 const res = await makeGetRequest({
34 url: server.url,
35 path,
36 token,
37 query: {
38 sort: 'createdAt',
39 filter
40 },
41 statusCodeExpected
42 })
43
44 videosResults.push(res.body.data.map(v => v.name))
45 }
46
47 return videosResults
48}
49
50describe('Test videos filter validator', function () {
51 let servers: ServerInfo[]
52
53 // ---------------------------------------------------------------
54
55 before(async function () {
56 this.timeout(120000)
57
58 await flushTests()
59
60 servers = await flushAndRunMultipleServers(2)
61
62 await setAccessTokensToServers(servers)
63
64 for (const server of servers) {
65 const moderator = { username: 'moderator', password: 'my super password' }
66 await createUser(
67 server.url,
68 server.accessToken,
69 moderator.username,
70 moderator.password,
71 undefined,
72 undefined,
73 UserRole.MODERATOR
74 )
75 server['moderatorAccessToken'] = await userLogin(server, moderator)
76
77 await uploadVideo(server.url, server.accessToken, { name: 'public ' + server.serverNumber })
78
79 {
80 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED }
81 await uploadVideo(server.url, server.accessToken, attributes)
82 }
83
84 {
85 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE }
86 await uploadVideo(server.url, server.accessToken, attributes)
87 }
88 }
89
90 await doubleFollow(servers[0], servers[1])
91 })
92
93 describe('Check videos filter', function () {
94
95 it('Should display local videos', async function () {
96 for (const server of servers) {
97 const namesResults = await getVideosNames(server, server.accessToken, 'local')
98 for (const names of namesResults) {
99 expect(names).to.have.lengthOf(1)
100 expect(names[ 0 ]).to.equal('public ' + server.serverNumber)
101 }
102 }
103 })
104
105 it('Should display all local videos by the admin or the moderator', async function () {
106 for (const server of servers) {
107 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
108
109 const namesResults = await getVideosNames(server, token, 'all-local')
110 for (const names of namesResults) {
111 expect(names).to.have.lengthOf(3)
112
113 expect(names[ 0 ]).to.equal('public ' + server.serverNumber)
114 expect(names[ 1 ]).to.equal('unlisted ' + server.serverNumber)
115 expect(names[ 2 ]).to.equal('private ' + server.serverNumber)
116 }
117 }
118 }
119 })
120 })
121
122 after(async function () {
123 killallServers(servers)
124
125 // Keep the logs if the test failed
126 if (this['ok']) {
127 await flushTests()
128 }
129 })
130})
diff --git a/server/tests/api/videos/videos-history.ts b/server/tests/api/videos/videos-history.ts
new file mode 100644
index 000000000..6d289b288
--- /dev/null
+++ b/server/tests/api/videos/videos-history.ts
@@ -0,0 +1,128 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 flushTests,
7 getVideosListWithToken,
8 getVideoWithToken,
9 killallServers, makePutBodyRequest,
10 runServer, searchVideoWithToken,
11 ServerInfo,
12 setAccessTokensToServers,
13 uploadVideo
14} from '../../utils'
15import { Video, VideoDetails } from '../../../../shared/models/videos'
16import { userWatchVideo } from '../../utils/videos/video-history'
17
18const expect = chai.expect
19
20describe('Test videos history', function () {
21 let server: ServerInfo = null
22 let video1UUID: string
23 let video2UUID: string
24 let video3UUID: string
25
26 before(async function () {
27 this.timeout(30000)
28
29 await flushTests()
30
31 server = await runServer(1)
32
33 await setAccessTokensToServers([ server ])
34
35 {
36 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 1' })
37 video1UUID = res.body.video.uuid
38 }
39
40 {
41 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 2' })
42 video2UUID = res.body.video.uuid
43 }
44
45 {
46 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 3' })
47 video3UUID = res.body.video.uuid
48 }
49 })
50
51 it('Should get videos, without watching history', async function () {
52 const res = await getVideosListWithToken(server.url, server.accessToken)
53 const videos: Video[] = res.body.data
54
55 for (const video of videos) {
56 const resDetail = await getVideoWithToken(server.url, server.accessToken, video.id)
57 const videoDetails: VideoDetails = resDetail.body
58
59 expect(video.userHistory).to.be.undefined
60 expect(videoDetails.userHistory).to.be.undefined
61 }
62 })
63
64 it('Should watch the first and second video', async function () {
65 await userWatchVideo(server.url, server.accessToken, video1UUID, 3)
66 await userWatchVideo(server.url, server.accessToken, video2UUID, 8)
67 })
68
69 it('Should return the correct history when listing, searching and getting videos', async function () {
70 const videosOfVideos: Video[][] = []
71
72 {
73 const res = await getVideosListWithToken(server.url, server.accessToken)
74 videosOfVideos.push(res.body.data)
75 }
76
77 {
78 const res = await searchVideoWithToken(server.url, 'video', server.accessToken)
79 videosOfVideos.push(res.body.data)
80 }
81
82 for (const videos of videosOfVideos) {
83 const video1 = videos.find(v => v.uuid === video1UUID)
84 const video2 = videos.find(v => v.uuid === video2UUID)
85 const video3 = videos.find(v => v.uuid === video3UUID)
86
87 expect(video1.userHistory).to.not.be.undefined
88 expect(video1.userHistory.currentTime).to.equal(3)
89
90 expect(video2.userHistory).to.not.be.undefined
91 expect(video2.userHistory.currentTime).to.equal(8)
92
93 expect(video3.userHistory).to.be.undefined
94 }
95
96 {
97 const resDetail = await getVideoWithToken(server.url, server.accessToken, video1UUID)
98 const videoDetails: VideoDetails = resDetail.body
99
100 expect(videoDetails.userHistory).to.not.be.undefined
101 expect(videoDetails.userHistory.currentTime).to.equal(3)
102 }
103
104 {
105 const resDetail = await getVideoWithToken(server.url, server.accessToken, video2UUID)
106 const videoDetails: VideoDetails = resDetail.body
107
108 expect(videoDetails.userHistory).to.not.be.undefined
109 expect(videoDetails.userHistory.currentTime).to.equal(8)
110 }
111
112 {
113 const resDetail = await getVideoWithToken(server.url, server.accessToken, video3UUID)
114 const videoDetails: VideoDetails = resDetail.body
115
116 expect(videoDetails.userHistory).to.be.undefined
117 }
118 })
119
120 after(async function () {
121 killallServers([ server ])
122
123 // Keep the logs if the test failed
124 if (this['ok']) {
125 await flushTests()
126 }
127 })
128})
diff --git a/server/tests/cli/optimize-old-videos.ts b/server/tests/cli/optimize-old-videos.ts
new file mode 100644
index 000000000..66dd39cce
--- /dev/null
+++ b/server/tests/cli/optimize-old-videos.ts
@@ -0,0 +1,120 @@
1/* tslint:disable:no-unused-expression */
2
3import 'mocha'
4import * as chai from 'chai'
5import { getMaxBitrate, Video, VideoDetails, VideoResolution } from '../../../shared/models/videos'
6import {
7 doubleFollow,
8 execCLI,
9 flushAndRunMultipleServers,
10 flushTests, generateHighBitrateVideo,
11 getEnvCli,
12 getVideo,
13 getVideosList,
14 killallServers, root,
15 ServerInfo,
16 setAccessTokensToServers,
17 uploadVideo, viewVideo, wait
18} from '../utils'
19import { waitJobs } from '../utils/server/jobs'
20import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffmpeg-utils'
21import { VIDEO_TRANSCODING_FPS } from '../../initializers'
22import { join } from 'path'
23
24const expect = chai.expect
25
26describe('Test optimize old videos', function () {
27 let servers: ServerInfo[] = []
28 let video1UUID: string
29 let video2UUID: string
30
31 before(async function () {
32 this.timeout(200000)
33
34 await flushTests()
35
36 // Run server 2 to have transcoding enabled
37 servers = await flushAndRunMultipleServers(2)
38 await setAccessTokensToServers(servers)
39
40 await doubleFollow(servers[0], servers[1])
41
42 let tempFixturePath: string
43
44 {
45 tempFixturePath = await generateHighBitrateVideo()
46
47 const bitrate = await getVideoFileBitrate(tempFixturePath)
48 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 60, VIDEO_TRANSCODING_FPS))
49 }
50
51 // Upload two videos for our needs
52 const res1 = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video1', fixture: tempFixturePath })
53 video1UUID = res1.body.video.uuid
54 const res2 = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video2', fixture: tempFixturePath })
55 video2UUID = res2.body.video.uuid
56
57 await waitJobs(servers)
58 })
59
60 it('Should have two video files on each server', async function () {
61 this.timeout(30000)
62
63 for (const server of servers) {
64 const res = await getVideosList(server.url)
65 const videos = res.body.data
66 expect(videos).to.have.lengthOf(2)
67
68 for (const video of videos) {
69 const res2 = await getVideo(server.url, video.uuid)
70 const videoDetail: VideoDetails = res2.body
71 expect(videoDetail.files).to.have.lengthOf(1)
72 }
73 }
74 })
75
76 it('Should run optimize script', async function () {
77 this.timeout(120000)
78
79 const env = getEnvCli(servers[0])
80 await execCLI(`${env} npm run optimize-old-videos`)
81
82 await waitJobs(servers)
83
84 for (const server of servers) {
85 const res = await getVideosList(server.url)
86 const videos: Video[] = res.body.data
87
88 expect(videos).to.have.lengthOf(2)
89
90 for (const video of videos) {
91 await viewVideo(server.url, video.uuid)
92
93 // Refresh video
94 await waitJobs(servers)
95 await wait(5000)
96 await waitJobs(servers)
97
98 const res2 = await getVideo(server.url, video.uuid)
99 const videosDetails: VideoDetails = res2.body
100
101 expect(videosDetails.files).to.have.lengthOf(1)
102 const file = videosDetails.files[0]
103
104 expect(file.size).to.be.below(5000000)
105
106 const path = join(root(), 'test1', 'videos', video.uuid + '-' + file.resolution.id + '.mp4')
107 const bitrate = await getVideoFileBitrate(path)
108 const fps = await getVideoFileFPS(path)
109 const resolution = await getVideoFileResolution(path)
110
111 expect(resolution.videoFileResolution).to.equal(file.resolution.id)
112 expect(bitrate).to.be.below(getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS))
113 }
114 }
115 })
116
117 after(async function () {
118 killallServers(servers)
119 })
120})
diff --git a/server/tests/helpers/core-utils.ts b/server/tests/helpers/core-utils.ts
new file mode 100644
index 000000000..a6d829a9f
--- /dev/null
+++ b/server/tests/helpers/core-utils.ts
@@ -0,0 +1,48 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 parseBytes
7} from '../../helpers/core-utils'
8
9const expect = chai.expect
10
11describe('Parse Bytes', function () {
12 it('Should pass when given valid value', async function () {
13 // just return it
14 expect(parseBytes(1024)).to.be.eq(1024)
15 expect(parseBytes(1048576)).to.be.eq(1048576)
16 expect(parseBytes('1024')).to.be.eq(1024)
17 expect(parseBytes('1048576')).to.be.eq(1048576)
18
19 // sizes
20 expect(parseBytes('1B')).to.be.eq(1024)
21 expect(parseBytes('1MB')).to.be.eq(1048576)
22 expect(parseBytes('1GB')).to.be.eq(1073741824)
23 expect(parseBytes('1TB')).to.be.eq(1099511627776)
24
25 expect(parseBytes('5GB')).to.be.eq(5368709120)
26 expect(parseBytes('5TB')).to.be.eq(5497558138880)
27
28 expect(parseBytes('1024B')).to.be.eq(1048576)
29 expect(parseBytes('1024MB')).to.be.eq(1073741824)
30 expect(parseBytes('1024GB')).to.be.eq(1099511627776)
31 expect(parseBytes('1024TB')).to.be.eq(1125899906842624)
32
33 // with whitespace
34 expect(parseBytes('1 GB')).to.be.eq(1073741824)
35 expect(parseBytes('1\tGB')).to.be.eq(1073741824)
36
37 // sum value
38 expect(parseBytes('1TB 1024MB')).to.be.eq(1100585369600)
39 expect(parseBytes('4GB 1024MB')).to.be.eq(5368709120)
40 expect(parseBytes('4TB 1024GB')).to.be.eq(5497558138880)
41 expect(parseBytes('4TB 1024GB 0MB')).to.be.eq(5497558138880)
42 expect(parseBytes('1024TB 1024GB 1024MB')).to.be.eq(1127000492212224)
43 })
44
45 it('Should be invalid when given invalid value', async function () {
46 expect(parseBytes('6GB 1GB')).to.be.eq(6)
47 })
48})
diff --git a/server/tests/helpers/index.ts b/server/tests/helpers/index.ts
new file mode 100644
index 000000000..40c7dc70e
--- /dev/null
+++ b/server/tests/helpers/index.ts
@@ -0,0 +1 @@
import './core-utils'
diff --git a/server/tests/utils/miscs/miscs.ts b/server/tests/utils/miscs/miscs.ts
index b2f80e9b1..589daa420 100644
--- a/server/tests/utils/miscs/miscs.ts
+++ b/server/tests/utils/miscs/miscs.ts
@@ -4,7 +4,8 @@ import * as chai from 'chai'
4import { isAbsolute, join } from 'path' 4import { isAbsolute, join } from 'path'
5import * as request from 'supertest' 5import * as request from 'supertest'
6import * as WebTorrent from 'webtorrent' 6import * as WebTorrent from 'webtorrent'
7import { readFile } from 'fs-extra' 7import { pathExists, readFile } from 'fs-extra'
8import * as ffmpeg from 'fluent-ffmpeg'
8 9
9const expect = chai.expect 10const expect = chai.expect
10let webtorrent = new WebTorrent() 11let webtorrent = new WebTorrent()
@@ -51,14 +52,41 @@ async function testImage (url: string, imageName: string, imagePath: string, ext
51 expect(data.length).to.be.below(maxLength) 52 expect(data.length).to.be.below(maxLength)
52} 53}
53 54
54function buildAbsoluteFixturePath (path: string) { 55function buildAbsoluteFixturePath (path: string, customTravisPath = false) {
55 if (isAbsolute(path)) { 56 if (isAbsolute(path)) {
56 return path 57 return path
57 } 58 }
58 59
60 if (customTravisPath && process.env.TRAVIS) return join(process.env.HOME, 'fixtures', path)
61
59 return join(__dirname, '..', '..', 'fixtures', path) 62 return join(__dirname, '..', '..', 'fixtures', path)
60} 63}
61 64
65async function generateHighBitrateVideo () {
66 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
67
68 const exists = await pathExists(tempFixturePath)
69 if (!exists) {
70
71 // Generate a random, high bitrate video on the fly, so we don't have to include
72 // a large file in the repo. The video needs to have a certain minimum length so
73 // that FFmpeg properly applies bitrate limits.
74 // https://stackoverflow.com/a/15795112
75 return new Promise<string>(async (res, rej) => {
76 ffmpeg()
77 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
78 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
79 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
80 .output(tempFixturePath)
81 .on('error', rej)
82 .on('end', () => res(tempFixturePath))
83 .run()
84 })
85 }
86
87 return tempFixturePath
88}
89
62// --------------------------------------------------------------------------- 90// ---------------------------------------------------------------------------
63 91
64export { 92export {
@@ -68,5 +96,6 @@ export {
68 immutableAssign, 96 immutableAssign,
69 testImage, 97 testImage,
70 buildAbsoluteFixturePath, 98 buildAbsoluteFixturePath,
71 root 99 root,
100 generateHighBitrateVideo
72} 101}
diff --git a/server/tests/utils/requests/requests.ts b/server/tests/utils/requests/requests.ts
index 27a529eda..5796540f7 100644
--- a/server/tests/utils/requests/requests.ts
+++ b/server/tests/utils/requests/requests.ts
@@ -37,9 +37,7 @@ function makeDeleteRequest (options: {
37 37
38 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 38 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
39 39
40 return req 40 return req.expect(options.statusCodeExpected)
41 .expect('Content-Type', /json/)
42 .expect(options.statusCodeExpected)
43} 41}
44 42
45function makeUploadRequest (options: { 43function makeUploadRequest (options: {
diff --git a/server/tests/utils/server/follows.ts b/server/tests/utils/server/follows.ts
index 8a65a958b..7741757a6 100644
--- a/server/tests/utils/server/follows.ts
+++ b/server/tests/utils/server/follows.ts
@@ -2,7 +2,7 @@ import * as request from 'supertest'
2import { ServerInfo } from './servers' 2import { ServerInfo } from './servers'
3import { waitJobs } from './jobs' 3import { waitJobs } from './jobs'
4 4
5function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string) { 5function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
6 const path = '/api/v1/server/followers' 6 const path = '/api/v1/server/followers'
7 7
8 return request(url) 8 return request(url)
@@ -10,12 +10,13 @@ function getFollowersListPaginationAndSort (url: string, start: number, count: n
10 .query({ start }) 10 .query({ start })
11 .query({ count }) 11 .query({ count })
12 .query({ sort }) 12 .query({ sort })
13 .query({ search })
13 .set('Accept', 'application/json') 14 .set('Accept', 'application/json')
14 .expect(200) 15 .expect(200)
15 .expect('Content-Type', /json/) 16 .expect('Content-Type', /json/)
16} 17}
17 18
18function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string) { 19function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
19 const path = '/api/v1/server/following' 20 const path = '/api/v1/server/following'
20 21
21 return request(url) 22 return request(url)
@@ -23,6 +24,7 @@ function getFollowingListPaginationAndSort (url: string, start: number, count: n
23 .query({ start }) 24 .query({ start })
24 .query({ count }) 25 .query({ count })
25 .query({ sort }) 26 .query({ sort })
27 .query({ search })
26 .set('Accept', 'application/json') 28 .set('Accept', 'application/json')
27 .expect(200) 29 .expect(200)
28 .expect('Content-Type', /json/) 30 .expect('Content-Type', /json/)
diff --git a/server/tests/utils/users/blocklist.ts b/server/tests/utils/users/blocklist.ts
new file mode 100644
index 000000000..35b537571
--- /dev/null
+++ b/server/tests/utils/users/blocklist.ts
@@ -0,0 +1,198 @@
1/* tslint:disable:no-unused-expression */
2
3import { makeDeleteRequest, makePostBodyRequest } from '../index'
4import { makeGetRequest } from '../requests/requests'
5
6function getAccountBlocklistByAccount (
7 url: string,
8 token: string,
9 start: number,
10 count: number,
11 sort = '-createdAt',
12 statusCodeExpected = 200
13) {
14 const path = '/api/v1/users/me/blocklist/accounts'
15
16 return makeGetRequest({
17 url,
18 token,
19 query: { start, count, sort },
20 path,
21 statusCodeExpected
22 })
23}
24
25function addAccountToAccountBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
26 const path = '/api/v1/users/me/blocklist/accounts'
27
28 return makePostBodyRequest({
29 url,
30 path,
31 token,
32 fields: {
33 accountName: accountToBlock
34 },
35 statusCodeExpected
36 })
37}
38
39function removeAccountFromAccountBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
40 const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
41
42 return makeDeleteRequest({
43 url,
44 path,
45 token,
46 statusCodeExpected
47 })
48}
49
50function getServerBlocklistByAccount (
51 url: string,
52 token: string,
53 start: number,
54 count: number,
55 sort = '-createdAt',
56 statusCodeExpected = 200
57) {
58 const path = '/api/v1/users/me/blocklist/servers'
59
60 return makeGetRequest({
61 url,
62 token,
63 query: { start, count, sort },
64 path,
65 statusCodeExpected
66 })
67}
68
69function addServerToAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
70 const path = '/api/v1/users/me/blocklist/servers'
71
72 return makePostBodyRequest({
73 url,
74 path,
75 token,
76 fields: {
77 host: serverToBlock
78 },
79 statusCodeExpected
80 })
81}
82
83function removeServerFromAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
84 const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
85
86 return makeDeleteRequest({
87 url,
88 path,
89 token,
90 statusCodeExpected
91 })
92}
93
94function getAccountBlocklistByServer (
95 url: string,
96 token: string,
97 start: number,
98 count: number,
99 sort = '-createdAt',
100 statusCodeExpected = 200
101) {
102 const path = '/api/v1/server/blocklist/accounts'
103
104 return makeGetRequest({
105 url,
106 token,
107 query: { start, count, sort },
108 path,
109 statusCodeExpected
110 })
111}
112
113function addAccountToServerBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
114 const path = '/api/v1/server/blocklist/accounts'
115
116 return makePostBodyRequest({
117 url,
118 path,
119 token,
120 fields: {
121 accountName: accountToBlock
122 },
123 statusCodeExpected
124 })
125}
126
127function removeAccountFromServerBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
128 const path = '/api/v1/server/blocklist/accounts/' + accountToUnblock
129
130 return makeDeleteRequest({
131 url,
132 path,
133 token,
134 statusCodeExpected
135 })
136}
137
138function getServerBlocklistByServer (
139 url: string,
140 token: string,
141 start: number,
142 count: number,
143 sort = '-createdAt',
144 statusCodeExpected = 200
145) {
146 const path = '/api/v1/server/blocklist/servers'
147
148 return makeGetRequest({
149 url,
150 token,
151 query: { start, count, sort },
152 path,
153 statusCodeExpected
154 })
155}
156
157function addServerToServerBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
158 const path = '/api/v1/server/blocklist/servers'
159
160 return makePostBodyRequest({
161 url,
162 path,
163 token,
164 fields: {
165 host: serverToBlock
166 },
167 statusCodeExpected
168 })
169}
170
171function removeServerFromServerBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
172 const path = '/api/v1/server/blocklist/servers/' + serverToBlock
173
174 return makeDeleteRequest({
175 url,
176 path,
177 token,
178 statusCodeExpected
179 })
180}
181
182// ---------------------------------------------------------------------------
183
184export {
185 getAccountBlocklistByAccount,
186 addAccountToAccountBlocklist,
187 removeAccountFromAccountBlocklist,
188 getServerBlocklistByAccount,
189 addServerToAccountBlocklist,
190 removeServerFromAccountBlocklist,
191
192 getAccountBlocklistByServer,
193 addAccountToServerBlocklist,
194 removeAccountFromServerBlocklist,
195 getServerBlocklistByServer,
196 addServerToServerBlocklist,
197 removeServerFromServerBlocklist
198}
diff --git a/server/tests/utils/users/users.ts b/server/tests/utils/users/users.ts
index 41d8ce265..d77233d62 100644
--- a/server/tests/utils/users/users.ts
+++ b/server/tests/utils/users/users.ts
@@ -112,7 +112,7 @@ function getUsersList (url: string, accessToken: string) {
112 .expect('Content-Type', /json/) 112 .expect('Content-Type', /json/)
113} 113}
114 114
115function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string) { 115function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string, search?: string) {
116 const path = '/api/v1/users' 116 const path = '/api/v1/users'
117 117
118 return request(url) 118 return request(url)
@@ -120,6 +120,7 @@ function getUsersListPaginationAndSort (url: string, accessToken: string, start:
120 .query({ start }) 120 .query({ start })
121 .query({ count }) 121 .query({ count })
122 .query({ sort }) 122 .query({ sort })
123 .query({ search })
123 .set('Accept', 'application/json') 124 .set('Accept', 'application/json')
124 .set('Authorization', 'Bearer ' + accessToken) 125 .set('Authorization', 'Bearer ' + accessToken)
125 .expect(200) 126 .expect(200)
diff --git a/server/tests/utils/videos/video-comments.ts b/server/tests/utils/videos/video-comments.ts
index 1b9ee452e..7d4cae364 100644
--- a/server/tests/utils/videos/video-comments.ts
+++ b/server/tests/utils/videos/video-comments.ts
@@ -1,7 +1,7 @@
1import * as request from 'supertest' 1import * as request from 'supertest'
2import { makeDeleteRequest } from '../' 2import { makeDeleteRequest } from '../'
3 3
4function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string) { 4function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
5 const path = '/api/v1/videos/' + videoId + '/comment-threads' 5 const path = '/api/v1/videos/' + videoId + '/comment-threads'
6 6
7 const req = request(url) 7 const req = request(url)
@@ -10,20 +10,24 @@ function getVideoCommentThreads (url: string, videoId: number | string, start: n
10 .query({ count: count }) 10 .query({ count: count })
11 11
12 if (sort) req.query({ sort }) 12 if (sort) req.query({ sort })
13 if (token) req.set('Authorization', 'Bearer ' + token)
13 14
14 return req.set('Accept', 'application/json') 15 return req.set('Accept', 'application/json')
15 .expect(200) 16 .expect(200)
16 .expect('Content-Type', /json/) 17 .expect('Content-Type', /json/)
17} 18}
18 19
19function getVideoThreadComments (url: string, videoId: number | string, threadId: number) { 20function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
20 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId 21 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
21 22
22 return request(url) 23 const req = request(url)
23 .get(path) 24 .get(path)
24 .set('Accept', 'application/json') 25 .set('Accept', 'application/json')
25 .expect(200) 26
26 .expect('Content-Type', /json/) 27 if (token) req.set('Authorization', 'Bearer ' + token)
28
29 return req.expect(200)
30 .expect('Content-Type', /json/)
27} 31}
28 32
29function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) { 33function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) {
diff --git a/server/tests/utils/videos/video-history.ts b/server/tests/utils/videos/video-history.ts
new file mode 100644
index 000000000..7635478f7
--- /dev/null
+++ b/server/tests/utils/videos/video-history.ts
@@ -0,0 +1,14 @@
1import { makePutBodyRequest } from '../requests/requests'
2
3function userWatchVideo (url: string, token: string, videoId: number | string, currentTime: number) {
4 const path = '/api/v1/videos/' + videoId + '/watching'
5 const fields = { currentTime }
6
7 return makePutBodyRequest({ url, path, token, fields, statusCodeExpected: 204 })
8}
9
10// ---------------------------------------------------------------------------
11
12export {
13 userWatchVideo
14}