]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/tests/api/videos/resumable-upload.ts
Increase timeout
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / resumable-upload.ts
index b7756a4a89edb776c7333868b60ce3c9ef9031ab..1ba7cdbcc4bc775a0a5a1cb6a05b2dcd989d8d41 100644 (file)
@@ -4,16 +4,15 @@ import 'mocha'
 import * as chai from 'chai'
 import { pathExists, readdir, stat } from 'fs-extra'
 import { join } from 'path'
-import { HttpStatusCode } from '@shared/core-utils'
 import {
   buildAbsoluteFixturePath,
   cleanupTests,
-  flushAndRunServer,
-  ServerInfo,
+  createSingleServer,
+  PeerTubeServer,
   setAccessTokensToServers,
   setDefaultVideoChannel
 } from '@shared/extra-utils'
-import { VideoPrivacy } from '@shared/models'
+import { HttpStatusCode, VideoPrivacy } from '@shared/models'
 
 const expect = chai.expect
 
@@ -21,8 +20,10 @@ const expect = chai.expect
 
 describe('Test resumable upload', function () {
   const defaultFixture = 'video_short.mp4'
-  let server: ServerInfo
+  let server: PeerTubeServer
   let rootId: number
+  let userAccessToken: string
+  let userChannelId: number
 
   async function buildSize (fixture: string, size?: number) {
     if (size !== undefined) return size
@@ -31,24 +32,33 @@ describe('Test resumable upload', function () {
     return (await stat(baseFixture)).size
   }
 
-  async function prepareUpload (sizeArg?: number) {
-    const size = await buildSize(defaultFixture, sizeArg)
+  async function prepareUpload (options: {
+    channelId?: number
+    token?: string
+    size?: number
+    originalName?: string
+    lastModified?: number
+  } = {}) {
+    const { token, originalName, lastModified } = options
+
+    const size = await buildSize(defaultFixture, options.size)
 
     const attributes = {
       name: 'video',
-      channelId: server.videoChannel.id,
+      channelId: options.channelId ?? server.store.channel.id,
       privacy: VideoPrivacy.PUBLIC,
       fixture: defaultFixture
     }
 
     const mimetype = 'video/mp4'
 
-    const res = await server.videosCommand.prepareResumableUpload({ attributes, size, mimetype })
+    const res = await server.videos.prepareResumableUpload({ token, attributes, size, mimetype, originalName, lastModified })
 
     return res.header['location'].split('?')[1]
   }
 
   async function sendChunks (options: {
+    token?: string
     pathUploadId: string
     size?: number
     expectedStatus?: HttpStatusCode
@@ -56,12 +66,13 @@ describe('Test resumable upload', function () {
     contentRange?: string
     contentRangeBuilder?: (start: number, chunk: any) => string
   }) {
-    const { pathUploadId, expectedStatus, contentLength, contentRangeBuilder } = options
+    const { token, pathUploadId, expectedStatus, contentLength, contentRangeBuilder } = options
 
     const size = await buildSize(defaultFixture, options.size)
     const absoluteFilePath = buildAbsoluteFixturePath(defaultFixture)
 
-    return server.videosCommand.sendResumableChunks({
+    return server.videos.sendResumableChunks({
+      token,
       pathUploadId,
       videoFilePath: absoluteFilePath,
       size,
@@ -75,7 +86,7 @@ describe('Test resumable upload', function () {
     const uploadId = uploadIdArg.replace(/^upload_id=/, '')
 
     const subPath = join('tmp', 'resumable-uploads', uploadId)
-    const filePath = server.serversCommand.buildDirectory(subPath)
+    const filePath = server.servers.buildDirectory(subPath)
     const exists = await pathExists(filePath)
 
     if (expectedSize === null) {
@@ -90,7 +101,7 @@ describe('Test resumable upload', function () {
 
   async function countResumableUploads () {
     const subPath = join('tmp', 'resumable-uploads')
-    const filePath = server.serversCommand.buildDirectory(subPath)
+    const filePath = server.servers.buildDirectory(subPath)
 
     const files = await readdir(filePath)
     return files.length
@@ -99,14 +110,20 @@ describe('Test resumable upload', function () {
   before(async function () {
     this.timeout(30000)
 
-    server = await flushAndRunServer(1)
+    server = await createSingleServer(1)
     await setAccessTokensToServers([ server ])
     await setDefaultVideoChannel([ server ])
 
-    const body = await server.usersCommand.getMyInfo()
+    const body = await server.users.getMyInfo()
     rootId = body.id
 
-    await server.usersCommand.update({ userId: rootId, videoQuota: 10_000_000 })
+    {
+      userAccessToken = await server.users.generateUserAndToken('user1')
+      const { videoChannels } = await server.users.getMyInfo({ token: userAccessToken })
+      userChannelId = videoChannels[0].id
+    }
+
+    await server.users.update({ userId: rootId, videoQuota: 10_000_000 })
   })
 
   describe('Directory cleaning', function () {
@@ -114,6 +131,7 @@ describe('Test resumable upload', function () {
     it('Should correctly delete files after an upload', async function () {
       const uploadId = await prepareUpload()
       await sendChunks({ pathUploadId: uploadId })
+      await server.videos.endResumableUpload({ pathUploadId: uploadId })
 
       expect(await countResumableUploads()).to.equal(0)
     })
@@ -125,13 +143,13 @@ describe('Test resumable upload', function () {
     })
 
     it('Should not delete recent uploads', async function () {
-      await server.debugCommand.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
+      await server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
 
       expect(await countResumableUploads()).to.equal(2)
     })
 
     it('Should delete old uploads', async function () {
-      await server.debugCommand.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
+      await server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
 
       expect(await countResumableUploads()).to.equal(0)
     })
@@ -147,29 +165,99 @@ describe('Test resumable upload', function () {
     })
 
     it('Should not accept more chunks than expected', async function () {
-      const size = 100
-      const uploadId = await prepareUpload(size)
+      const uploadId = await prepareUpload({ size: 100 })
 
       await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409 })
       await checkFileSize(uploadId, 0)
     })
 
     it('Should not accept more chunks than expected with an invalid content length/content range', async function () {
-      const uploadId = await prepareUpload(1500)
-
-      await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentLength: 1000 })
-      await checkFileSize(uploadId, 0)
+      const uploadId = await prepareUpload({ size: 1500 })
+
+      // Content length check seems to have changed in v16
+      if (process.version.startsWith('v16')) {
+        await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409, contentLength: 1000 })
+        await checkFileSize(uploadId, 1000)
+      } else {
+        await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentLength: 1000 })
+        await checkFileSize(uploadId, 0)
+      }
     })
 
     it('Should not accept more chunks than expected with an invalid content length', async function () {
-      const uploadId = await prepareUpload(500)
+      const uploadId = await prepareUpload({ size: 500 })
 
       const size = 1000
 
-      const contentRangeBuilder = start => `bytes ${start}-${start + size - 1}/${size}`
-      await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentRangeBuilder, contentLength: size })
+      // Content length check seems to have changed in v16
+      const expectedStatus = process.version.startsWith('v16')
+        ? HttpStatusCode.CONFLICT_409
+        : HttpStatusCode.BAD_REQUEST_400
+
+      const contentRangeBuilder = (start: number) => `bytes ${start}-${start + size - 1}/${size}`
+      await sendChunks({ pathUploadId: uploadId, expectedStatus, contentRangeBuilder, contentLength: size })
       await checkFileSize(uploadId, 0)
     })
+
+    it('Should be able to accept 2 PUT requests', async function () {
+      const uploadId = await prepareUpload()
+
+      const result1 = await sendChunks({ pathUploadId: uploadId })
+      const result2 = await sendChunks({ pathUploadId: uploadId })
+
+      expect(result1.body.video.uuid).to.exist
+      expect(result1.body.video.uuid).to.equal(result2.body.video.uuid)
+
+      expect(result1.headers['x-resumable-upload-cached']).to.not.exist
+      expect(result2.headers['x-resumable-upload-cached']).to.equal('true')
+
+      await checkFileSize(uploadId, null)
+    })
+
+    it('Should not have the same upload id with 2 different users', async function () {
+      const originalName = 'toto.mp4'
+      const lastModified = new Date().getTime()
+
+      const uploadId1 = await prepareUpload({ originalName, lastModified, token: server.accessToken })
+      const uploadId2 = await prepareUpload({ originalName, lastModified, channelId: userChannelId, token: userAccessToken })
+
+      expect(uploadId1).to.not.equal(uploadId2)
+    })
+
+    it('Should have the same upload id with the same user', async function () {
+      const originalName = 'toto.mp4'
+      const lastModified = new Date().getTime()
+
+      const uploadId1 = await prepareUpload({ originalName, lastModified })
+      const uploadId2 = await prepareUpload({ originalName, lastModified })
+
+      expect(uploadId1).to.equal(uploadId2)
+    })
+
+    it('Should not cache a request with 2 different users', async function () {
+      const originalName = 'toto.mp4'
+      const lastModified = new Date().getTime()
+
+      const uploadId = await prepareUpload({ originalName, lastModified, token: server.accessToken })
+
+      await sendChunks({ pathUploadId: uploadId, token: server.accessToken })
+      await sendChunks({ pathUploadId: uploadId, token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+    })
+
+    it('Should not cache a request after a delete', async function () {
+      const originalName = 'toto.mp4'
+      const lastModified = new Date().getTime()
+      const uploadId1 = await prepareUpload({ originalName, lastModified, token: server.accessToken })
+
+      await sendChunks({ pathUploadId: uploadId1 })
+      await server.videos.endResumableUpload({ pathUploadId: uploadId1 })
+
+      const uploadId2 = await prepareUpload({ originalName, lastModified, token: server.accessToken })
+      expect(uploadId1).to.equal(uploadId2)
+
+      const result2 = await sendChunks({ pathUploadId: uploadId1 })
+      expect(result2.headers['x-resumable-upload-cached']).to.not.exist
+    })
   })
 
   after(async function () {