]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/object-storage/videos.ts
Increase test timeout
[github/Chocobozzz/PeerTube.git] / server / tests / api / object-storage / videos.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import 'mocha'
4 import * as chai from 'chai'
5 import { merge } from 'lodash'
6 import {
7 areObjectStorageTestsDisabled,
8 checkTmpIsEmpty,
9 cleanupTests,
10 createMultipleServers,
11 createSingleServer,
12 doubleFollow,
13 expectLogDoesNotContain,
14 expectStartWith,
15 killallServers,
16 makeRawRequest,
17 MockObjectStorage,
18 ObjectStorageCommand,
19 PeerTubeServer,
20 setAccessTokensToServers,
21 waitJobs,
22 webtorrentAdd
23 } from '@shared/extra-utils'
24 import { HttpStatusCode, VideoDetails } from '@shared/models'
25
26 const expect = chai.expect
27
28 async function checkFiles (options: {
29 video: VideoDetails
30
31 baseMockUrl?: string
32
33 playlistBucket: string
34 playlistPrefix?: string
35
36 webtorrentBucket: string
37 webtorrentPrefix?: string
38 }) {
39 const {
40 video,
41 playlistBucket,
42 webtorrentBucket,
43 baseMockUrl,
44 playlistPrefix,
45 webtorrentPrefix
46 } = options
47
48 let allFiles = video.files
49
50 for (const file of video.files) {
51 const baseUrl = baseMockUrl
52 ? `${baseMockUrl}/${webtorrentBucket}/`
53 : `http://${webtorrentBucket}.${ObjectStorageCommand.getEndpointHost()}/`
54
55 const prefix = webtorrentPrefix || ''
56 const start = baseUrl + prefix
57
58 expectStartWith(file.fileUrl, start)
59
60 const res = await makeRawRequest(file.fileDownloadUrl, HttpStatusCode.FOUND_302)
61 const location = res.headers['location']
62 expectStartWith(location, start)
63
64 await makeRawRequest(location, HttpStatusCode.OK_200)
65 }
66
67 const hls = video.streamingPlaylists[0]
68
69 if (hls) {
70 allFiles = allFiles.concat(hls.files)
71
72 const baseUrl = baseMockUrl
73 ? `${baseMockUrl}/${playlistBucket}/`
74 : `http://${playlistBucket}.${ObjectStorageCommand.getEndpointHost()}/`
75
76 const prefix = playlistPrefix || ''
77 const start = baseUrl + prefix
78
79 expectStartWith(hls.playlistUrl, start)
80 expectStartWith(hls.segmentsSha256Url, start)
81
82 await makeRawRequest(hls.playlistUrl, HttpStatusCode.OK_200)
83
84 const resSha = await makeRawRequest(hls.segmentsSha256Url, HttpStatusCode.OK_200)
85 expect(JSON.stringify(resSha.body)).to.not.throw
86
87 for (const file of hls.files) {
88 expectStartWith(file.fileUrl, start)
89
90 const res = await makeRawRequest(file.fileDownloadUrl, HttpStatusCode.FOUND_302)
91 const location = res.headers['location']
92 expectStartWith(location, start)
93
94 await makeRawRequest(location, HttpStatusCode.OK_200)
95 }
96 }
97
98 for (const file of allFiles) {
99 const torrent = await webtorrentAdd(file.magnetUri, true)
100
101 expect(torrent.files).to.be.an('array')
102 expect(torrent.files.length).to.equal(1)
103 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
104
105 const res = await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
106 expect(res.body).to.have.length.above(100)
107 }
108
109 return allFiles.map(f => f.fileUrl)
110 }
111
112 function runTestSuite (options: {
113 playlistBucket: string
114 playlistPrefix?: string
115
116 webtorrentBucket: string
117 webtorrentPrefix?: string
118
119 useMockBaseUrl?: boolean
120
121 maxUploadPart?: string
122 }) {
123 const mockObjectStorage = new MockObjectStorage()
124 let baseMockUrl: string
125
126 let servers: PeerTubeServer[]
127
128 let keptUrls: string[] = []
129
130 const uuidsToDelete: string[] = []
131 let deletedUrls: string[] = []
132
133 before(async function () {
134 this.timeout(120000)
135
136 const port = await mockObjectStorage.initialize()
137 baseMockUrl = options.useMockBaseUrl ? `http://localhost:${port}` : undefined
138
139 await ObjectStorageCommand.createBucket(options.playlistBucket)
140 await ObjectStorageCommand.createBucket(options.webtorrentBucket)
141
142 const config = {
143 object_storage: {
144 enabled: true,
145 endpoint: 'http://' + ObjectStorageCommand.getEndpointHost(),
146 region: ObjectStorageCommand.getRegion(),
147
148 credentials: ObjectStorageCommand.getCredentialsConfig(),
149
150 max_upload_part: options.maxUploadPart || '2MB',
151
152 streaming_playlists: {
153 bucket_name: options.playlistBucket,
154 prefix: options.playlistPrefix,
155 base_url: baseMockUrl
156 ? `${baseMockUrl}/${options.playlistBucket}`
157 : undefined
158 },
159
160 videos: {
161 bucket_name: options.webtorrentBucket,
162 prefix: options.webtorrentPrefix,
163 base_url: baseMockUrl
164 ? `${baseMockUrl}/${options.webtorrentBucket}`
165 : undefined
166 }
167 }
168 }
169
170 servers = await createMultipleServers(2, config)
171
172 await setAccessTokensToServers(servers)
173 await doubleFollow(servers[0], servers[1])
174
175 for (const server of servers) {
176 const { uuid } = await server.videos.quickUpload({ name: 'video to keep' })
177 await waitJobs(servers)
178
179 const files = await server.videos.listFiles({ id: uuid })
180 keptUrls = keptUrls.concat(files.map(f => f.fileUrl))
181 }
182 })
183
184 it('Should upload a video and move it to the object storage without transcoding', async function () {
185 this.timeout(20000)
186
187 const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1' })
188 uuidsToDelete.push(uuid)
189
190 await waitJobs(servers)
191
192 for (const server of servers) {
193 const video = await server.videos.get({ id: uuid })
194 const files = await checkFiles({ ...options, video, baseMockUrl })
195
196 deletedUrls = deletedUrls.concat(files)
197 }
198 })
199
200 it('Should upload a video and move it to the object storage with transcoding', async function () {
201 this.timeout(120000)
202
203 const { uuid } = await servers[1].videos.quickUpload({ name: 'video 2' })
204 uuidsToDelete.push(uuid)
205
206 await waitJobs(servers)
207
208 for (const server of servers) {
209 const video = await server.videos.get({ id: uuid })
210 const files = await checkFiles({ ...options, video, baseMockUrl })
211
212 deletedUrls = deletedUrls.concat(files)
213 }
214 })
215
216 it('Should fetch correctly all the files', async function () {
217 for (const url of deletedUrls.concat(keptUrls)) {
218 await makeRawRequest(url, HttpStatusCode.OK_200)
219 }
220 })
221
222 it('Should correctly delete the files', async function () {
223 await servers[0].videos.remove({ id: uuidsToDelete[0] })
224 await servers[1].videos.remove({ id: uuidsToDelete[1] })
225
226 await waitJobs(servers)
227
228 for (const url of deletedUrls) {
229 await makeRawRequest(url, HttpStatusCode.NOT_FOUND_404)
230 }
231 })
232
233 it('Should have kept other files', async function () {
234 for (const url of keptUrls) {
235 await makeRawRequest(url, HttpStatusCode.OK_200)
236 }
237 })
238
239 it('Should have an empty tmp directory', async function () {
240 for (const server of servers) {
241 await checkTmpIsEmpty(server)
242 }
243 })
244
245 it('Should not have downloaded files from object storage', async function () {
246 for (const server of servers) {
247 await expectLogDoesNotContain(server, 'from object storage')
248 }
249 })
250
251 after(async function () {
252 await mockObjectStorage.terminate()
253
254 await cleanupTests(servers)
255 })
256 }
257
258 describe('Object storage for videos', function () {
259 if (areObjectStorageTestsDisabled()) return
260
261 describe('Test config', function () {
262 let server: PeerTubeServer
263
264 const baseConfig = {
265 object_storage: {
266 enabled: true,
267 endpoint: 'http://' + ObjectStorageCommand.getEndpointHost(),
268 region: ObjectStorageCommand.getRegion(),
269
270 credentials: ObjectStorageCommand.getCredentialsConfig(),
271
272 streaming_playlists: {
273 bucket_name: ObjectStorageCommand.DEFAULT_PLAYLIST_BUCKET
274 },
275
276 videos: {
277 bucket_name: ObjectStorageCommand.DEFAULT_WEBTORRENT_BUCKET
278 }
279 }
280 }
281
282 const badCredentials = {
283 access_key_id: 'AKIAIOSFODNN7EXAMPLE',
284 secret_access_key: 'aJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
285 }
286
287 it('Should fail with same bucket names without prefix', function (done) {
288 const config = merge({}, baseConfig, {
289 object_storage: {
290 streaming_playlists: {
291 bucket_name: 'aaa'
292 },
293
294 videos: {
295 bucket_name: 'aaa'
296 }
297 }
298 })
299
300 createSingleServer(1, config)
301 .then(() => done(new Error('Did not throw')))
302 .catch(() => done())
303 })
304
305 it('Should fail with bad credentials', async function () {
306 this.timeout(60000)
307
308 await ObjectStorageCommand.prepareDefaultBuckets()
309
310 const config = merge({}, baseConfig, {
311 object_storage: {
312 credentials: badCredentials
313 }
314 })
315
316 server = await createSingleServer(1, config)
317 await setAccessTokensToServers([ server ])
318
319 const { uuid } = await server.videos.quickUpload({ name: 'video' })
320
321 await waitJobs([ server ], true)
322 const video = await server.videos.get({ id: uuid })
323
324 expectStartWith(video.files[0].fileUrl, server.url)
325
326 await killallServers([ server ])
327 })
328
329 it('Should succeed with credentials from env', async function () {
330 this.timeout(60000)
331
332 await ObjectStorageCommand.prepareDefaultBuckets()
333
334 const config = merge({}, baseConfig, {
335 object_storage: {
336 credentials: {
337 access_key_id: '',
338 secret_access_key: ''
339 }
340 }
341 })
342
343 const goodCredentials = ObjectStorageCommand.getCredentialsConfig()
344
345 server = await createSingleServer(1, config, {
346 env: {
347 AWS_ACCESS_KEY_ID: goodCredentials.access_key_id,
348 AWS_SECRET_ACCESS_KEY: goodCredentials.secret_access_key
349 }
350 })
351
352 await setAccessTokensToServers([ server ])
353
354 const { uuid } = await server.videos.quickUpload({ name: 'video' })
355
356 await waitJobs([ server ], true)
357 const video = await server.videos.get({ id: uuid })
358
359 expectStartWith(video.files[0].fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
360 })
361
362 after(async function () {
363 await killallServers([ server ])
364 })
365 })
366
367 describe('Test simple object storage', function () {
368 runTestSuite({
369 playlistBucket: 'streaming-playlists',
370 webtorrentBucket: 'videos'
371 })
372 })
373
374 describe('Test object storage with prefix', function () {
375 runTestSuite({
376 playlistBucket: 'mybucket',
377 webtorrentBucket: 'mybucket',
378
379 playlistPrefix: 'streaming-playlists_',
380 webtorrentPrefix: 'webtorrent_'
381 })
382 })
383
384 describe('Test object storage with prefix and base URL', function () {
385 runTestSuite({
386 playlistBucket: 'mybucket',
387 webtorrentBucket: 'mybucket',
388
389 playlistPrefix: 'streaming-playlists/',
390 webtorrentPrefix: 'webtorrent/',
391
392 useMockBaseUrl: true
393 })
394 })
395
396 describe('Test object storage with small upload part', function () {
397 runTestSuite({
398 playlistBucket: 'streaming-playlists',
399 webtorrentBucket: 'videos',
400
401 maxUploadPart: '5KB'
402 })
403 })
404 })