aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests
diff options
context:
space:
mode:
Diffstat (limited to 'server/tests')
-rw-r--r--server/tests/api/activitypub/security.ts116
-rw-r--r--server/tests/api/check-params/user-notifications.ts4
-rw-r--r--server/tests/api/check-params/users.ts2
-rw-r--r--server/tests/api/check-params/video-channels.ts70
-rw-r--r--server/tests/api/live/live.ts10
-rw-r--r--server/tests/api/notifications/admin-notifications.ts165
-rw-r--r--server/tests/api/notifications/index.ts1
-rw-r--r--server/tests/api/notifications/moderation-notifications.ts39
-rw-r--r--server/tests/api/server/auto-follows.ts7
-rw-r--r--server/tests/api/server/config.ts34
-rw-r--r--server/tests/api/server/handle-down.ts2
-rw-r--r--server/tests/api/server/services.ts12
-rw-r--r--server/tests/api/server/stats.ts76
-rw-r--r--server/tests/api/users/users.ts51
-rw-r--r--server/tests/api/videos/video-channels.ts99
-rw-r--r--server/tests/cli/index.ts1
-rw-r--r--server/tests/cli/print-transcode-command.ts3
-rw-r--r--server/tests/cli/regenerate-thumbnails.ts124
-rw-r--r--server/tests/client.ts9
-rw-r--r--server/tests/feeds/feeds.ts17
-rw-r--r--server/tests/fixtures/banner-resized.jpgbin0 -> 88780 bytes
-rw-r--r--server/tests/fixtures/banner.jpgbin0 -> 31648 bytes
-rw-r--r--server/tests/fixtures/peertube-plugin-test-four/main.js13
-rw-r--r--server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js82
-rw-r--r--server/tests/fixtures/peertube-plugin-test-unloading/lib.js2
-rw-r--r--server/tests/fixtures/peertube-plugin-test-unloading/main.js14
-rw-r--r--server/tests/fixtures/peertube-plugin-test-unloading/package.json20
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js70
-rw-r--r--server/tests/fixtures/thumbnail-playlist.jpgbin2520 -> 4981 bytes
-rw-r--r--server/tests/fixtures/video_import_thumbnail.jpgbin5885 -> 10980 bytes
-rw-r--r--server/tests/fixtures/video_short.mp4.jpgbin2618 -> 4981 bytes
-rw-r--r--server/tests/fixtures/video_short.ogv.jpgbin2618 -> 4981 bytes
-rw-r--r--server/tests/fixtures/video_short.webm.jpgbin3598 -> 4981 bytes
-rw-r--r--server/tests/fixtures/video_short1.webm.jpgbin4616 -> 6309 bytes
-rw-r--r--server/tests/fixtures/video_short2.webm.jpgbin4221 -> 5506 bytes
-rw-r--r--server/tests/fixtures/video_short3.webm.jpgbin3972 -> 4981 bytes
-rw-r--r--server/tests/helpers/request.ts16
-rw-r--r--server/tests/plugins/external-auth.ts2
-rw-r--r--server/tests/plugins/filter-hooks.ts178
-rw-r--r--server/tests/plugins/index.ts1
-rw-r--r--server/tests/plugins/plugin-helpers.ts27
-rw-r--r--server/tests/plugins/plugin-transcoding.ts57
-rw-r--r--server/tests/plugins/plugin-unloading.ts89
43 files changed, 1247 insertions, 166 deletions
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index 8bde54a40..364b53e0f 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -8,6 +8,8 @@ import {
8 cleanupTests, 8 cleanupTests,
9 closeAllSequelize, 9 closeAllSequelize,
10 flushAndRunMultipleServers, 10 flushAndRunMultipleServers,
11 killallServers,
12 reRunServer,
11 ServerInfo, 13 ServerInfo,
12 setActorField, 14 setActorField,
13 wait 15 wait
@@ -20,21 +22,32 @@ import { buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activi
20const expect = chai.expect 22const expect = chai.expect
21 23
22function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) { 24function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) {
25 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube'
26
23 return Promise.all([ 27 return Promise.all([
24 setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'publicKey', publicKey), 28 setActorField(onServer.internalServerNumber, url, 'publicKey', publicKey),
25 setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'privateKey', privateKey) 29 setActorField(onServer.internalServerNumber, url, 'privateKey', privateKey)
26 ]) 30 ])
27} 31}
28 32
29function getAnnounceWithoutContext (server2: ServerInfo) { 33function setUpdatedAtOfServer (onServer: ServerInfo, ofServer: ServerInfo, updatedAt: string) {
34 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube'
35
36 return Promise.all([
37 setActorField(onServer.internalServerNumber, url, 'createdAt', updatedAt),
38 setActorField(onServer.internalServerNumber, url, 'updatedAt', updatedAt)
39 ])
40}
41
42function getAnnounceWithoutContext (server: ServerInfo) {
30 const json = require('./json/peertube/announce-without-context.json') 43 const json = require('./json/peertube/announce-without-context.json')
31 const result: typeof json = {} 44 const result: typeof json = {}
32 45
33 for (const key of Object.keys(json)) { 46 for (const key of Object.keys(json)) {
34 if (Array.isArray(json[key])) { 47 if (Array.isArray(json[key])) {
35 result[key] = json[key].map(v => v.replace(':9002', `:${server2.port}`)) 48 result[key] = json[key].map(v => v.replace(':9002', `:${server.port}`))
36 } else { 49 } else {
37 result[key] = json[key].replace(':9002', `:${server2.port}`) 50 result[key] = json[key].replace(':9002', `:${server.port}`)
38 } 51 }
39 } 52 }
40 53
@@ -64,7 +77,8 @@ describe('Test ActivityPub security', function () {
64 77
65 url = servers[0].url + '/inbox' 78 url = servers[0].url + '/inbox'
66 79
67 await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) 80 await setKeysOfServer(servers[0], servers[1], keys.publicKey, null)
81 await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey)
68 82
69 const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' } 83 const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' }
70 const by = { url: 'http://localhost:' + servers[1].port + '/accounts/peertube', privateKey: keys.privateKey } 84 const by = { url: 'http://localhost:' + servers[1].port + '/accounts/peertube', privateKey: keys.privateKey }
@@ -79,9 +93,12 @@ describe('Test ActivityPub security', function () {
79 Digest: buildDigest({ hello: 'coucou' }) 93 Digest: buildDigest({ hello: 'coucou' })
80 } 94 }
81 95
82 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) 96 try {
83 97 await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
84 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 98 expect(true, 'Did not throw').to.be.false
99 } catch (err) {
100 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
101 }
85 }) 102 })
86 103
87 it('Should fail with an invalid date', async function () { 104 it('Should fail with an invalid date', async function () {
@@ -89,9 +106,12 @@ describe('Test ActivityPub security', function () {
89 const headers = buildGlobalHeaders(body) 106 const headers = buildGlobalHeaders(body)
90 headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' 107 headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT'
91 108
92 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) 109 try {
93 110 await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
94 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 111 expect(true, 'Did not throw').to.be.false
112 } catch (err) {
113 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
114 }
95 }) 115 })
96 116
97 it('Should fail with bad keys', async function () { 117 it('Should fail with bad keys', async function () {
@@ -101,9 +121,12 @@ describe('Test ActivityPub security', function () {
101 const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) 121 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
102 const headers = buildGlobalHeaders(body) 122 const headers = buildGlobalHeaders(body)
103 123
104 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) 124 try {
105 125 await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
106 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 126 expect(true, 'Did not throw').to.be.false
127 } catch (err) {
128 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
129 }
107 }) 130 })
108 131
109 it('Should reject requests without appropriate signed headers', async function () { 132 it('Should reject requests without appropriate signed headers', async function () {
@@ -123,8 +146,12 @@ describe('Test ActivityPub security', function () {
123 for (const badHeaders of badHeadersMatrix) { 146 for (const badHeaders of badHeadersMatrix) {
124 signatureOptions.headers = badHeaders 147 signatureOptions.headers = badHeaders
125 148
126 const { response } = await makePOSTAPRequest(url, body, signatureOptions, headers) 149 try {
127 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 150 await makePOSTAPRequest(url, body, signatureOptions, headers)
151 expect(true, 'Did not throw').to.be.false
152 } catch (err) {
153 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
154 }
128 } 155 }
129 }) 156 })
130 157
@@ -132,27 +159,32 @@ describe('Test ActivityPub security', function () {
132 const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) 159 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
133 const headers = buildGlobalHeaders(body) 160 const headers = buildGlobalHeaders(body)
134 161
135 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) 162 const { statusCode } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
136 163 expect(statusCode).to.equal(HttpStatusCode.NO_CONTENT_204)
137 expect(response.statusCode).to.equal(HttpStatusCode.NO_CONTENT_204)
138 }) 164 })
139 165
140 it('Should refresh the actor keys', async function () { 166 it('Should refresh the actor keys', async function () {
141 this.timeout(20000) 167 this.timeout(20000)
142 168
143 // Wait refresh invalidation
144 await wait(10000)
145
146 // Update keys of server 2 to invalid keys 169 // Update keys of server 2 to invalid keys
147 // Server 1 should refresh the actor and fail 170 // Server 1 should refresh the actor and fail
148 await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) 171 await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
172 await setUpdatedAtOfServer(servers[0], servers[1], '2015-07-17 22:00:00+00')
173
174 // Invalid peertube actor cache
175 killallServers([ servers[1] ])
176 await reRunServer(servers[1])
149 177
150 const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) 178 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
151 const headers = buildGlobalHeaders(body) 179 const headers = buildGlobalHeaders(body)
152 180
153 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) 181 try {
154 182 await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
155 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 183 expect(true, 'Did not throw').to.be.false
184 } catch (err) {
185 console.error(err)
186 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
187 }
156 }) 188 })
157 }) 189 })
158 190
@@ -183,9 +215,12 @@ describe('Test ActivityPub security', function () {
183 215
184 const headers = buildGlobalHeaders(signedBody) 216 const headers = buildGlobalHeaders(signedBody)
185 217
186 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) 218 try {
187 219 await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
188 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 220 expect(true, 'Did not throw').to.be.false
221 } catch (err) {
222 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
223 }
189 }) 224 })
190 225
191 it('Should fail with an altered body', async function () { 226 it('Should fail with an altered body', async function () {
@@ -204,9 +239,12 @@ describe('Test ActivityPub security', function () {
204 239
205 const headers = buildGlobalHeaders(signedBody) 240 const headers = buildGlobalHeaders(signedBody)
206 241
207 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) 242 try {
208 243 await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
209 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 244 expect(true, 'Did not throw').to.be.false
245 } catch (err) {
246 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
247 }
210 }) 248 })
211 249
212 it('Should succeed with a valid signature', async function () { 250 it('Should succeed with a valid signature', async function () {
@@ -220,9 +258,8 @@ describe('Test ActivityPub security', function () {
220 258
221 const headers = buildGlobalHeaders(signedBody) 259 const headers = buildGlobalHeaders(signedBody)
222 260
223 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) 261 const { statusCode } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
224 262 expect(statusCode).to.equal(HttpStatusCode.NO_CONTENT_204)
225 expect(response.statusCode).to.equal(HttpStatusCode.NO_CONTENT_204)
226 }) 263 })
227 264
228 it('Should refresh the actor keys', async function () { 265 it('Should refresh the actor keys', async function () {
@@ -243,9 +280,12 @@ describe('Test ActivityPub security', function () {
243 280
244 const headers = buildGlobalHeaders(signedBody) 281 const headers = buildGlobalHeaders(signedBody)
245 282
246 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) 283 try {
247 284 await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
248 expect(response.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403) 285 expect(true, 'Did not throw').to.be.false
286 } catch (err) {
287 expect(err.statusCode).to.equal(HttpStatusCode.FORBIDDEN_403)
288 }
249 }) 289 })
250 }) 290 })
251 291
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts
index 05a78b0ad..26d4423f9 100644
--- a/server/tests/api/check-params/user-notifications.ts
+++ b/server/tests/api/check-params/user-notifications.ts
@@ -176,7 +176,9 @@ describe('Test user notifications API validators', function () {
176 newInstanceFollower: UserNotificationSettingValue.WEB, 176 newInstanceFollower: UserNotificationSettingValue.WEB,
177 autoInstanceFollowing: UserNotificationSettingValue.WEB, 177 autoInstanceFollowing: UserNotificationSettingValue.WEB,
178 abuseNewMessage: UserNotificationSettingValue.WEB, 178 abuseNewMessage: UserNotificationSettingValue.WEB,
179 abuseStateChange: UserNotificationSettingValue.WEB 179 abuseStateChange: UserNotificationSettingValue.WEB,
180 newPeerTubeVersion: UserNotificationSettingValue.WEB,
181 newPluginVersion: UserNotificationSettingValue.WEB
180 } 182 }
181 183
182 it('Should fail with missing fields', async function () { 184 it('Should fail with missing fields', async function () {
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 0a13f5b67..2b03fde2d 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -241,7 +241,7 @@ describe('Test users API validators', function () {
241 }) 241 })
242 242
243 it('Should succeed with no password on a server with smtp enabled', async function () { 243 it('Should succeed with no password on a server with smtp enabled', async function () {
244 this.timeout(10000) 244 this.timeout(20000)
245 245
246 killallServers([ server ]) 246 killallServers([ server ])
247 247
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index 0dd436426..bc2e6192e 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -234,7 +234,8 @@ describe('Test video channels API validator', function () {
234 }) 234 })
235 }) 235 })
236 236
237 describe('When updating video channel avatar', function () { 237 describe('When updating video channel avatar/banner', function () {
238 const types = [ 'avatar', 'banner' ]
238 let path: string 239 let path: string
239 240
240 before(async function () { 241 before(async function () {
@@ -242,48 +243,57 @@ describe('Test video channels API validator', function () {
242 }) 243 })
243 244
244 it('Should fail with an incorrect input file', async function () { 245 it('Should fail with an incorrect input file', async function () {
245 const fields = {} 246 for (const type of types) {
246 const attaches = { 247 const fields = {}
247 avatarfile: join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') 248 const attaches = {
249 [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'video_short.mp4')
250 }
251
252 await makeUploadRequest({ url: server.url, path: `${path}/${type}/pick`, token: server.accessToken, fields, attaches })
248 } 253 }
249 await makeUploadRequest({ url: server.url, path: path + '/avatar/pick', token: server.accessToken, fields, attaches })
250 }) 254 })
251 255
252 it('Should fail with a big file', async function () { 256 it('Should fail with a big file', async function () {
253 const fields = {} 257 for (const type of types) {
254 const attaches = { 258 const fields = {}
255 avatarfile: join(__dirname, '..', '..', 'fixtures', 'avatar-big.png') 259 const attaches = {
260 [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'avatar-big.png')
261 }
262 await makeUploadRequest({ url: server.url, path: `${path}/${type}/pick`, token: server.accessToken, fields, attaches })
256 } 263 }
257 await makeUploadRequest({ url: server.url, path: path + '/avatar/pick', token: server.accessToken, fields, attaches })
258 }) 264 })
259 265
260 it('Should fail with an unauthenticated user', async function () { 266 it('Should fail with an unauthenticated user', async function () {
261 const fields = {} 267 for (const type of types) {
262 const attaches = { 268 const fields = {}
263 avatarfile: join(__dirname, '..', '..', 'fixtures', 'avatar.png') 269 const attaches = {
270 [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'avatar.png')
271 }
272 await makeUploadRequest({
273 url: server.url,
274 path: `${path}/${type}/pick`,
275 fields,
276 attaches,
277 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401
278 })
264 } 279 }
265 await makeUploadRequest({
266 url: server.url,
267 path: path + '/avatar/pick',
268 fields,
269 attaches,
270 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401
271 })
272 }) 280 })
273 281
274 it('Should succeed with the correct params', async function () { 282 it('Should succeed with the correct params', async function () {
275 const fields = {} 283 for (const type of types) {
276 const attaches = { 284 const fields = {}
277 avatarfile: join(__dirname, '..', '..', 'fixtures', 'avatar.png') 285 const attaches = {
286 [type + 'file']: join(__dirname, '..', '..', 'fixtures', 'avatar.png')
287 }
288 await makeUploadRequest({
289 url: server.url,
290 path: `${path}/${type}/pick`,
291 token: server.accessToken,
292 fields,
293 attaches,
294 statusCodeExpected: HttpStatusCode.OK_200
295 })
278 } 296 }
279 await makeUploadRequest({
280 url: server.url,
281 path: path + '/avatar/pick',
282 token: server.accessToken,
283 fields,
284 attaches,
285 statusCodeExpected: HttpStatusCode.OK_200
286 })
287 }) 297 })
288 }) 298 })
289 299
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 0831f91f0..d48e2a8ee 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -255,7 +255,7 @@ describe('Test live', function () {
255 } 255 }
256 256
257 it('Should not allow a stream without the appropriate path', async function () { 257 it('Should not allow a stream without the appropriate path', async function () {
258 this.timeout(30000) 258 this.timeout(60000)
259 259
260 liveVideo = await createLiveWrapper() 260 liveVideo = await createLiveWrapper()
261 261
@@ -264,14 +264,14 @@ describe('Test live', function () {
264 }) 264 })
265 265
266 it('Should not allow a stream without the appropriate stream key', async function () { 266 it('Should not allow a stream without the appropriate stream key', async function () {
267 this.timeout(30000) 267 this.timeout(60000)
268 268
269 const command = sendRTMPStream(rtmpUrl + '/live', 'bad-stream-key') 269 const command = sendRTMPStream(rtmpUrl + '/live', 'bad-stream-key')
270 await testFfmpegStreamError(command, true) 270 await testFfmpegStreamError(command, true)
271 }) 271 })
272 272
273 it('Should succeed with the correct params', async function () { 273 it('Should succeed with the correct params', async function () {
274 this.timeout(30000) 274 this.timeout(60000)
275 275
276 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) 276 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
277 await testFfmpegStreamError(command, false) 277 await testFfmpegStreamError(command, false)
@@ -292,7 +292,7 @@ describe('Test live', function () {
292 }) 292 })
293 293
294 it('Should not allow a stream on a live that was blacklisted', async function () { 294 it('Should not allow a stream on a live that was blacklisted', async function () {
295 this.timeout(30000) 295 this.timeout(60000)
296 296
297 liveVideo = await createLiveWrapper() 297 liveVideo = await createLiveWrapper()
298 298
@@ -303,7 +303,7 @@ describe('Test live', function () {
303 }) 303 })
304 304
305 it('Should not allow a stream on a live that was deleted', async function () { 305 it('Should not allow a stream on a live that was deleted', async function () {
306 this.timeout(30000) 306 this.timeout(60000)
307 307
308 liveVideo = await createLiveWrapper() 308 liveVideo = await createLiveWrapper()
309 309
diff --git a/server/tests/api/notifications/admin-notifications.ts b/server/tests/api/notifications/admin-notifications.ts
new file mode 100644
index 000000000..cfe0bd2bb
--- /dev/null
+++ b/server/tests/api/notifications/admin-notifications.ts
@@ -0,0 +1,165 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import { expect } from 'chai'
5import { MockJoinPeerTubeVersions } from '@shared/extra-utils/mock-servers/joinpeertube-versions'
6import { PluginType } from '@shared/models'
7import { cleanupTests, installPlugin, setPluginLatestVersion, setPluginVersion, wait } from '../../../../shared/extra-utils'
8import { ServerInfo } from '../../../../shared/extra-utils/index'
9import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
10import {
11 CheckerBaseParams,
12 checkNewPeerTubeVersion,
13 checkNewPluginVersion,
14 prepareNotificationsTest
15} from '../../../../shared/extra-utils/users/user-notifications'
16import { UserNotification, UserNotificationType } from '../../../../shared/models/users'
17
18describe('Test admin notifications', function () {
19 let server: ServerInfo
20 let userNotifications: UserNotification[] = []
21 let adminNotifications: UserNotification[] = []
22 let emails: object[] = []
23 let baseParams: CheckerBaseParams
24 let joinPeerTubeServer: MockJoinPeerTubeVersions
25
26 before(async function () {
27 this.timeout(120000)
28
29 joinPeerTubeServer = new MockJoinPeerTubeVersions()
30 const port = await joinPeerTubeServer.initialize()
31
32 const config = {
33 peertube: {
34 check_latest_version: {
35 enabled: true,
36 url: `http://localhost:${port}/versions.json`
37 }
38 },
39 plugins: {
40 index: {
41 enabled: true,
42 check_latest_versions_interval: '5 seconds'
43 }
44 }
45 }
46
47 const res = await prepareNotificationsTest(1, config)
48 emails = res.emails
49 server = res.servers[0]
50
51 userNotifications = res.userNotifications
52 adminNotifications = res.adminNotifications
53
54 baseParams = {
55 server: server,
56 emails,
57 socketNotifications: adminNotifications,
58 token: server.accessToken
59 }
60
61 await installPlugin({
62 url: server.url,
63 accessToken: server.accessToken,
64 npmName: 'peertube-plugin-hello-world'
65 })
66
67 await installPlugin({
68 url: server.url,
69 accessToken: server.accessToken,
70 npmName: 'peertube-theme-background-red'
71 })
72 })
73
74 describe('Latest PeerTube version notification', function () {
75
76 it('Should not send a notification to admins if there is not a new version', async function () {
77 this.timeout(30000)
78
79 joinPeerTubeServer.setLatestVersion('1.4.2')
80
81 await wait(3000)
82 await checkNewPeerTubeVersion(baseParams, '1.4.2', 'absence')
83 })
84
85 it('Should send a notification to admins on new plugin version', async function () {
86 this.timeout(30000)
87
88 joinPeerTubeServer.setLatestVersion('15.4.2')
89
90 await wait(3000)
91 await checkNewPeerTubeVersion(baseParams, '15.4.2', 'presence')
92 })
93
94 it('Should not send the same notification to admins', async function () {
95 this.timeout(30000)
96
97 await wait(3000)
98 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(1)
99 })
100
101 it('Should not have sent a notification to users', async function () {
102 this.timeout(30000)
103
104 expect(userNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(0)
105 })
106
107 it('Should send a new notification after a new release', async function () {
108 this.timeout(30000)
109
110 joinPeerTubeServer.setLatestVersion('15.4.3')
111
112 await wait(3000)
113 await checkNewPeerTubeVersion(baseParams, '15.4.3', 'presence')
114 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
115 })
116 })
117
118 describe('Latest plugin version notification', function () {
119
120 it('Should not send a notification to admins if there is no new plugin version', async function () {
121 this.timeout(30000)
122
123 await wait(6000)
124 await checkNewPluginVersion(baseParams, PluginType.PLUGIN, 'hello-world', 'absence')
125 })
126
127 it('Should send a notification to admins on new plugin version', async function () {
128 this.timeout(30000)
129
130 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1')
131 await setPluginLatestVersion(server.internalServerNumber, 'hello-world', '0.0.1')
132 await wait(6000)
133
134 await checkNewPluginVersion(baseParams, PluginType.PLUGIN, 'hello-world', 'presence')
135 })
136
137 it('Should not send the same notification to admins', async function () {
138 this.timeout(30000)
139
140 await wait(6000)
141
142 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PLUGIN_VERSION)).to.have.lengthOf(1)
143 })
144
145 it('Should not have sent a notification to users', async function () {
146 expect(userNotifications.filter(n => n.type === UserNotificationType.NEW_PLUGIN_VERSION)).to.have.lengthOf(0)
147 })
148
149 it('Should send a new notification after a new plugin release', async function () {
150 this.timeout(30000)
151
152 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1')
153 await setPluginLatestVersion(server.internalServerNumber, 'hello-world', '0.0.1')
154 await wait(6000)
155
156 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
157 })
158 })
159
160 after(async function () {
161 MockSmtpServer.Instance.kill()
162
163 await cleanupTests([ server ])
164 })
165})
diff --git a/server/tests/api/notifications/index.ts b/server/tests/api/notifications/index.ts
index bd07a339e..8caa30a3d 100644
--- a/server/tests/api/notifications/index.ts
+++ b/server/tests/api/notifications/index.ts
@@ -1,3 +1,4 @@
1import './admin-notifications'
1import './comments-notifications' 2import './comments-notifications'
2import './moderation-notifications' 3import './moderation-notifications'
3import './notifications-api' 4import './notifications-api'
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts
index 4c00d97f8..4ce6675b6 100644
--- a/server/tests/api/notifications/moderation-notifications.ts
+++ b/server/tests/api/notifications/moderation-notifications.ts
@@ -2,8 +2,9 @@
2 2
3import 'mocha' 3import 'mocha'
4import { v4 as uuidv4 } from 'uuid' 4import { v4 as uuidv4 } from 'uuid'
5 5import { AbuseState } from '@shared/models'
6import { 6import {
7 addAbuseMessage,
7 addVideoCommentThread, 8 addVideoCommentThread,
8 addVideoToBlacklist, 9 addVideoToBlacklist,
9 cleanupTests, 10 cleanupTests,
@@ -20,18 +21,19 @@ import {
20 removeVideoFromBlacklist, 21 removeVideoFromBlacklist,
21 reportAbuse, 22 reportAbuse,
22 unfollow, 23 unfollow,
24 updateAbuse,
23 updateCustomConfig, 25 updateCustomConfig,
24 updateCustomSubConfig, 26 updateCustomSubConfig,
25 wait, 27 wait
26 updateAbuse,
27 addAbuseMessage
28} from '../../../../shared/extra-utils' 28} from '../../../../shared/extra-utils'
29import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index' 29import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
30import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' 30import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
31import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 31import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
32import { 32import {
33 checkAbuseStateChange,
33 checkAutoInstanceFollowing, 34 checkAutoInstanceFollowing,
34 CheckerBaseParams, 35 CheckerBaseParams,
36 checkNewAbuseMessage,
35 checkNewAccountAbuseForModerators, 37 checkNewAccountAbuseForModerators,
36 checkNewBlacklistOnMyVideo, 38 checkNewBlacklistOnMyVideo,
37 checkNewCommentAbuseForModerators, 39 checkNewCommentAbuseForModerators,
@@ -41,15 +43,12 @@ import {
41 checkUserRegistered, 43 checkUserRegistered,
42 checkVideoAutoBlacklistForModerators, 44 checkVideoAutoBlacklistForModerators,
43 checkVideoIsPublished, 45 checkVideoIsPublished,
44 prepareNotificationsTest, 46 prepareNotificationsTest
45 checkAbuseStateChange,
46 checkNewAbuseMessage
47} from '../../../../shared/extra-utils/users/user-notifications' 47} from '../../../../shared/extra-utils/users/user-notifications'
48import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions' 48import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions'
49import { CustomConfig } from '../../../../shared/models/server' 49import { CustomConfig } from '../../../../shared/models/server'
50import { UserNotification } from '../../../../shared/models/users' 50import { UserNotification } from '../../../../shared/models/users'
51import { VideoPrivacy } from '../../../../shared/models/videos' 51import { VideoPrivacy } from '../../../../shared/models/videos'
52import { AbuseState } from '@shared/models'
53 52
54describe('Test moderation notifications', function () { 53describe('Test moderation notifications', function () {
55 let servers: ServerInfo[] = [] 54 let servers: ServerInfo[] = []
@@ -364,16 +363,7 @@ describe('Test moderation notifications', function () {
364 363
365 describe('New instance follows', function () { 364 describe('New instance follows', function () {
366 const instanceIndexServer = new MockInstancesIndex() 365 const instanceIndexServer = new MockInstancesIndex()
367 const config = { 366 let config: any
368 followings: {
369 instance: {
370 autoFollowIndex: {
371 indexUrl: 'http://localhost:42101/api/v1/instances/hosts',
372 enabled: true
373 }
374 }
375 }
376 }
377 let baseParams: CheckerBaseParams 367 let baseParams: CheckerBaseParams
378 368
379 before(async () => { 369 before(async () => {
@@ -384,8 +374,19 @@ describe('Test moderation notifications', function () {
384 token: servers[0].accessToken 374 token: servers[0].accessToken
385 } 375 }
386 376
387 await instanceIndexServer.initialize() 377 const port = await instanceIndexServer.initialize()
388 instanceIndexServer.addInstance(servers[1].host) 378 instanceIndexServer.addInstance(servers[1].host)
379
380 config = {
381 followings: {
382 instance: {
383 autoFollowIndex: {
384 indexUrl: `http://localhost:${port}/api/v1/instances/hosts`,
385 enabled: true
386 }
387 }
388 }
389 }
389 }) 390 })
390 391
391 it('Should send a notification only to admin when there is a new instance follower', async function () { 392 it('Should send a notification only to admin when there is a new instance follower', async function () {
diff --git a/server/tests/api/server/auto-follows.ts b/server/tests/api/server/auto-follows.ts
index e04d70af4..1519b263f 100644
--- a/server/tests/api/server/auto-follows.ts
+++ b/server/tests/api/server/auto-follows.ts
@@ -1,7 +1,7 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 acceptFollower, 6 acceptFollower,
7 cleanupTests, 7 cleanupTests,
@@ -153,9 +153,10 @@ describe('Test auto follows', function () {
153 153
154 describe('Auto follow index', function () { 154 describe('Auto follow index', function () {
155 const instanceIndexServer = new MockInstancesIndex() 155 const instanceIndexServer = new MockInstancesIndex()
156 let port: number
156 157
157 before(async () => { 158 before(async () => {
158 await instanceIndexServer.initialize() 159 port = await instanceIndexServer.initialize()
159 }) 160 })
160 161
161 it('Should not auto follow index if the option is not enabled', async function () { 162 it('Should not auto follow index if the option is not enabled', async function () {
@@ -177,7 +178,7 @@ describe('Test auto follows', function () {
177 followings: { 178 followings: {
178 instance: { 179 instance: {
179 autoFollowIndex: { 180 autoFollowIndex: {
180 indexUrl: 'http://localhost:42101/api/v1/instances/hosts', 181 indexUrl: `http://localhost:${port}/api/v1/instances/hosts`,
181 enabled: true 182 enabled: true
182 } 183 }
183 } 184 }
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 0b0f48d22..1d9ea31df 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -12,6 +12,7 @@ import {
12 getConfig, 12 getConfig,
13 getCustomConfig, 13 getCustomConfig,
14 killallServers, 14 killallServers,
15 makeGetRequest,
15 parallelTests, 16 parallelTests,
16 registerUser, 17 registerUser,
17 reRunServer, 18 reRunServer,
@@ -508,6 +509,39 @@ describe('Test config', function () {
508 checkInitialConfig(server, data) 509 checkInitialConfig(server, data)
509 }) 510 })
510 511
512 it('Should enable frameguard', async function () {
513 this.timeout(25000)
514
515 {
516 const res = await makeGetRequest({
517 url: server.url,
518 path: '/api/v1/config',
519 statusCodeExpected: 200
520 })
521
522 expect(res.headers['x-frame-options']).to.exist
523 }
524
525 killallServers([ server ])
526
527 const config = {
528 security: {
529 frameguard: { enabled: false }
530 }
531 }
532 server = await reRunServer(server, config)
533
534 {
535 const res = await makeGetRequest({
536 url: server.url,
537 path: '/api/v1/config',
538 statusCodeExpected: 200
539 })
540
541 expect(res.headers['x-frame-options']).to.not.exist
542 }
543 })
544
511 after(async function () { 545 after(async function () {
512 await cleanupTests([ server ]) 546 await cleanupTests([ server ])
513 }) 547 })
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index 043754e70..f3ba11950 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -348,8 +348,8 @@ describe('Test handle downs', function () {
348 348
349 for (let i = 0; i < 3; i++) { 349 for (let i = 0; i < 3; i++) {
350 await getVideo(servers[1].url, videoIdsServer1[i]) 350 await getVideo(servers[1].url, videoIdsServer1[i])
351 await wait(1000)
352 await waitJobs([ servers[1] ]) 351 await waitJobs([ servers[1] ])
352 await wait(1500)
353 } 353 }
354 354
355 for (const id of videoIdsServer1) { 355 for (const id of videoIdsServer1) {
diff --git a/server/tests/api/server/services.ts b/server/tests/api/server/services.ts
index df910c111..f0fa91674 100644
--- a/server/tests/api/server/services.ts
+++ b/server/tests/api/server/services.ts
@@ -20,6 +20,7 @@ const expect = chai.expect
20describe('Test services', function () { 20describe('Test services', function () {
21 let server: ServerInfo = null 21 let server: ServerInfo = null
22 let playlistUUID: string 22 let playlistUUID: string
23 let playlistDisplayName: string
23 let video: Video 24 let video: Video
24 25
25 before(async function () { 26 before(async function () {
@@ -52,6 +53,7 @@ describe('Test services', function () {
52 }) 53 })
53 54
54 playlistUUID = res.body.videoPlaylist.uuid 55 playlistUUID = res.body.videoPlaylist.uuid
56 playlistDisplayName = 'The Life and Times of Scrooge McDuck'
55 57
56 await addVideoInPlaylist({ 58 await addVideoInPlaylist({
57 url: server.url, 59 url: server.url,
@@ -69,7 +71,7 @@ describe('Test services', function () {
69 71
70 const res = await getOEmbed(server.url, oembedUrl) 72 const res = await getOEmbed(server.url, oembedUrl)
71 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 73 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
72 `src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 74 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
73 'frameborder="0" allowfullscreen></iframe>' 75 'frameborder="0" allowfullscreen></iframe>'
74 const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath 76 const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath
75 77
@@ -88,7 +90,7 @@ describe('Test services', function () {
88 90
89 const res = await getOEmbed(server.url, oembedUrl) 91 const res = await getOEmbed(server.url, oembedUrl)
90 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 92 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
91 `src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` + 93 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
92 'frameborder="0" allowfullscreen></iframe>' 94 'frameborder="0" allowfullscreen></iframe>'
93 95
94 expect(res.body.html).to.equal(expectedHtml) 96 expect(res.body.html).to.equal(expectedHtml)
@@ -97,8 +99,8 @@ describe('Test services', function () {
97 expect(res.body.width).to.equal(560) 99 expect(res.body.width).to.equal(560)
98 expect(res.body.height).to.equal(315) 100 expect(res.body.height).to.equal(315)
99 expect(res.body.thumbnail_url).exist 101 expect(res.body.thumbnail_url).exist
100 expect(res.body.thumbnail_width).to.equal(223) 102 expect(res.body.thumbnail_width).to.equal(280)
101 expect(res.body.thumbnail_height).to.equal(122) 103 expect(res.body.thumbnail_height).to.equal(157)
102 }) 104 })
103 105
104 it('Should have a valid oEmbed response with small max height query', async function () { 106 it('Should have a valid oEmbed response with small max height query', async function () {
@@ -109,7 +111,7 @@ describe('Test services', function () {
109 111
110 const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) 112 const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth)
111 const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' + 113 const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' +
112 `src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 114 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
113 'frameborder="0" allowfullscreen></iframe>' 115 'frameborder="0" allowfullscreen></iframe>'
114 116
115 expect(res.body.html).to.equal(expectedHtml) 117 expect(res.body.html).to.equal(expectedHtml)
diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts
index eb474c1f5..304181a6d 100644
--- a/server/tests/api/server/stats.ts
+++ b/server/tests/api/server/stats.ts
@@ -3,8 +3,10 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
6 cleanupTests, 7 cleanupTests,
7 createUser, 8 createUser,
9 createVideoPlaylist,
8 doubleFollow, 10 doubleFollow,
9 flushAndRunMultipleServers, 11 flushAndRunMultipleServers,
10 follow, 12 follow,
@@ -21,12 +23,14 @@ import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
21import { getStats } from '../../../../shared/extra-utils/server/stats' 23import { getStats } from '../../../../shared/extra-utils/server/stats'
22import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments' 24import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
23import { ServerStats } from '../../../../shared/models/server/server-stats.model' 25import { ServerStats } from '../../../../shared/models/server/server-stats.model'
26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
24import { ActivityType } from '@shared/models' 27import { ActivityType } from '@shared/models'
25 28
26const expect = chai.expect 29const expect = chai.expect
27 30
28describe('Test stats (excluding redundancy)', function () { 31describe('Test stats (excluding redundancy)', function () {
29 let servers: ServerInfo[] = [] 32 let servers: ServerInfo[] = []
33 let channelId
30 const user = { 34 const user = {
31 username: 'user1', 35 username: 'user1',
32 password: 'super_password' 36 password: 'super_password'
@@ -70,6 +74,7 @@ describe('Test stats (excluding redundancy)', function () {
70 expect(data.totalVideos).to.equal(1) 74 expect(data.totalVideos).to.equal(1)
71 expect(data.totalInstanceFollowers).to.equal(2) 75 expect(data.totalInstanceFollowers).to.equal(2)
72 expect(data.totalInstanceFollowing).to.equal(1) 76 expect(data.totalInstanceFollowing).to.equal(1)
77 expect(data.totalLocalPlaylists).to.equal(0)
73 }) 78 })
74 79
75 it('Should have the correct stats on instance 2', async function () { 80 it('Should have the correct stats on instance 2', async function () {
@@ -85,6 +90,7 @@ describe('Test stats (excluding redundancy)', function () {
85 expect(data.totalVideos).to.equal(1) 90 expect(data.totalVideos).to.equal(1)
86 expect(data.totalInstanceFollowers).to.equal(1) 91 expect(data.totalInstanceFollowers).to.equal(1)
87 expect(data.totalInstanceFollowing).to.equal(1) 92 expect(data.totalInstanceFollowing).to.equal(1)
93 expect(data.totalLocalPlaylists).to.equal(0)
88 }) 94 })
89 95
90 it('Should have the correct stats on instance 3', async function () { 96 it('Should have the correct stats on instance 3', async function () {
@@ -99,6 +105,7 @@ describe('Test stats (excluding redundancy)', function () {
99 expect(data.totalVideos).to.equal(1) 105 expect(data.totalVideos).to.equal(1)
100 expect(data.totalInstanceFollowing).to.equal(1) 106 expect(data.totalInstanceFollowing).to.equal(1)
101 expect(data.totalInstanceFollowers).to.equal(0) 107 expect(data.totalInstanceFollowers).to.equal(0)
108 expect(data.totalLocalPlaylists).to.equal(0)
102 }) 109 })
103 110
104 it('Should have the correct total videos stats after an unfollow', async function () { 111 it('Should have the correct total videos stats after an unfollow', async function () {
@@ -113,7 +120,7 @@ describe('Test stats (excluding redundancy)', function () {
113 expect(data.totalVideos).to.equal(0) 120 expect(data.totalVideos).to.equal(0)
114 }) 121 })
115 122
116 it('Should have the correct active users stats', async function () { 123 it('Should have the correct active user stats', async function () {
117 const server = servers[0] 124 const server = servers[0]
118 125
119 { 126 {
@@ -135,6 +142,69 @@ describe('Test stats (excluding redundancy)', function () {
135 } 142 }
136 }) 143 })
137 144
145 it('Should have the correct active channel stats', async function () {
146 const server = servers[0]
147
148 {
149 const res = await getStats(server.url)
150 const data: ServerStats = res.body
151 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1)
152 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1)
153 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1)
154 }
155
156 {
157 const channelAttributes = {
158 name: 'stats_channel',
159 displayName: 'My stats channel'
160 }
161 const resChannel = await addVideoChannel(server.url, server.accessToken, channelAttributes)
162 channelId = resChannel.body.videoChannel.id
163
164 const res = await getStats(server.url)
165 const data: ServerStats = res.body
166 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1)
167 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1)
168 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1)
169 }
170
171 {
172 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.webm', channelId })
173
174 const res = await getStats(server.url)
175 const data: ServerStats = res.body
176 expect(data.totalLocalDailyActiveVideoChannels).to.equal(2)
177 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(2)
178 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(2)
179 }
180 })
181
182 it('Should have the correct playlist stats', async function () {
183 const server = servers[0]
184
185 {
186 const resStats = await getStats(server.url)
187 const dataStats: ServerStats = resStats.body
188 expect(dataStats.totalLocalPlaylists).to.equal(0)
189 }
190
191 {
192 await createVideoPlaylist({
193 url: server.url,
194 token: server.accessToken,
195 playlistAttrs: {
196 displayName: 'playlist for count',
197 privacy: VideoPlaylistPrivacy.PUBLIC,
198 videoChannelId: channelId
199 }
200 })
201
202 const resStats = await getStats(server.url)
203 const dataStats: ServerStats = resStats.body
204 expect(dataStats.totalLocalPlaylists).to.equal(1)
205 }
206 })
207
138 it('Should correctly count video file sizes if transcoding is enabled', async function () { 208 it('Should correctly count video file sizes if transcoding is enabled', async function () {
139 this.timeout(60000) 209 this.timeout(60000)
140 210
@@ -173,8 +243,8 @@ describe('Test stats (excluding redundancy)', function () {
173 { 243 {
174 const res = await getStats(servers[0].url) 244 const res = await getStats(servers[0].url)
175 const data: ServerStats = res.body 245 const data: ServerStats = res.body
176 expect(data.totalLocalVideoFilesSize).to.be.greaterThan(300000) 246 expect(data.totalLocalVideoFilesSize).to.be.greaterThan(500000)
177 expect(data.totalLocalVideoFilesSize).to.be.lessThan(400000) 247 expect(data.totalLocalVideoFilesSize).to.be.lessThan(600000)
178 } 248 }
179 }) 249 })
180 250
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 62a59033f..cea98aac7 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -4,10 +4,12 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models' 5import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models'
6import { CustomConfig } from '@shared/models/server' 6import { CustomConfig } from '@shared/models/server'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 8import {
8 addVideoCommentThread, 9 addVideoCommentThread,
9 blockUser, 10 blockUser,
10 cleanupTests, 11 cleanupTests,
12 closeAllSequelize,
11 createUser, 13 createUser,
12 deleteMe, 14 deleteMe,
13 flushAndRunServer, 15 flushAndRunServer,
@@ -24,6 +26,7 @@ import {
24 getVideoChannel, 26 getVideoChannel,
25 getVideosList, 27 getVideosList,
26 installPlugin, 28 installPlugin,
29 killallServers,
27 login, 30 login,
28 makePutBodyRequest, 31 makePutBodyRequest,
29 rateVideo, 32 rateVideo,
@@ -31,7 +34,9 @@ import {
31 removeUser, 34 removeUser,
32 removeVideo, 35 removeVideo,
33 reportAbuse, 36 reportAbuse,
37 reRunServer,
34 ServerInfo, 38 ServerInfo,
39 setTokenField,
35 testImage, 40 testImage,
36 unblockUser, 41 unblockUser,
37 updateAbuse, 42 updateAbuse,
@@ -44,10 +49,9 @@ import {
44 waitJobs 49 waitJobs
45} from '../../../../shared/extra-utils' 50} from '../../../../shared/extra-utils'
46import { follow } from '../../../../shared/extra-utils/server/follows' 51import { follow } from '../../../../shared/extra-utils/server/follows'
47import { logout, serverLogin, setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' 52import { logout, refreshToken, setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
48import { getMyVideos } from '../../../../shared/extra-utils/videos/videos' 53import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
49import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' 54import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
50import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
51 55
52const expect = chai.expect 56const expect = chai.expect
53 57
@@ -89,6 +93,7 @@ describe('Test users', function () {
89 const client = { id: 'client', secret: server.client.secret } 93 const client = { id: 'client', secret: server.client.secret }
90 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) 94 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400)
91 95
96 expect(res.body.code).to.equal('invalid_client')
92 expect(res.body.error).to.contain('client is invalid') 97 expect(res.body.error).to.contain('client is invalid')
93 }) 98 })
94 99
@@ -96,6 +101,7 @@ describe('Test users', function () {
96 const client = { id: server.client.id, secret: 'coucou' } 101 const client = { id: server.client.id, secret: 'coucou' }
97 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) 102 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400)
98 103
104 expect(res.body.code).to.equal('invalid_client')
99 expect(res.body.error).to.contain('client is invalid') 105 expect(res.body.error).to.contain('client is invalid')
100 }) 106 })
101 }) 107 })
@@ -106,6 +112,7 @@ describe('Test users', function () {
106 const user = { username: 'captain crochet', password: server.user.password } 112 const user = { username: 'captain crochet', password: server.user.password }
107 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) 113 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400)
108 114
115 expect(res.body.code).to.equal('invalid_grant')
109 expect(res.body.error).to.contain('credentials are invalid') 116 expect(res.body.error).to.contain('credentials are invalid')
110 }) 117 })
111 118
@@ -113,6 +120,7 @@ describe('Test users', function () {
113 const user = { username: server.user.username, password: 'mew_three' } 120 const user = { username: server.user.username, password: 'mew_three' }
114 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) 121 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400)
115 122
123 expect(res.body.code).to.equal('invalid_grant')
116 expect(res.body.error).to.contain('credentials are invalid') 124 expect(res.body.error).to.contain('credentials are invalid')
117 }) 125 })
118 126
@@ -245,12 +253,44 @@ describe('Test users', function () {
245 }) 253 })
246 254
247 it('Should be able to login again', async function () { 255 it('Should be able to login again', async function () {
248 server.accessToken = await serverLogin(server) 256 const res = await login(server.url, server.client, server.user)
257 server.accessToken = res.body.access_token
258 server.refreshToken = res.body.refresh_token
259 })
260
261 it('Should be able to get my user information again', async function () {
262 await getMyUserInformation(server.url, server.accessToken)
263 })
264
265 it('Should have an expired access token', async function () {
266 this.timeout(15000)
267
268 await setTokenField(server.internalServerNumber, server.accessToken, 'accessTokenExpiresAt', new Date().toISOString())
269 await setTokenField(server.internalServerNumber, server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString())
270
271 killallServers([ server ])
272 await reRunServer(server)
273
274 await getMyUserInformation(server.url, server.accessToken, 401)
275 })
276
277 it('Should not be able to refresh an access token with an expired refresh token', async function () {
278 await refreshToken(server, server.refreshToken, 400)
249 }) 279 })
250 280
251 it('Should have an expired access token') 281 it('Should refresh the token', async function () {
282 this.timeout(15000)
283
284 const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString()
285 await setTokenField(server.internalServerNumber, server.accessToken, 'refreshTokenExpiresAt', futureDate)
252 286
253 it('Should refresh the token') 287 killallServers([ server ])
288 await reRunServer(server)
289
290 const res = await refreshToken(server, server.refreshToken)
291 server.accessToken = res.body.access_token
292 server.refreshToken = res.body.refresh_token
293 })
254 294
255 it('Should be able to get my user information again', async function () { 295 it('Should be able to get my user information again', async function () {
256 await getMyUserInformation(server.url, server.accessToken) 296 await getMyUserInformation(server.url, server.accessToken)
@@ -976,6 +1016,7 @@ describe('Test users', function () {
976 }) 1016 })
977 1017
978 after(async function () { 1018 after(async function () {
1019 await closeAllSequelize([ server ])
979 await cleanupTests([ server ]) 1020 await cleanupTests([ server ])
980 }) 1021 })
981}) 1022})
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index 367f99fdd..d12d58e75 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -2,16 +2,20 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { basename } from 'path'
5import { 6import {
6 cleanupTests, 7 cleanupTests,
7 createUser, 8 createUser,
9 deleteVideoChannelImage,
8 doubleFollow, 10 doubleFollow,
9 flushAndRunMultipleServers, 11 flushAndRunMultipleServers,
12 getActorImage,
10 getVideo, 13 getVideo,
14 getVideoChannel,
11 getVideoChannelVideos, 15 getVideoChannelVideos,
12 testImage, 16 testImage,
13 updateVideo, 17 updateVideo,
14 updateVideoChannelAvatar, 18 updateVideoChannelImage,
15 uploadVideo, 19 uploadVideo,
16 userLogin, 20 userLogin,
17 wait 21 wait
@@ -21,7 +25,6 @@ import {
21 deleteVideoChannel, 25 deleteVideoChannel,
22 getAccountVideoChannelsList, 26 getAccountVideoChannelsList,
23 getMyUserInformation, 27 getMyUserInformation,
24 getVideoChannel,
25 getVideoChannelsList, 28 getVideoChannelsList,
26 ServerInfo, 29 ServerInfo,
27 setAccessTokensToServers, 30 setAccessTokensToServers,
@@ -30,9 +33,17 @@ import {
30} from '../../../../shared/extra-utils/index' 33} from '../../../../shared/extra-utils/index'
31import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 34import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
32import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index' 35import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index'
36import { ACTOR_IMAGES_SIZE } from '@server/initializers/constants'
33 37
34const expect = chai.expect 38const expect = chai.expect
35 39
40async function findChannel (server: ServerInfo, channelId: number) {
41 const res = await getVideoChannelsList(server.url, 0, 5, '-name')
42 const videoChannel = res.body.data.find(c => c.id === channelId)
43
44 return videoChannel as VideoChannel
45}
46
36describe('Test video channels', function () { 47describe('Test video channels', function () {
37 let servers: ServerInfo[] 48 let servers: ServerInfo[]
38 let userInfo: User 49 let userInfo: User
@@ -262,38 +273,94 @@ describe('Test video channels', function () {
262 }) 273 })
263 274
264 it('Should update video channel avatar', async function () { 275 it('Should update video channel avatar', async function () {
265 this.timeout(5000) 276 this.timeout(15000)
266 277
267 const fixture = 'avatar.png' 278 const fixture = 'avatar.png'
268 279
269 await updateVideoChannelAvatar({ 280 await updateVideoChannelImage({
270 url: servers[0].url, 281 url: servers[0].url,
271 accessToken: servers[0].accessToken, 282 accessToken: servers[0].accessToken,
272 videoChannelName: 'second_video_channel', 283 videoChannelName: 'second_video_channel',
273 fixture 284 fixture,
285 type: 'avatar'
274 }) 286 })
275 287
276 await waitJobs(servers) 288 await waitJobs(servers)
289
290 for (const server of servers) {
291 const videoChannel = await findChannel(server, secondVideoChannelId)
292
293 await testImage(server.url, 'avatar-resized', videoChannel.avatar.path, '.png')
294
295 const row = await getActorImage(server.internalServerNumber, basename(videoChannel.avatar.path))
296 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.AVATARS.height)
297 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.AVATARS.width)
298 }
277 }) 299 })
278 300
279 it('Should have video channel avatar updated', async function () { 301 it('Should update video channel banner', async function () {
302 this.timeout(15000)
303
304 const fixture = 'banner.jpg'
305
306 await updateVideoChannelImage({
307 url: servers[0].url,
308 accessToken: servers[0].accessToken,
309 videoChannelName: 'second_video_channel',
310 fixture,
311 type: 'banner'
312 })
313
314 await waitJobs(servers)
315
280 for (const server of servers) { 316 for (const server of servers) {
281 const res = await getVideoChannelsList(server.url, 0, 1, '-name') 317 const res = await getVideoChannel(server.url, 'second_video_channel@' + servers[0].host)
318 const videoChannel = res.body
282 319
283 const videoChannel = res.body.data.find(c => c.id === secondVideoChannelId) 320 await testImage(server.url, 'banner-resized', videoChannel.banner.path)
284 321
285 await testImage(server.url, 'avatar-resized', videoChannel.avatar.path, '.png') 322 const row = await getActorImage(server.internalServerNumber, basename(videoChannel.banner.path))
323 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.BANNERS.height)
324 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.BANNERS.width)
325 }
326 })
327
328 it('Should delete the video channel avatar', async function () {
329 this.timeout(15000)
330
331 await deleteVideoChannelImage({
332 url: servers[0].url,
333 accessToken: servers[0].accessToken,
334 videoChannelName: 'second_video_channel',
335 type: 'avatar'
336 })
337
338 await waitJobs(servers)
339
340 for (const server of servers) {
341 const videoChannel = await findChannel(server, secondVideoChannelId)
342
343 expect(videoChannel.avatar).to.be.null
286 } 344 }
287 }) 345 })
288 346
289 it('Should get video channel', async function () { 347 it('Should delete the video channel banner', async function () {
290 const res = await getVideoChannel(servers[0].url, 'second_video_channel') 348 this.timeout(15000)
349
350 await deleteVideoChannelImage({
351 url: servers[0].url,
352 accessToken: servers[0].accessToken,
353 videoChannelName: 'second_video_channel',
354 type: 'banner'
355 })
356
357 await waitJobs(servers)
358
359 for (const server of servers) {
360 const videoChannel = await findChannel(server, secondVideoChannelId)
291 361
292 const videoChannel = res.body 362 expect(videoChannel.banner).to.be.null
293 expect(videoChannel.name).to.equal('second_video_channel') 363 }
294 expect(videoChannel.displayName).to.equal('video channel updated')
295 expect(videoChannel.description).to.equal('video channel description updated')
296 expect(videoChannel.support).to.equal('video channel support text updated')
297 }) 364 })
298 365
299 it('Should list the second video channel videos', async function () { 366 it('Should list the second video channel videos', async function () {
diff --git a/server/tests/cli/index.ts b/server/tests/cli/index.ts
index 242589010..7e6eebd17 100644
--- a/server/tests/cli/index.ts
+++ b/server/tests/cli/index.ts
@@ -6,5 +6,6 @@ import './peertube'
6import './plugins' 6import './plugins'
7import './print-transcode-command' 7import './print-transcode-command'
8import './prune-storage' 8import './prune-storage'
9import './regenerate-thumbnails'
9import './reset-password' 10import './reset-password'
10import './update-host' 11import './update-host'
diff --git a/server/tests/cli/print-transcode-command.ts b/server/tests/cli/print-transcode-command.ts
index 4a7988d4d..2d7255db7 100644
--- a/server/tests/cli/print-transcode-command.ts
+++ b/server/tests/cli/print-transcode-command.ts
@@ -22,7 +22,8 @@ describe('Test create transcoding jobs', function () {
22 const command = await execCLI(`npm run print-transcode-command -- ${fixturePath} -r ${resolution}`) 22 const command = await execCLI(`npm run print-transcode-command -- ${fixturePath} -r ${resolution}`)
23 const targetBitrate = Math.min(getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS), bitrate) 23 const targetBitrate = Math.min(getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS), bitrate)
24 24
25 expect(command).to.includes(`-y -acodec aac -vcodec libx264 -filter:v scale=w=trunc(oh*a/2)*2:h=${resolution}`) 25 expect(command).to.includes(`-vf scale=w=-2:h=${resolution}`)
26 expect(command).to.includes(`-y -acodec aac -vcodec libx264`)
26 expect(command).to.includes('-f mp4') 27 expect(command).to.includes('-f mp4')
27 expect(command).to.includes('-movflags faststart') 28 expect(command).to.includes('-movflags faststart')
28 expect(command).to.includes('-b:a 256k') 29 expect(command).to.includes('-b:a 256k')
diff --git a/server/tests/cli/regenerate-thumbnails.ts b/server/tests/cli/regenerate-thumbnails.ts
new file mode 100644
index 000000000..8acb9f263
--- /dev/null
+++ b/server/tests/cli/regenerate-thumbnails.ts
@@ -0,0 +1,124 @@
1import 'mocha'
2import { expect } from 'chai'
3import { writeFile } from 'fs-extra'
4import { basename, join } from 'path'
5import { Video, VideoDetails } from '@shared/models'
6import {
7 buildServerDirectory,
8 cleanupTests,
9 doubleFollow,
10 execCLI,
11 flushAndRunMultipleServers,
12 getEnvCli,
13 getVideo,
14 makeRawRequest,
15 ServerInfo,
16 setAccessTokensToServers,
17 uploadVideoAndGetId,
18 waitJobs
19} from '../../../shared/extra-utils'
20import { HttpStatusCode } from '@shared/core-utils'
21
22async function testThumbnail (server: ServerInfo, videoId: number | string) {
23 const res = await getVideo(server.url, videoId)
24 const video: VideoDetails = res.body
25
26 const res1 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
27 expect(res1.body).to.not.have.lengthOf(0)
28
29 const res2 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
30 expect(res2.body).to.not.have.lengthOf(0)
31}
32
33describe('Test regenerate thumbnails script', function () {
34 let servers: ServerInfo[]
35
36 let video1: Video
37 let video2: Video
38 let remoteVideo: Video
39
40 let thumbnail1Path: string
41 let thumbnailRemotePath: string
42
43 before(async function () {
44 this.timeout(60000)
45
46 servers = await flushAndRunMultipleServers(2)
47 await setAccessTokensToServers(servers)
48
49 await doubleFollow(servers[0], servers[1])
50
51 {
52 const videoUUID1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).uuid
53 video1 = await (getVideo(servers[0].url, videoUUID1).then(res => res.body))
54
55 thumbnail1Path = join(buildServerDirectory(servers[0], 'thumbnails'), basename(video1.thumbnailPath))
56
57 const videoUUID2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).uuid
58 video2 = await (getVideo(servers[0].url, videoUUID2).then(res => res.body))
59 }
60
61 {
62 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3' })).uuid
63 await waitJobs(servers)
64
65 remoteVideo = await (getVideo(servers[0].url, videoUUID).then(res => res.body))
66
67 thumbnailRemotePath = join(buildServerDirectory(servers[0], 'thumbnails'), basename(remoteVideo.thumbnailPath))
68 }
69
70 await writeFile(thumbnail1Path, '')
71 await writeFile(thumbnailRemotePath, '')
72 })
73
74 it('Should have empty thumbnails', async function () {
75 {
76 const res = await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.OK_200)
77 expect(res.body).to.have.lengthOf(0)
78 }
79
80 {
81 const res = await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.OK_200)
82 expect(res.body).to.not.have.lengthOf(0)
83 }
84
85 {
86 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200)
87 expect(res.body).to.have.lengthOf(0)
88 }
89 })
90
91 it('Should regenerate local thumbnails from the CLI', async function () {
92 this.timeout(15000)
93
94 const env = getEnvCli(servers[0])
95 await execCLI(`${env} npm run regenerate-thumbnails`)
96 })
97
98 it('Should have generated new thumbnail files', async function () {
99 await testThumbnail(servers[0], video1.uuid)
100 await testThumbnail(servers[0], video2.uuid)
101
102 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200)
103 expect(res.body).to.have.lengthOf(0)
104 })
105
106 it('Should have deleted old thumbnail files', async function () {
107 {
108 await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.NOT_FOUND_404)
109 }
110
111 {
112 await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.NOT_FOUND_404)
113 }
114
115 {
116 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200)
117 expect(res.body).to.have.lengthOf(0)
118 }
119 })
120
121 after(async function () {
122 await cleanupTests(servers)
123 })
124})
diff --git a/server/tests/client.ts b/server/tests/client.ts
index d608764ee..3c99bcd1f 100644
--- a/server/tests/client.ts
+++ b/server/tests/client.ts
@@ -39,7 +39,8 @@ describe('Test a client controllers', function () {
39 let account: Account 39 let account: Account
40 40
41 const videoName = 'my super name for server 1' 41 const videoName = 'my super name for server 1'
42 const videoDescription = 'my super description for server 1' 42 const videoDescription = 'my<br> super __description__ for *server* 1<p></p>'
43 const videoDescriptionPlainText = 'my super description for server 1'
43 44
44 const playlistName = 'super playlist name' 45 const playlistName = 'super playlist name'
45 const playlistDescription = 'super playlist description' 46 const playlistDescription = 'super playlist description'
@@ -169,7 +170,7 @@ describe('Test a client controllers', function () {
169 .expect(HttpStatusCode.OK_200) 170 .expect(HttpStatusCode.OK_200)
170 171
171 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`) 172 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
172 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`) 173 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
173 expect(res.text).to.contain('<meta property="og:type" content="video" />') 174 expect(res.text).to.contain('<meta property="og:type" content="video" />')
174 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`) 175 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
175 }) 176 })
@@ -181,7 +182,7 @@ describe('Test a client controllers', function () {
181 .expect(HttpStatusCode.OK_200) 182 .expect(HttpStatusCode.OK_200)
182 183
183 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`) 184 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
184 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`) 185 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
185 expect(res.text).to.contain('<meta property="og:type" content="video" />') 186 expect(res.text).to.contain('<meta property="og:type" content="video" />')
186 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`) 187 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
187 }) 188 })
@@ -210,7 +211,7 @@ describe('Test a client controllers', function () {
210 expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />') 211 expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
211 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />') 212 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
212 expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`) 213 expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
213 expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescription}" />`) 214 expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescriptionPlainText}" />`)
214 }) 215 })
215 216
216 it('Should have valid twitter card on the watch playlist page', async function () { 217 it('Should have valid twitter card on the watch playlist page', async function () {
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index f1055ea44..7bad81751 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -2,7 +2,7 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import * as libxmljs from 'libxmljs' 5import * as xmlParser from 'fast-xml-parser'
6import { 6import {
7 addAccountToAccountBlocklist, 7 addAccountToAccountBlocklist,
8 addAccountToServerBlocklist, 8 addAccountToServerBlocklist,
@@ -139,12 +139,15 @@ describe('Test syndication feeds', () => {
139 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { 139 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () {
140 for (const server of servers) { 140 for (const server of servers) {
141 const rss = await getXMLfeed(server.url, 'videos') 141 const rss = await getXMLfeed(server.url, 'videos')
142 const xmlDoc = libxmljs.parseXmlString(rss.text) 142 expect(xmlParser.validate(rss.text)).to.be.true
143 const xmlEnclosure = xmlDoc.get('/rss/channel/item/enclosure') 143
144 expect(xmlEnclosure).to.exist 144 const xmlDoc = xmlParser.parse(rss.text, { parseAttributeValue: true, ignoreAttributes: false })
145 expect(xmlEnclosure.attr('type').value()).to.be.equal('application/x-bittorrent') 145
146 expect(xmlEnclosure.attr('length').value()).to.be.equal('218910') 146 const enclosure = xmlDoc.rss.channel.item[0].enclosure
147 expect(xmlEnclosure.attr('url').value()).to.contain('720.torrent') 147 expect(enclosure).to.exist
148 expect(enclosure['@_type']).to.equal('application/x-bittorrent')
149 expect(enclosure['@_length']).to.equal(218910)
150 expect(enclosure['@_url']).to.contain('720.torrent')
148 } 151 }
149 }) 152 })
150 153
diff --git a/server/tests/fixtures/banner-resized.jpg b/server/tests/fixtures/banner-resized.jpg
new file mode 100644
index 000000000..13ea422cb
--- /dev/null
+++ b/server/tests/fixtures/banner-resized.jpg
Binary files differ
diff --git a/server/tests/fixtures/banner.jpg b/server/tests/fixtures/banner.jpg
new file mode 100644
index 000000000..e5f284f59
--- /dev/null
+++ b/server/tests/fixtures/banner.jpg
Binary files differ
diff --git a/server/tests/fixtures/peertube-plugin-test-four/main.js b/server/tests/fixtures/peertube-plugin-test-four/main.js
index 8df456c8a..ea0599997 100644
--- a/server/tests/fixtures/peertube-plugin-test-four/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-four/main.js
@@ -69,7 +69,20 @@ async function register ({
69 res.sendStatus(500) 69 res.sendStatus(500)
70 } 70 }
71 }) 71 })
72
73 router.get('/server-config', async (req, res) => {
74 const serverConfig = await peertubeHelpers.config.getServerConfig()
75
76 return res.json({ serverConfig })
77 })
78
79 router.get('/static-route', async (req, res) => {
80 const staticRoute = await peertubeHelpers.plugin.getBaseStaticRoute()
81
82 return res.json({ staticRoute })
83 })
72 } 84 }
85
73} 86}
74 87
75async function unregister () { 88async function unregister () {
diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
index 5990ce1ce..59b136947 100644
--- a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
@@ -1,30 +1,88 @@
1async function register ({ transcodingManager }) { 1async function register ({ transcodingManager }) {
2 2
3 // Output options
3 { 4 {
4 const builder = () => { 5 {
5 return { 6 const builder = () => {
6 outputOptions: [ 7 return {
7 '-r 10' 8 outputOptions: [
8 ] 9 '-r 10'
10 ]
11 }
9 } 12 }
13
14 transcodingManager.addVODProfile('libx264', 'low-vod', builder)
10 } 15 }
11 16
12 transcodingManager.addVODProfile('libx264', 'low-vod', builder) 17 {
18 const builder = (options) => {
19 return {
20 outputOptions: [
21 '-r:' + options.streamNum + ' 5'
22 ]
23 }
24 }
25
26 transcodingManager.addLiveProfile('libx264', 'low-live', builder)
27 }
13 } 28 }
14 29
30 // Input options
15 { 31 {
16 const builder = (options) => { 32 {
17 return { 33 const builder = () => {
18 outputOptions: [ 34 return {
19 '-r:' + options.streamNum + ' 5' 35 inputOptions: [
20 ] 36 '-r 5'
37 ]
38 }
21 } 39 }
40
41 transcodingManager.addVODProfile('libx264', 'input-options-vod', builder)
22 } 42 }
23 43
24 transcodingManager.addLiveProfile('libx264', 'low-live', builder) 44 {
45 const builder = () => {
46 return {
47 inputOptions: [
48 '-r 5'
49 ]
50 }
51 }
52
53 transcodingManager.addLiveProfile('libx264', 'input-options-live', builder)
54 }
55 }
56
57 // Scale filters
58 {
59 {
60 const builder = () => {
61 return {
62 scaleFilter: {
63 name: 'Glomgold'
64 }
65 }
66 }
67
68 transcodingManager.addVODProfile('libx264', 'bad-scale-vod', builder)
69 }
70
71 {
72 const builder = () => {
73 return {
74 scaleFilter: {
75 name: 'Flintheart'
76 }
77 }
78 }
79
80 transcodingManager.addLiveProfile('libx264', 'bad-scale-live', builder)
81 }
25 } 82 }
26} 83}
27 84
85
28async function unregister () { 86async function unregister () {
29 return 87 return
30} 88}
diff --git a/server/tests/fixtures/peertube-plugin-test-unloading/lib.js b/server/tests/fixtures/peertube-plugin-test-unloading/lib.js
new file mode 100644
index 000000000..f57e7cb01
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-unloading/lib.js
@@ -0,0 +1,2 @@
1const d = new Date()
2exports.value = d.getTime()
diff --git a/server/tests/fixtures/peertube-plugin-test-unloading/main.js b/server/tests/fixtures/peertube-plugin-test-unloading/main.js
new file mode 100644
index 000000000..5c8457cef
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-unloading/main.js
@@ -0,0 +1,14 @@
1const lib = require('./lib')
2
3async function register ({ getRouter }) {
4 const router = getRouter()
5 router.get('/get', (req, res) => res.json({ message: lib.value }))
6}
7
8async function unregister () {
9}
10
11module.exports = {
12 register,
13 unregister
14}
diff --git a/server/tests/fixtures/peertube-plugin-test-unloading/package.json b/server/tests/fixtures/peertube-plugin-test-unloading/package.json
new file mode 100644
index 000000000..7076d4b6f
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-unloading/package.json
@@ -0,0 +1,20 @@
1{
2 "name": "peertube-plugin-test-unloading",
3 "version": "0.0.1",
4 "description": "Plugin test (modules unloading)",
5 "engine": {
6 "peertube": ">=1.3.0"
7 },
8 "keywords": [
9 "peertube",
10 "plugin"
11 ],
12 "homepage": "https://github.com/Chocobozzz/PeerTube",
13 "author": "Chocobozzz",
14 "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
15 "library": "./main.js",
16 "staticDirs": {},
17 "css": [],
18 "clientScripts": [],
19 "translations": {}
20}
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index 305d92002..ee0bc39f3 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -184,6 +184,76 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
184 return result 184 return result
185 } 185 }
186 }) 186 })
187
188 registerHook({
189 target: 'filter:api.download.torrent.allowed.result',
190 handler: (result, params) => {
191 if (params && params.downloadName.includes('bad torrent')) {
192 return { allowed: false, errorMessage: 'Liu Bei' }
193 }
194
195 return result
196 }
197 })
198
199 registerHook({
200 target: 'filter:api.download.video.allowed.result',
201 handler: (result, params) => {
202 if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) {
203 return { allowed: false, errorMessage: 'Cao Cao' }
204 }
205
206 if (params && params.streamingPlaylist && params.video.name.includes('bad playlist file')) {
207 return { allowed: false, errorMessage: 'Sun Jian' }
208 }
209
210 return result
211 }
212 })
213
214 registerHook({
215 target: 'filter:html.embed.video.allowed.result',
216 handler: (result, params) => {
217 return {
218 allowed: false,
219 html: 'Lu Bu'
220 }
221 }
222 })
223
224 registerHook({
225 target: 'filter:html.embed.video-playlist.allowed.result',
226 handler: (result, params) => {
227 return {
228 allowed: false,
229 html: 'Diao Chan'
230 }
231 }
232 })
233
234 {
235 const searchHooks = [
236 'filter:api.search.videos.local.list.params',
237 'filter:api.search.videos.local.list.result',
238 'filter:api.search.videos.index.list.params',
239 'filter:api.search.videos.index.list.result',
240 'filter:api.search.video-channels.local.list.params',
241 'filter:api.search.video-channels.local.list.result',
242 'filter:api.search.video-channels.index.list.params',
243 'filter:api.search.video-channels.index.list.result',
244 ]
245
246 for (const h of searchHooks) {
247 registerHook({
248 target: h,
249 handler: (obj) => {
250 peertubeHelpers.logger.debug('Run hook %s.', h)
251
252 return obj
253 }
254 })
255 }
256 }
187} 257}
188 258
189async function unregister () { 259async function unregister () {
diff --git a/server/tests/fixtures/thumbnail-playlist.jpg b/server/tests/fixtures/thumbnail-playlist.jpg
index 19db4f18c..62cd77435 100644
--- a/server/tests/fixtures/thumbnail-playlist.jpg
+++ b/server/tests/fixtures/thumbnail-playlist.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_import_thumbnail.jpg b/server/tests/fixtures/video_import_thumbnail.jpg
index fcc50b75f..9ee1bc382 100644
--- a/server/tests/fixtures/video_import_thumbnail.jpg
+++ b/server/tests/fixtures/video_import_thumbnail.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_short.mp4.jpg b/server/tests/fixtures/video_short.mp4.jpg
index 48790ffec..62cd77435 100644
--- a/server/tests/fixtures/video_short.mp4.jpg
+++ b/server/tests/fixtures/video_short.mp4.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_short.ogv.jpg b/server/tests/fixtures/video_short.ogv.jpg
index c4c1d00e5..62cd77435 100644
--- a/server/tests/fixtures/video_short.ogv.jpg
+++ b/server/tests/fixtures/video_short.ogv.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_short.webm.jpg b/server/tests/fixtures/video_short.webm.jpg
index 7f8047516..62cd77435 100644
--- a/server/tests/fixtures/video_short.webm.jpg
+++ b/server/tests/fixtures/video_short.webm.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_short1.webm.jpg b/server/tests/fixtures/video_short1.webm.jpg
index 582eb9ea3..615cb2a5d 100644
--- a/server/tests/fixtures/video_short1.webm.jpg
+++ b/server/tests/fixtures/video_short1.webm.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_short2.webm.jpg b/server/tests/fixtures/video_short2.webm.jpg
index b331aba3b..aa3126381 100644
--- a/server/tests/fixtures/video_short2.webm.jpg
+++ b/server/tests/fixtures/video_short2.webm.jpg
Binary files differ
diff --git a/server/tests/fixtures/video_short3.webm.jpg b/server/tests/fixtures/video_short3.webm.jpg
index ec8652167..62cd77435 100644
--- a/server/tests/fixtures/video_short3.webm.jpg
+++ b/server/tests/fixtures/video_short3.webm.jpg
Binary files differ
diff --git a/server/tests/helpers/request.ts b/server/tests/helpers/request.ts
index f8b2d599b..5e77f129e 100644
--- a/server/tests/helpers/request.ts
+++ b/server/tests/helpers/request.ts
@@ -1,11 +1,11 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
5import { get4KFileUrl, root, wait } from '../../../shared/extra-utils'
6import { join } from 'path'
7import { pathExists, remove } from 'fs-extra'
8import { expect } from 'chai' 4import { expect } from 'chai'
5import { pathExists, remove } from 'fs-extra'
6import { join } from 'path'
7import { get4KFileUrl, root, wait } from '../../../shared/extra-utils'
8import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
9 9
10describe('Request helpers', function () { 10describe('Request helpers', function () {
11 const destPath1 = join(root(), 'test-output-1.txt') 11 const destPath1 = join(root(), 'test-output-1.txt')
@@ -13,7 +13,7 @@ describe('Request helpers', function () {
13 13
14 it('Should throw an error when the bytes limit is exceeded for request', async function () { 14 it('Should throw an error when the bytes limit is exceeded for request', async function () {
15 try { 15 try {
16 await doRequest({ uri: get4KFileUrl() }, 3) 16 await doRequest(get4KFileUrl(), { bodyKBLimit: 3 })
17 } catch { 17 } catch {
18 return 18 return
19 } 19 }
@@ -23,7 +23,7 @@ describe('Request helpers', function () {
23 23
24 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () { 24 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () {
25 try { 25 try {
26 await doRequestAndSaveToFile({ uri: get4KFileUrl() }, destPath1, 3) 26 await doRequestAndSaveToFile(get4KFileUrl(), destPath1, { bodyKBLimit: 3 })
27 } catch { 27 } catch {
28 28
29 await wait(500) 29 await wait(500)
@@ -35,8 +35,8 @@ describe('Request helpers', function () {
35 }) 35 })
36 36
37 it('Should succeed if the file is below the limit', async function () { 37 it('Should succeed if the file is below the limit', async function () {
38 await doRequest({ uri: get4KFileUrl() }, 5) 38 await doRequest(get4KFileUrl(), { bodyKBLimit: 5 })
39 await doRequestAndSaveToFile({ uri: get4KFileUrl() }, destPath2, 5) 39 await doRequestAndSaveToFile(get4KFileUrl(), destPath2, { bodyKBLimit: 5 })
40 40
41 expect(await pathExists(destPath2)).to.be.true 41 expect(await pathExists(destPath2)).to.be.true
42 }) 42 })
diff --git a/server/tests/plugins/external-auth.ts b/server/tests/plugins/external-auth.ts
index a1b5e8f5d..5addb45c7 100644
--- a/server/tests/plugins/external-auth.ts
+++ b/server/tests/plugins/external-auth.ts
@@ -137,7 +137,7 @@ describe('Test external auth plugins', function () {
137 137
138 await loginUsingExternalToken(server, 'cyan', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 138 await loginUsingExternalToken(server, 'cyan', externalAuthToken, HttpStatusCode.BAD_REQUEST_400)
139 139
140 await waitUntilLog(server, 'expired external auth token') 140 await waitUntilLog(server, 'expired external auth token', 2)
141 }) 141 })
142 142
143 it('Should auto login Cyan, create the user and use the token', async function () { 143 it('Should auto login Cyan, create the user and use the token', async function () {
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index d88170201..ac958c5f5 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -2,11 +2,15 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels'
5import { ServerConfig } from '@shared/models' 6import { ServerConfig } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6import { 8import {
7 addVideoCommentReply, 9 addVideoCommentReply,
8 addVideoCommentThread, 10 addVideoCommentThread,
11 advancedVideosSearch,
9 createLive, 12 createLive,
13 createVideoPlaylist,
10 doubleFollow, 14 doubleFollow,
11 getAccountVideos, 15 getAccountVideos,
12 getConfig, 16 getConfig,
@@ -15,24 +19,33 @@ import {
15 getVideo, 19 getVideo,
16 getVideoChannelVideos, 20 getVideoChannelVideos,
17 getVideoCommentThreads, 21 getVideoCommentThreads,
22 getVideoPlaylist,
18 getVideosList, 23 getVideosList,
19 getVideosListPagination, 24 getVideosListPagination,
20 getVideoThreadComments, 25 getVideoThreadComments,
21 getVideoWithToken, 26 getVideoWithToken,
22 installPlugin, 27 installPlugin,
28 makeRawRequest,
23 registerUser, 29 registerUser,
24 setAccessTokensToServers, 30 setAccessTokensToServers,
25 setDefaultVideoChannel, 31 setDefaultVideoChannel,
26 updateCustomSubConfig, 32 updateCustomSubConfig,
27 updateVideo, 33 updateVideo,
28 uploadVideo, 34 uploadVideo,
35 uploadVideoAndGetId,
29 waitJobs 36 waitJobs
30} from '../../../shared/extra-utils' 37} from '../../../shared/extra-utils'
31import { cleanupTests, flushAndRunMultipleServers, ServerInfo } from '../../../shared/extra-utils/server/servers' 38import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
32import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' 39import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports'
33import { VideoDetails, VideoImport, VideoImportState, VideoPrivacy } from '../../../shared/models/videos' 40import {
41 VideoDetails,
42 VideoImport,
43 VideoImportState,
44 VideoPlaylist,
45 VideoPlaylistPrivacy,
46 VideoPrivacy
47} from '../../../shared/models/videos'
34import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' 48import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
35import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
36 49
37const expect = chai.expect 50const expect = chai.expect
38 51
@@ -355,6 +368,165 @@ describe('Test plugin filter hooks', function () {
355 }) 368 })
356 }) 369 })
357 370
371 describe('Download hooks', function () {
372 const downloadVideos: VideoDetails[] = []
373
374 before(async function () {
375 this.timeout(60000)
376
377 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
378 transcoding: {
379 webtorrent: {
380 enabled: true
381 },
382 hls: {
383 enabled: true
384 }
385 }
386 })
387
388 const uuids: string[] = []
389
390 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) {
391 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid
392 uuids.push(uuid)
393 }
394
395 await waitJobs(servers)
396
397 for (const uuid of uuids) {
398 const res = await getVideo(servers[0].url, uuid)
399 downloadVideos.push(res.body)
400 }
401 })
402
403 it('Should run filter:api.download.torrent.allowed.result', async function () {
404 const res = await makeRawRequest(downloadVideos[0].files[0].torrentDownloadUrl, 403)
405 expect(res.body.error).to.equal('Liu Bei')
406
407 await makeRawRequest(downloadVideos[1].files[0].torrentDownloadUrl, 200)
408 await makeRawRequest(downloadVideos[2].files[0].torrentDownloadUrl, 200)
409 })
410
411 it('Should run filter:api.download.video.allowed.result', async function () {
412 {
413 const res = await makeRawRequest(downloadVideos[1].files[0].fileDownloadUrl, 403)
414 expect(res.body.error).to.equal('Cao Cao')
415
416 await makeRawRequest(downloadVideos[0].files[0].fileDownloadUrl, 200)
417 await makeRawRequest(downloadVideos[2].files[0].fileDownloadUrl, 200)
418 }
419
420 {
421 const res = await makeRawRequest(downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl, 403)
422 expect(res.body.error).to.equal('Sun Jian')
423
424 await makeRawRequest(downloadVideos[2].files[0].fileDownloadUrl, 200)
425
426 await makeRawRequest(downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl, 200)
427 await makeRawRequest(downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl, 200)
428 }
429 })
430 })
431
432 describe('Embed filters', function () {
433 const embedVideos: VideoDetails[] = []
434 const embedPlaylists: VideoPlaylist[] = []
435
436 before(async function () {
437 this.timeout(60000)
438
439 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
440 transcoding: {
441 enabled: false
442 }
443 })
444
445 for (const name of [ 'bad embed', 'good embed' ]) {
446 {
447 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid
448 const res = await getVideo(servers[0].url, uuid)
449 embedVideos.push(res.body)
450 }
451
452 {
453 const playlistAttrs = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC }
454 const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
455
456 const resPlaylist = await getVideoPlaylist(servers[0].url, res.body.videoPlaylist.id)
457 embedPlaylists.push(resPlaylist.body)
458 }
459 }
460 })
461
462 it('Should run filter:html.embed.video.allowed.result', async function () {
463 const res = await makeRawRequest(servers[0].url + embedVideos[0].embedPath, 200)
464 expect(res.text).to.equal('Lu Bu')
465 })
466
467 it('Should run filter:html.embed.video-playlist.allowed.result', async function () {
468 const res = await makeRawRequest(servers[0].url + embedPlaylists[0].embedPath, 200)
469 expect(res.text).to.equal('Diao Chan')
470 })
471 })
472
473 describe('Search filters', function () {
474
475 before(async function () {
476 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
477 search: {
478 searchIndex: {
479 enabled: true,
480 isDefaultSearch: false,
481 disableLocalSearch: false
482 }
483 }
484 })
485 })
486
487 it('Should run filter:api.search.videos.local.list.{params,result}', async function () {
488 await advancedVideosSearch(servers[0].url, {
489 search: 'Sun Quan'
490 })
491
492 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1)
493 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1)
494 })
495
496 it('Should run filter:api.search.videos.index.list.{params,result}', async function () {
497 await advancedVideosSearch(servers[0].url, {
498 search: 'Sun Quan',
499 searchTarget: 'search-index'
500 })
501
502 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1)
503 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1)
504 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.params', 1)
505 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.result', 1)
506 })
507
508 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () {
509 await advancedVideoChannelSearch(servers[0].url, {
510 search: 'Sun Ce'
511 })
512
513 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1)
514 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1)
515 })
516
517 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () {
518 await advancedVideoChannelSearch(servers[0].url, {
519 search: 'Sun Ce',
520 searchTarget: 'search-index'
521 })
522
523 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1)
524 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1)
525 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.params', 1)
526 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.result', 1)
527 })
528 })
529
358 after(async function () { 530 after(async function () {
359 await cleanupTests(servers) 531 await cleanupTests(servers)
360 }) 532 })
diff --git a/server/tests/plugins/index.ts b/server/tests/plugins/index.ts
index fd7116efd..4534120fd 100644
--- a/server/tests/plugins/index.ts
+++ b/server/tests/plugins/index.ts
@@ -7,5 +7,6 @@ import './plugin-helpers'
7import './plugin-router' 7import './plugin-router'
8import './plugin-storage' 8import './plugin-storage'
9import './plugin-transcoding' 9import './plugin-transcoding'
10import './plugin-unloading'
10import './translations' 11import './translations'
11import './video-constants' 12import './video-constants'
diff --git a/server/tests/plugins/plugin-helpers.ts b/server/tests/plugins/plugin-helpers.ts
index a585e3020..325d20e84 100644
--- a/server/tests/plugins/plugin-helpers.ts
+++ b/server/tests/plugins/plugin-helpers.ts
@@ -12,7 +12,8 @@ import {
12 uploadVideoAndGetId, 12 uploadVideoAndGetId,
13 viewVideo, 13 viewVideo,
14 getVideosList, 14 getVideosList,
15 waitJobs 15 waitJobs,
16 makeGetRequest
16} from '../../../shared/extra-utils' 17} from '../../../shared/extra-utils'
17import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' 18import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
18import { expect } from 'chai' 19import { expect } from 'chai'
@@ -68,6 +69,17 @@ describe('Test plugin helpers', function () {
68 it('Should have the correct webserver url', async function () { 69 it('Should have the correct webserver url', async function () {
69 await waitUntilLog(servers[0], `server url is http://localhost:${servers[0].port}`) 70 await waitUntilLog(servers[0], `server url is http://localhost:${servers[0].port}`)
70 }) 71 })
72
73 it('Should have the correct config', async function () {
74 const res = await makeGetRequest({
75 url: servers[0].url,
76 path: '/plugins/test-four/router/server-config',
77 statusCodeExpected: HttpStatusCode.OK_200
78 })
79
80 expect(res.body.serverConfig).to.exist
81 expect(res.body.serverConfig.instance.name).to.equal('PeerTube')
82 })
71 }) 83 })
72 84
73 describe('Server', function () { 85 describe('Server', function () {
@@ -77,6 +89,19 @@ describe('Test plugin helpers', function () {
77 }) 89 })
78 }) 90 })
79 91
92 describe('Plugin', function () {
93
94 it('Should get the base static route', async function () {
95 const res = await makeGetRequest({
96 url: servers[0].url,
97 path: '/plugins/test-four/router/static-route',
98 statusCodeExpected: HttpStatusCode.OK_200
99 })
100
101 expect(res.body.staticRoute).to.equal('/plugins/test-four/0.0.1/static/')
102 })
103 })
104
80 describe('Moderation', function () { 105 describe('Moderation', function () {
81 let videoUUIDServer1: string 106 let videoUUIDServer1: string
82 107
diff --git a/server/tests/plugins/plugin-transcoding.ts b/server/tests/plugins/plugin-transcoding.ts
index ecea21e69..c834b6985 100644
--- a/server/tests/plugins/plugin-transcoding.ts
+++ b/server/tests/plugins/plugin-transcoding.ts
@@ -15,6 +15,7 @@ import {
15 sendRTMPStreamInVideo, 15 sendRTMPStreamInVideo,
16 setAccessTokensToServers, 16 setAccessTokensToServers,
17 setDefaultVideoChannel, 17 setDefaultVideoChannel,
18 testFfmpegStreamError,
18 uninstallPlugin, 19 uninstallPlugin,
19 updateCustomSubConfig, 20 updateCustomSubConfig,
20 uploadVideoAndGetId, 21 uploadVideoAndGetId,
@@ -119,8 +120,8 @@ describe('Test transcoding plugins', function () {
119 const res = await getConfig(server.url) 120 const res = await getConfig(server.url)
120 const config = res.body as ServerConfig 121 const config = res.body as ServerConfig
121 122
122 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod' ]) 123 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ])
123 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live' ]) 124 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live', 'input-options-live', 'bad-scale-live' ])
124 }) 125 })
125 126
126 it('Should not use the plugin profile if not chosen by the admin', async function () { 127 it('Should not use the plugin profile if not chosen by the admin', async function () {
@@ -143,6 +144,33 @@ describe('Test transcoding plugins', function () {
143 await checkVideoFPS(videoUUID, 'below', 12) 144 await checkVideoFPS(videoUUID, 'below', 12)
144 }) 145 })
145 146
147 it('Should apply input options in vod profile', async function () {
148 this.timeout(120000)
149
150 await updateConf(server, 'input-options-vod', 'default')
151
152 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid
153 await waitJobs([ server ])
154
155 await checkVideoFPS(videoUUID, 'below', 6)
156 })
157
158 it('Should apply the scale filter in vod profile', async function () {
159 this.timeout(120000)
160
161 await updateConf(server, 'bad-scale-vod', 'default')
162
163 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid
164 await waitJobs([ server ])
165
166 // Transcoding failed
167 const res = await getVideo(server.url, videoUUID)
168 const video: VideoDetails = res.body
169
170 expect(video.files).to.have.lengthOf(1)
171 expect(video.streamingPlaylists).to.have.lengthOf(0)
172 })
173
146 it('Should not use the plugin profile if not chosen by the admin', async function () { 174 it('Should not use the plugin profile if not chosen by the admin', async function () {
147 this.timeout(120000) 175 this.timeout(120000)
148 176
@@ -169,6 +197,31 @@ describe('Test transcoding plugins', function () {
169 await checkLiveFPS(liveVideoId, 'below', 12) 197 await checkLiveFPS(liveVideoId, 'below', 12)
170 }) 198 })
171 199
200 it('Should apply the input options on live profile', async function () {
201 this.timeout(120000)
202
203 await updateConf(server, 'low-vod', 'input-options-live')
204
205 const liveVideoId = await createLiveWrapper(server)
206
207 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm')
208 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
209 await waitJobs([ server ])
210
211 await checkLiveFPS(liveVideoId, 'below', 6)
212 })
213
214 it('Should apply the scale filter name on live profile', async function () {
215 this.timeout(120000)
216
217 await updateConf(server, 'low-vod', 'bad-scale-live')
218
219 const liveVideoId = await createLiveWrapper(server)
220
221 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm')
222 await testFfmpegStreamError(command, true)
223 })
224
172 it('Should default to the default profile if the specified profile does not exist', async function () { 225 it('Should default to the default profile if the specified profile does not exist', async function () {
173 this.timeout(120000) 226 this.timeout(120000)
174 227
diff --git a/server/tests/plugins/plugin-unloading.ts b/server/tests/plugins/plugin-unloading.ts
new file mode 100644
index 000000000..74ca82e2f
--- /dev/null
+++ b/server/tests/plugins/plugin-unloading.ts
@@ -0,0 +1,89 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import {
5 cleanupTests,
6 flushAndRunServer,
7 getPluginTestPath,
8 makeGetRequest,
9 installPlugin,
10 uninstallPlugin,
11 ServerInfo,
12 setAccessTokensToServers
13} from '../../../shared/extra-utils'
14import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
15import { expect } from 'chai'
16
17describe('Test plugins module unloading', function () {
18 let server: ServerInfo = null
19 const requestPath = '/plugins/test-unloading/router/get'
20 let value: string = null
21
22 before(async function () {
23 this.timeout(30000)
24
25 server = await flushAndRunServer(1)
26 await setAccessTokensToServers([ server ])
27
28 await installPlugin({
29 url: server.url,
30 accessToken: server.accessToken,
31 path: getPluginTestPath('-unloading')
32 })
33 })
34
35 it('Should return a numeric value', async function () {
36 const res = await makeGetRequest({
37 url: server.url,
38 path: requestPath,
39 statusCodeExpected: HttpStatusCode.OK_200
40 })
41
42 expect(res.body.message).to.match(/^\d+$/)
43 value = res.body.message
44 })
45
46 it('Should return the same value the second time', async function () {
47 const res = await makeGetRequest({
48 url: server.url,
49 path: requestPath,
50 statusCodeExpected: HttpStatusCode.OK_200
51 })
52
53 expect(res.body.message).to.be.equal(value)
54 })
55
56 it('Should uninstall the plugin and free the route', async function () {
57 await uninstallPlugin({
58 url: server.url,
59 accessToken: server.accessToken,
60 npmName: 'peertube-plugin-test-unloading'
61 })
62
63 await makeGetRequest({
64 url: server.url,
65 path: requestPath,
66 statusCodeExpected: HttpStatusCode.NOT_FOUND_404
67 })
68 })
69
70 it('Should return a different numeric value', async function () {
71 await installPlugin({
72 url: server.url,
73 accessToken: server.accessToken,
74 path: getPluginTestPath('-unloading')
75 })
76 const res = await makeGetRequest({
77 url: server.url,
78 path: requestPath,
79 statusCodeExpected: HttpStatusCode.OK_200
80 })
81
82 expect(res.body.message).to.match(/^\d+$/)
83 expect(res.body.message).to.be.not.equal(value)
84 })
85
86 after(async function () {
87 await cleanupTests([ server ])
88 })
89})