Fix tests and user quota
authorChocobozzz <florian.bigard@gmail.com>
Wed, 6 Sep 2017 14:35:40 +0000 (16:35 +0200)
committerChocobozzz <florian.bigard@gmail.com>
Wed, 6 Sep 2017 15:28:20 +0000 (17:28 +0200)
config/test-1.yaml
server/controllers/api/users.ts
server/initializers/checker.ts
server/middlewares/validators/users.ts
server/middlewares/validators/videos.ts
server/models/user/user.ts
server/tests/api/check-params/users.ts
server/tests/api/users.ts
server/tests/utils/users.ts

index f1514e9cc227f9bc01c8ab829dfa5106a31b8e75..4e9f294353466b37d5356e14cfcb091736810b83 100644 (file)
@@ -22,7 +22,7 @@ admin:
   email: 'admin1@example.com'
 
 user:
-  video_quota: 1024 * 1024 * 5
+  video_quota: 5242880 # 5MB
 
 signup:
   limit: 4
index 6922661ae5ff578d5bf1dff686c996e50149e75c..1ecaaf93f7a04c60dd0180b46be92933dab0f891 100644 (file)
@@ -8,6 +8,7 @@ import {
   ensureIsAdmin,
   ensureUserRegistrationAllowed,
   usersAddValidator,
+  usersRegisterValidator,
   usersUpdateValidator,
   usersUpdateMeValidator,
   usersRemoveValidator,
@@ -25,6 +26,7 @@ import {
   UserUpdate,
   UserUpdateMe
 } from '../../../shared'
+import { UserInstance } from '../../models'
 
 const usersRouter = express.Router()
 
@@ -61,8 +63,8 @@ usersRouter.post('/',
 
 usersRouter.post('/register',
   ensureUserRegistrationAllowed,
-  usersAddValidator,
-  createUser
+  usersRegisterValidator,
+  registerUser
 )
 
 usersRouter.put('/me',
@@ -99,11 +101,6 @@ export {
 function createUser (req: express.Request, res: express.Response, next: express.NextFunction) {
   const body: UserCreate = req.body
 
-  // On registration, we set the user video quota
-  if (body.videoQuota === undefined) {
-    body.videoQuota = CONFIG.USER.VIDEO_QUOTA
-  }
-
   const user = db.User.build({
     username: body.username,
     password: body.password,
@@ -118,6 +115,23 @@ function createUser (req: express.Request, res: express.Response, next: express.
     .catch(err => next(err))
 }
 
+function registerUser (req: express.Request, res: express.Response, next: express.NextFunction) {
+  const body: UserCreate = req.body
+
+  const user = db.User.build({
+    username: body.username,
+    password: body.password,
+    email: body.email,
+    displayNSFW: false,
+    role: USER_ROLES.USER,
+    videoQuota: CONFIG.USER.VIDEO_QUOTA
+  })
+
+  user.save()
+    .then(() => res.type('json').status(204).end())
+    .catch(err => next(err))
+}
+
 function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
   db.User.loadByUsername(res.locals.oauth.token.user.username)
     .then(user => res.json(user.toFormattedJSON()))
@@ -180,7 +194,7 @@ function updateMe (req: express.Request, res: express.Response, next: express.Ne
 
 function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
   const body: UserUpdate = req.body
-  const user = res.locals.user
+  const user: UserInstance = res.locals.user
 
   if (body.email !== undefined) user.email = body.email
   if (body.videoQuota !== undefined) user.videoQuota = body.videoQuota
index 97606ef311068b7156c0b486d00d94f327eb98bc..eb9e9e280e8bd2bd725322c806dd8fab98b2f763 100644 (file)
@@ -22,7 +22,7 @@ function checkMissedConfig () {
     'webserver.https', 'webserver.hostname', 'webserver.port',
     'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password',
     'storage.certs', 'storage.videos', 'storage.logs', 'storage.thumbnails', 'storage.previews', 'storage.torrents', 'storage.cache',
-    'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads'
+    'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads', 'user.video_quota'
   ]
   const miss: string[] = []
 
index ebb3435355a50ef8632ca45d42c86112b7413abd..aec6324bf0be21acf1d83f97f56dd46282ebf527 100644 (file)
@@ -6,7 +6,7 @@ import * as validator from 'validator'
 import { database as db } from '../../initializers/database'
 import { checkErrors } from './utils'
 import { isSignupAllowed, logger } from '../../helpers'
-import { VideoInstance } from '../../models'
+import { UserInstance, VideoInstance } from '../../models'
 
 function usersAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
   req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
@@ -17,16 +17,19 @@ function usersAddValidator (req: express.Request, res: express.Response, next: e
   logger.debug('Checking usersAdd parameters', { parameters: req.body })
 
   checkErrors(req, res, () => {
-    db.User.loadByUsernameOrEmail(req.body.username, req.body.email)
-      .then(user => {
-        if (user) return res.status(409).send('User already exists.')
+    checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next)
+  })
+}
 
-        next()
-      })
-      .catch(err => {
-        logger.error('Error in usersAdd request validator.', err)
-        return res.sendStatus(500)
-      })
+function usersRegisterValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
+  req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
+  req.checkBody('password', 'Should have a valid password').isUserPasswordValid()
+  req.checkBody('email', 'Should have a valid email').isEmail()
+
+  logger.debug('Checking usersRegister parameters', { parameters: req.body })
+
+  checkErrors(req, res, () => {
+    checkUserDoesNotAlreadyExist(req.body.username, req.body.email, res, next)
   })
 }
 
@@ -36,18 +39,16 @@ function usersRemoveValidator (req: express.Request, res: express.Response, next
   logger.debug('Checking usersRemove parameters', { parameters: req.params })
 
   checkErrors(req, res, () => {
-    db.User.loadById(req.params.id)
-      .then(user => {
-        if (!user) return res.status(404).send('User not found')
+    checkUserExists(req.params.id, res, (err, user) => {
+      if (err) {
+        logger.error('Error in usersRemoveValidator.', err)
+        return res.sendStatus(500)
+      }
 
-        if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
+      if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
 
-        next()
-      })
-      .catch(err => {
-        logger.error('Error in usersRemove request validator.', err)
-        return res.sendStatus(500)
-      })
+      next()
+    })
   })
 }
 
@@ -69,7 +70,7 @@ function usersUpdateMeValidator (req: express.Request, res: express.Response, ne
   req.checkBody('email', 'Should have a valid email attribute').optional().isEmail()
   req.checkBody('displayNSFW', 'Should have a valid display Not Safe For Work attribute').optional().isUserDisplayNSFWValid()
 
-  logger.debug('Checking usersUpdate parameters', { parameters: req.body })
+  logger.debug('Checking usersUpdateMe parameters', { parameters: req.body })
 
   checkErrors(req, res, next)
 }
@@ -123,6 +124,7 @@ function ensureUserRegistrationAllowed (req: express.Request, res: express.Respo
 
 export {
   usersAddValidator,
+  usersRegisterValidator,
   usersRemoveValidator,
   usersUpdateValidator,
   usersUpdateMeValidator,
@@ -133,16 +135,29 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function checkUserExists (id: number, res: express.Response, callback: () => void) {
+function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) {
   db.User.loadById(id)
     .then(user => {
       if (!user) return res.status(404).send('User not found')
 
       res.locals.user = user
-      callback()
+      callback(null, user)
     })
     .catch(err => {
       logger.error('Error in user request validator.', err)
       return res.sendStatus(500)
     })
 }
+
+function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) {
+  db.User.loadByUsernameOrEmail(username, email)
+      .then(user => {
+        if (user) return res.status(409).send('User already exists.')
+
+        callback()
+      })
+      .catch(err => {
+        logger.error('Error in usersAdd request validator.', err)
+        return res.sendStatus(500)
+      })
+}
index ba8c2d834f3e21ddbe329265834a394967219310..249da668dff51ae481ae225b2926d88efff06c30 100644 (file)
@@ -36,6 +36,12 @@ function videosAddValidator (req: express.Request, res: express.Response, next:
         }
 
         return db.Video.getDurationFromFile(videoFile.path)
+          .catch(err => {
+            logger.error('Invalid input file in videosAddValidator.', err)
+            res.status(400).send('Invalid input file.')
+
+            return undefined
+          })
       })
       .then(duration => {
         // Previous test failed, abort
@@ -51,7 +57,10 @@ function videosAddValidator (req: express.Request, res: express.Response, next:
       .catch(err => {
         logger.error('Error in video add validator', err)
         res.sendStatus(500)
+
+        return undefined
       })
+
   })
 }
 
index 9bf13ad243e5098cef1fd806332344bd3d64567e..79a595528db53babdd2371a7d880ff803b2196ff 100644 (file)
@@ -242,25 +242,26 @@ loadByUsernameOrEmail = function (username: string, email: string) {
 // ---------------------------------------------------------------------------
 
 function getOriginalVideoFileTotalFromUser (user: UserInstance) {
+  // attributes = [] because we don't want other fields than the sum
   const query = {
-    attributes: [
-      Sequelize.fn('COUNT', Sequelize.col('User.Author.Video.VideoFile.size'), 'totalVideoBytes')
-    ],
     where: {
-      id: user.id
+      resolution: 0 // Original, TODO: improve readability
     },
     include: [
       {
-        model: User['sequelize'].models.Author,
-        required: true,
+        attributes: [],
+        model: User['sequelize'].models.Video,
         include: [
           {
-            model: User['sequelize'].models.Video,
-            required: true,
+            attributes: [],
+            model: User['sequelize'].models.Author,
             include: [
               {
-                model: User['sequelize'].models.VideoFile,
-                required: true
+                attributes: [],
+                model: User['sequelize'].models.User,
+                where: {
+                  id: user.id
+                }
               }
             ]
           }
@@ -269,8 +270,5 @@ function getOriginalVideoFileTotalFromUser (user: UserInstance) {
     ]
   }
 
-  // FIXME: cast to any because of bad typing...
-  return User.findAll(query).then((res: any) => {
-    return res.totalVideoBytes
-  })
+  return User['sequelize'].models.VideoFile.sum('size', query)
 }
index 643a82afdce0c167332049caac0efc93d8a62aa6..ef78c8262408ac11f07412bf01b68df3d1769cf5 100644 (file)
@@ -43,7 +43,8 @@ describe('Test users API validators', function () {
 
     const username = 'user1'
     const password = 'my super password'
-    await createUser(server.url, server.accessToken, username, password)
+    const videoQuota = 42000000
+    await createUser(server.url, server.accessToken, username, password, videoQuota)
 
     const videoAttributes = {}
     await uploadVideo(server.url, server.accessToken, videoAttributes)
@@ -90,7 +91,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'ji',
         email: 'test@example.com',
-        password: 'my_super_password'
+        password: 'my_super_password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -100,7 +102,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'my_super_username_which_is_very_long',
         email: 'test@example.com',
-        password: 'my_super_password'
+        password: 'my_super_password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -110,7 +113,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'my username',
         email: 'test@example.com',
-        password: 'my_super_password'
+        password: 'my_super_password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -119,7 +123,8 @@ describe('Test users API validators', function () {
     it('Should fail with a missing email', async function () {
       const fields = {
         username: 'ji',
-        password: 'my_super_password'
+        password: 'my_super_password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -129,7 +134,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'my_super_username_which_is_very_long',
         email: 'test_example.com',
-        password: 'my_super_password'
+        password: 'my_super_password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -139,7 +145,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'my_username',
         email: 'test@example.com',
-        password: 'bla'
+        password: 'bla',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -151,7 +158,8 @@ describe('Test users API validators', function () {
         email: 'test@example.com',
         password: 'my super long password which is very very very very very very very very very very very very very very' +
                   'very very very very very very very very very very very very very very very veryv very very very very' +
-                  'very very very very very very very very very very very very very very very very very very very very long'
+                  'very very very very very very very very very very very very very very very very very very very very long',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
@@ -161,7 +169,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'my_username',
         email: 'test@example.com',
-        password: 'my super password'
+        password: 'my super password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: 'super token', fields, statusCodeExpected: 401 })
@@ -171,7 +180,8 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'user1',
         email: 'test@example.com',
-        password: 'my super password'
+        password: 'my super password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 409 })
@@ -181,17 +191,40 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'my_username',
         email: 'user1@example.com',
-        password: 'my super password'
+        password: 'my super password',
+        videoQuota: 42000000
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 409 })
     })
 
+    it('Should fail without a videoQuota', async function () {
+      const fields = {
+        username: 'my_username',
+        email: 'user1@example.com',
+        password: 'my super password'
+      }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with an invalid videoQuota', async function () {
+      const fields = {
+        username: 'my_username',
+        email: 'user1@example.com',
+        password: 'my super password',
+        videoQuota: -5
+      }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
     it('Should succeed with the correct params', async function () {
       const fields = {
         username: 'user2',
         email: 'test@example.com',
-        password: 'my super password'
+        password: 'my super password',
+        videoQuota: -1
       }
 
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 204 })
@@ -208,18 +241,20 @@ describe('Test users API validators', function () {
       const fields = {
         username: 'user3',
         email: 'test@example.com',
-        password: 'my super password'
+        password: 'my super password',
+        videoQuota: 42000000
       }
       await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: 403 })
     })
   })
 
-  describe('When updating a user', function () {
-    before(async function () {
-      const res = await getUsersList(server.url)
+  describe('When updating my account', function () {
+    it('Should fail with an invalid email attribute', async function () {
+      const fields = {
+        email: 'blabla'
+      }
 
-      userId = res.body.data[1].id
-      rootId = res.body.data[2].id
+      await makePutBodyRequest({ url: server.url, path: path + 'me', token: server.accessToken, fields })
     })
 
     it('Should fail with a too small password', async function () {
@@ -227,7 +262,7 @@ describe('Test users API validators', function () {
         password: 'bla'
       }
 
-      await makePutBodyRequest({ url: server.url, path: path + userId, token: userAccessToken, fields })
+      await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
     })
 
     it('Should fail with a too long password', async function () {
@@ -237,7 +272,7 @@ describe('Test users API validators', function () {
                   'very very very very very very very very very very very very very very very very very very very very long'
       }
 
-      await makePutBodyRequest({ url: server.url, path: path + userId, token: userAccessToken, fields })
+      await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
     })
 
     it('Should fail with an invalid display NSFW attribute', async function () {
@@ -245,7 +280,7 @@ describe('Test users API validators', function () {
         displayNSFW: -1
       }
 
-      await makePutBodyRequest({ url: server.url, path: path + userId, token: userAccessToken, fields })
+      await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
     })
 
     it('Should fail with an non authenticated user', async function () {
@@ -253,16 +288,60 @@ describe('Test users API validators', function () {
         password: 'my super password'
       }
 
-      await makePutBodyRequest({ url: server.url, path: path + userId, token: 'super token', fields, statusCodeExpected: 401 })
+      await makePutBodyRequest({ url: server.url, path: path + 'me', token: 'super token', fields, statusCodeExpected: 401 })
     })
 
     it('Should succeed with the correct params', async function () {
       const fields = {
         password: 'my super password',
-        displayNSFW: true
+        displayNSFW: true,
+        email: 'super_email@example.com'
       }
 
-      await makePutBodyRequest({ url: server.url, path: path + userId, token: userAccessToken, fields, statusCodeExpected: 204 })
+      await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
+    })
+  })
+
+  describe('When updating a user', function () {
+
+    before(async function () {
+      const res = await getUsersList(server.url)
+
+      userId = res.body.data[1].id
+      rootId = res.body.data[2].id
+    })
+
+    it('Should fail with an invalid email attribute', async function () {
+      const fields = {
+        email: 'blabla'
+      }
+
+      await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields })
+    })
+
+    it('Should fail with an invalid videoQuota attribute', async function () {
+      const fields = {
+        videoQuota: -90
+      }
+
+      await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields })
+    })
+
+    it('Should fail with an non authenticated user', async function () {
+      const fields = {
+        videoQuota: 42
+      }
+
+      await makePutBodyRequest({ url: server.url, path: path + userId, token: 'super token', fields, statusCodeExpected: 401 })
+    })
+
+    it('Should succeed with the correct params', async function () {
+      const fields = {
+        email: 'email@example.com',
+        videoQuota: 42
+      }
+
+      await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields, statusCodeExpected: 204 })
     })
   })
 
@@ -491,6 +570,38 @@ describe('Test users API validators', function () {
     })
   })
 
+  describe('When having a video quota', function () {
+    it('Should fail with a user having too many video', async function () {
+      const fields = {
+        videoQuota: 42
+      }
+
+      await makePutBodyRequest({ url: server.url, path: path + rootId, token: server.accessToken, fields, statusCodeExpected: 204 })
+
+      const videoAttributes = {}
+      await uploadVideo(server.url, server.accessToken, videoAttributes, 403)
+    })
+
+    it('Should fail with a registered user having too many video', async function () {
+      this.timeout(10000)
+
+      server.user = {
+        username: 'user3',
+        email: 'test3@example.com',
+        password: 'my super password'
+      }
+      userAccessToken = await loginAndGetAccessToken(server)
+
+      const videoAttributes = { fixture: 'video_short2.webm' }
+      await uploadVideo(server.url, userAccessToken, videoAttributes)
+      await uploadVideo(server.url, userAccessToken, videoAttributes)
+      await uploadVideo(server.url, userAccessToken, videoAttributes)
+      await uploadVideo(server.url, userAccessToken, videoAttributes)
+      await uploadVideo(server.url, userAccessToken, videoAttributes)
+      await uploadVideo(server.url, userAccessToken, videoAttributes, 403)
+    })
+  })
+
   after(async function () {
     killallServers([ server, serverWithRegistrationDisabled ])
 
index 104d783bb369b298b13f3d5a492893369bfac9f7..04c68d4ea91dce6a2140bba1668cf6334d1695e2 100644 (file)
@@ -319,9 +319,9 @@ describe('Test users', function () {
   })
 
   it('Should be able to update another user', async function () {
-    await updateUser(server.url, userId, server.accessToken, 'updated2@example.com', 42 )
+    await updateUser(server.url, userId, accessToken, 'updated2@example.com', 42)
 
-    const res = await getUserInformation(server.url, server.accessToken, userId)
+    const res = await getUserInformation(server.url, accessToken, userId)
     const user = res.body
 
     expect(user.username).to.equal('user_1')
index 1c3f6826ee398a7204b87cf90e689fbcdb9ea329..e5f3eb1b3a2724e65894b74c11f9fc5bfa803ca6 100644 (file)
@@ -118,7 +118,7 @@ function updateUser (url: string, userId: number, accessToken: string, email: st
   const path = '/api/v1/users/' + userId
 
   const toSend = {}
-  if (email !== undefined && email !== null) toSend['password'] = email
+  if (email !== undefined && email !== null) toSend['email'] = email
   if (videoQuota !== undefined && videoQuota !== null) toSend['videoQuota'] = videoQuota
 
   return request(url)